From 131e64e56c14760ffc408993ca5416d44f863528 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Fri, 12 Nov 2021 21:29:51 +1100 Subject: [PATCH 0001/1276] Add bonus based on opacity of hit objects --- .../Difficulty/OsuDifficultyCalculator.cs | 17 ++++---- .../Difficulty/OsuPerformanceCalculator.cs | 14 ------- .../Difficulty/Skills/Flashlight.cs | 39 +++++++++++++++++-- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 558ddc16ef..19e92d4365 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { private const double difficulty_multiplier = 0.0675; private double hitWindowGreat; + private double preempt; public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) @@ -34,11 +35,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty return new OsuDifficultyAttributes { Mods = mods, Skills = skills }; double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; - double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; - double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier; - double flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier; - - double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1; + double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; + double flashlightRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier; if (mods.Any(h => h is OsuModRelax)) speedRating = 0.0; @@ -77,7 +75,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty AimStrain = aimRating, SpeedStrain = speedRating, FlashlightRating = flashlightRating, - SliderFactor = sliderFactor, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, DrainRate = drainRate, @@ -110,12 +107,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty hitWindowGreat = hitWindows.WindowFor(HitResult.Great) / clockRate; + preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; + return new Skill[] { - new Aim(mods, true), - new Aim(mods, false), + new Aim(mods), new Speed(mods, hitWindowGreat), - new Flashlight(mods) + new Flashlight(mods, preempt) }; } @@ -126,6 +124,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty new OsuModEasy(), new OsuModHardRock(), new OsuModFlashlight(), + new OsuModHidden(), }; } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 8d45c7a8cc..817b0b63a8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -125,16 +125,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); } - // We assume 15% of sliders in a map are difficult since there's no way to tell from the performance calculator. - double estimateDifficultSliders = Attributes.SliderCount * 0.15; - - if (Attributes.SliderCount > 0) - { - double estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, Attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders); - double sliderNerfFactor = (1 - Attributes.SliderFactor) * Math.Pow(1 - estimateSliderEndsDropped / estimateDifficultSliders, 3) + Attributes.SliderFactor; - aimValue *= sliderNerfFactor; - } - aimValue *= accuracy; // It is important to also consider accuracy difficulty when doing that. aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; @@ -234,10 +224,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty double flashlightValue = Math.Pow(rawFlashlight, 2.0) * 25.0; - // Add an additional bonus for HDFL. - if (mods.Any(h => h is OsuModHidden)) - flashlightValue *= 1.3; - // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (effectiveMissCount > 0) flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)effectiveMissCount / totalHits, 0.775), Math.Pow(effectiveMissCount, .875)); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 466f0556ab..55ef8db129 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -2,8 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; @@ -14,16 +16,24 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// public class Flashlight : OsuStrainSkill { - public Flashlight(Mod[] mods) + public Flashlight(Mod[] mods, double preemptTime) : base(mods) { + this.mods = mods; + this.preemptTime = preemptTime; } - private double skillMultiplier => 0.15; + private double skillMultiplier => 0.12; private double strainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. + private Mod[] mods; + private bool hidden; + private double preemptTime; + + private const double max_opacity_bonus = 0.4; + private double currentStrain; private double strainValueOf(DifficultyHitObject current) @@ -31,6 +41,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (current.BaseObject is Spinner) return 0; + hidden = mods.Any(m => m is OsuModHidden); + var osuCurrent = (OsuDifficultyHitObject)current; var osuHitObject = (OsuHitObject)(osuCurrent.BaseObject); @@ -58,11 +70,30 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // We also want to nerf stacks so that only the first object of the stack is accounted for. double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); - result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; + // Bonus based on how visible the object is. + double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - opacity(cumulativeStrainTime, preemptTime, hidden)); + + result += Math.Pow(0.8, i) * stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime; } } - return Math.Pow(smallDistNerf * result, 2.0); + result = Math.Pow(smallDistNerf * result, 2.0); + + if (hidden) { + result *= 1.0 + max_opacity_bonus; + } + + return result; + } + + private double opacity(double ms, double preemptTime, bool hidden) { + if (hidden) { + return Math.Clamp(Math.Min((1 - ms / preemptTime) * 2.5, (ms / preemptTime) * (1.0 / 0.3)), 0.0, 1.0); + } + else + { + return Math.Clamp((1.0 - ms / preemptTime) * 1.5, 0.0, 1.0); + } } private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); From 5a3be778a172405402bdcaadfdfdb7bb04ce190d Mon Sep 17 00:00:00 2001 From: MBmasher Date: Fri, 12 Nov 2021 21:41:01 +1100 Subject: [PATCH 0002/1276] Resolve conflicts with recent slider hotfix --- .../Difficulty/OsuDifficultyCalculator.cs | 15 ++++++++++----- .../Difficulty/OsuPerformanceCalculator.cs | 10 ++++++++++ .../Difficulty/Skills/Flashlight.cs | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 19e92d4365..4e916af813 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -35,8 +35,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty return new OsuDifficultyAttributes { Mods = mods, Skills = skills }; double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; - double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; - double flashlightRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier; + double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; + double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier; + double flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier; + + double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1; if (mods.Any(h => h is OsuModRelax)) speedRating = 0.0; @@ -75,6 +78,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty AimStrain = aimRating, SpeedStrain = speedRating, FlashlightRating = flashlightRating, + SliderFactor = sliderFactor, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, DrainRate = drainRate, @@ -111,7 +115,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty return new Skill[] { - new Aim(mods), + new Aim(mods, true), + new Aim(mods, false), new Speed(mods, hitWindowGreat), new Flashlight(mods, preempt) }; @@ -124,7 +129,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty new OsuModEasy(), new OsuModHardRock(), new OsuModFlashlight(), - new OsuModHidden(), + new OsuModHidden() }; } -} +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 817b0b63a8..f4c451f80b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -125,6 +125,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); } + // We assume 15% of sliders in a map are difficult since there's no way to tell from the performance calculator. + double estimateDifficultSliders = Attributes.SliderCount * 0.15; + + if (Attributes.SliderCount > 0) + { + double estimateSliderEndsDropped = Math.Clamp(Math.Min(countOk + countMeh + countMiss, Attributes.MaxCombo - scoreMaxCombo), 0, estimateDifficultSliders); + double sliderNerfFactor = (1 - Attributes.SliderFactor) * Math.Pow(1 - estimateSliderEndsDropped / estimateDifficultSliders, 3) + Attributes.SliderFactor; + aimValue *= sliderNerfFactor; + } + aimValue *= accuracy; // It is important to also consider accuracy difficulty when doing that. aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 55ef8db129..e0d2a084bd 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; From efac11e886f887673b36d5d42d840bc85ead9688 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Fri, 12 Nov 2021 21:42:27 +1100 Subject: [PATCH 0003/1276] Add extra bonus for hidden+flashlight --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index e0d2a084bd..caae53516d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills this.preemptTime = preemptTime; } - private double skillMultiplier => 0.12; + private double skillMultiplier => 0.11; private double strainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double preemptTime; private const double max_opacity_bonus = 0.4; + private const double hidden_bonus = 0.3; private double currentStrain; @@ -81,6 +82,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (hidden) { result *= 1.0 + max_opacity_bonus; + // Additional bonus for Hidden due to there being no approach circles. + result *= 1.0 + hidden_bonus; } return result; From f2d05ea899ba2aaa93deb9680042c1ce6a5cba8f Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 17 Nov 2021 11:27:48 +1100 Subject: [PATCH 0004/1276] Remove strain being multiplied by max opacity bonus --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index caae53516d..6d6f2b0f6c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -80,11 +80,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills result = Math.Pow(smallDistNerf * result, 2.0); - if (hidden) { - result *= 1.0 + max_opacity_bonus; - // Additional bonus for Hidden due to there being no approach circles. + // Additional bonus for Hidden due to there being no approach circles. + if (hidden) result *= 1.0 + hidden_bonus; - } return result; } From 63c5f7d9d7b16309f045fa22faf39e4809a0a631 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 17 Nov 2021 11:39:12 +1100 Subject: [PATCH 0005/1276] Balancing opacity and hidden bonus --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 6d6f2b0f6c..12580cb450 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double preemptTime; private const double max_opacity_bonus = 0.4; - private const double hidden_bonus = 0.3; + private const double hidden_bonus = 0.4; private double currentStrain; From 6a444b9edbc2c8b780e965b08dcef1ade102e990 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 18 Nov 2021 09:47:41 +1100 Subject: [PATCH 0006/1276] Further balancing opacity/hidden bonus --- .../Difficulty/Flashlight.cs | 112 ++++++++++++++++++ .../Difficulty/Skills/Flashlight.cs | 8 +- 2 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Difficulty/Flashlight.cs diff --git a/osu.Game.Rulesets.Osu/Difficulty/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Flashlight.cs new file mode 100644 index 0000000000..da7ab3d1fc --- /dev/null +++ b/osu.Game.Rulesets.Osu/Difficulty/Flashlight.cs @@ -0,0 +1,112 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Difficulty.Skills +{ + /// + /// Represents the skill required to memorise and hit every object in a map with the Flashlight mod enabled. + /// + public class Flashlight : OsuStrainSkill + { + public Flashlight(Mod[] mods, double preemptTime) + : base(mods) + { + this.mods = mods; + this.preemptTime = preemptTime; + } + + private double skillMultiplier => 0.07; + private double strainDecayBase => 0.15; + protected override double DecayWeight => 1.0; + protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. + + private Mod[] mods; + private bool hidden; + private double preemptTime; + + private const double max_opacity_bonus = 1.0; + private const double hidden_bonus = 0.8; + + private double currentStrain; + + private double strainValueOf(DifficultyHitObject current) + { + if (current.BaseObject is Spinner) + return 0; + + hidden = mods.Any(m => m is OsuModHidden); + + var osuCurrent = (OsuDifficultyHitObject)current; + var osuHitObject = (OsuHitObject)(osuCurrent.BaseObject); + + double scalingFactor = 52.0 / osuHitObject.Radius; + double smallDistNerf = 1.0; + double cumulativeStrainTime = 0.0; + + double result = 0.0; + + for (int i = 0; i < Previous.Count; i++) + { + var osuPrevious = (OsuDifficultyHitObject)Previous[i]; + var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); + + if (!(osuPrevious.BaseObject is Spinner)) + { + double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + + cumulativeStrainTime += osuPrevious.StrainTime; + + // We want to nerf objects that can be easily seen within the Flashlight circle radius. + if (i == 0) + smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); + + // We also want to nerf stacks so that only the first object of the stack is accounted for. + double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); + + // Bonus based on how visible the object is. + double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - opacity(cumulativeStrainTime, preemptTime, hidden)); + + result += Math.Pow(0.8, i) * stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime; + } + } + + result = Math.Pow(smallDistNerf * result, 2.0); + + // Additional bonus for Hidden due to there being no approach circles. + if (hidden) + result *= 1.0 + hidden_bonus; + + return result; + } + + private double opacity(double ms, double preemptTime, bool hidden) { + if (hidden) { + return Math.Clamp(Math.Min((1 - ms / preemptTime) * 2.5, (ms / preemptTime) * (1.0 / 0.3)), 0.0, 1.0); + } + else + { + return Math.Clamp((1.0 - ms / preemptTime) * 1.5, 0.0, 1.0); + } + } + + private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); + + protected override double CalculateInitialStrain(double time) => currentStrain * strainDecay(time - Previous[0].StartTime); + + protected override double StrainValueAt(DifficultyHitObject current) + { + currentStrain *= strainDecay(current.DeltaTime); + currentStrain += strainValueOf(current) * skillMultiplier; + + return currentStrain; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 12580cb450..209374714d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills this.preemptTime = preemptTime; } - private double skillMultiplier => 0.11; + private double skillMultiplier => 0.09; private double strainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -32,8 +32,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private bool hidden; private double preemptTime; - private const double max_opacity_bonus = 0.4; - private const double hidden_bonus = 0.4; + private const double max_opacity_bonus = 0.7; + private const double hidden_bonus = 0.5; private double currentStrain; From 8e8571543d526ee218cceb1c56b0ae784bc870bc Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 18 Nov 2021 09:48:18 +1100 Subject: [PATCH 0007/1276] Removing unnecessary file --- .../Difficulty/Flashlight.cs | 112 ------------------ 1 file changed, 112 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Difficulty/Flashlight.cs diff --git a/osu.Game.Rulesets.Osu/Difficulty/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Flashlight.cs deleted file mode 100644 index da7ab3d1fc..0000000000 --- a/osu.Game.Rulesets.Osu/Difficulty/Flashlight.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Linq; -using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; -using osu.Game.Rulesets.Osu.Objects; - -namespace osu.Game.Rulesets.Osu.Difficulty.Skills -{ - /// - /// Represents the skill required to memorise and hit every object in a map with the Flashlight mod enabled. - /// - public class Flashlight : OsuStrainSkill - { - public Flashlight(Mod[] mods, double preemptTime) - : base(mods) - { - this.mods = mods; - this.preemptTime = preemptTime; - } - - private double skillMultiplier => 0.07; - private double strainDecayBase => 0.15; - protected override double DecayWeight => 1.0; - protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. - - private Mod[] mods; - private bool hidden; - private double preemptTime; - - private const double max_opacity_bonus = 1.0; - private const double hidden_bonus = 0.8; - - private double currentStrain; - - private double strainValueOf(DifficultyHitObject current) - { - if (current.BaseObject is Spinner) - return 0; - - hidden = mods.Any(m => m is OsuModHidden); - - var osuCurrent = (OsuDifficultyHitObject)current; - var osuHitObject = (OsuHitObject)(osuCurrent.BaseObject); - - double scalingFactor = 52.0 / osuHitObject.Radius; - double smallDistNerf = 1.0; - double cumulativeStrainTime = 0.0; - - double result = 0.0; - - for (int i = 0; i < Previous.Count; i++) - { - var osuPrevious = (OsuDifficultyHitObject)Previous[i]; - var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); - - if (!(osuPrevious.BaseObject is Spinner)) - { - double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; - - cumulativeStrainTime += osuPrevious.StrainTime; - - // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0) - smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); - - // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); - - // Bonus based on how visible the object is. - double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - opacity(cumulativeStrainTime, preemptTime, hidden)); - - result += Math.Pow(0.8, i) * stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime; - } - } - - result = Math.Pow(smallDistNerf * result, 2.0); - - // Additional bonus for Hidden due to there being no approach circles. - if (hidden) - result *= 1.0 + hidden_bonus; - - return result; - } - - private double opacity(double ms, double preemptTime, bool hidden) { - if (hidden) { - return Math.Clamp(Math.Min((1 - ms / preemptTime) * 2.5, (ms / preemptTime) * (1.0 / 0.3)), 0.0, 1.0); - } - else - { - return Math.Clamp((1.0 - ms / preemptTime) * 1.5, 0.0, 1.0); - } - } - - private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); - - protected override double CalculateInitialStrain(double time) => currentStrain * strainDecay(time - Previous[0].StartTime); - - protected override double StrainValueAt(DifficultyHitObject current) - { - currentStrain *= strainDecay(current.DeltaTime); - currentStrain += strainValueOf(current) * skillMultiplier; - - return currentStrain; - } - } -} From 92cf447180144a3b2a00fff624aacd531eda8406 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 18 Nov 2021 10:32:41 +1100 Subject: [PATCH 0008/1276] Remove unnecessary braces --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 209374714d..d8e73ffad4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -88,13 +88,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } private double opacity(double ms, double preemptTime, bool hidden) { - if (hidden) { + if (hidden) return Math.Clamp(Math.Min((1 - ms / preemptTime) * 2.5, (ms / preemptTime) * (1.0 / 0.3)), 0.0, 1.0); - } else - { return Math.Clamp((1.0 - ms / preemptTime) * 1.5, 0.0, 1.0); - } } private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); From 30e18f16d9b6c42eec1b5cace8836cbc1151f7bf Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 18 Nov 2021 10:33:44 +1100 Subject: [PATCH 0009/1276] Change mods and preemptTime to readonly --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index d8e73ffad4..3ae9c0eca9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -28,9 +28,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. - private Mod[] mods; + private readonly Mod[] mods; private bool hidden; - private double preemptTime; + private readonly double preemptTime; private const double max_opacity_bonus = 0.7; private const double hidden_bonus = 0.5; From f4b23f09607ca514c62e2da8f1e8b07a707f2036 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 18 Nov 2021 10:37:07 +1100 Subject: [PATCH 0010/1276] Remove setting preempt in CreateDifficultyAttributes --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 4e916af813..fe20ce112e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -60,7 +60,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; - double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; int maxCombo = beatmap.HitObjects.Count; From fe83b8fc77c8e247bcf48fe597f35a4c381b49c3 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 18 Nov 2021 10:50:32 +1100 Subject: [PATCH 0011/1276] Add line break --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 3ae9c0eca9..b45a54f9e7 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -87,7 +87,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return result; } - private double opacity(double ms, double preemptTime, bool hidden) { + private double opacity(double ms, double preemptTime, bool hidden) + { if (hidden) return Math.Clamp(Math.Min((1 - ms / preemptTime) * 2.5, (ms / preemptTime) * (1.0 / 0.3)), 0.0, 1.0); else From afbec941249b1d309ed31be7fcd0e84e180805f6 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sun, 21 Nov 2021 23:40:15 +1100 Subject: [PATCH 0012/1276] Move opacity function to OsuDifficultyHitObject --- .../Preprocessing/OsuDifficultyHitObject.cs | 11 +++++++++++ osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 10 +--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index d073d751d0..dc8188929a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -56,12 +56,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing private readonly OsuHitObject lastLastObject; private readonly OsuHitObject lastObject; + private readonly double clockRate; public OsuDifficultyHitObject(HitObject hitObject, HitObject lastLastObject, HitObject lastObject, double clockRate) : base(hitObject, lastObject, clockRate) { this.lastLastObject = (OsuHitObject)lastLastObject; this.lastObject = (OsuHitObject)lastObject; + this.clockRate = clockRate; // Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects. StrainTime = Math.Max(DeltaTime, min_delta_time); @@ -69,6 +71,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing setDistances(clockRate); } + public double opacity(double ms, bool hidden) + { + double preemptTime = BaseObject.TimePreempt / clockRate; + if (hidden) + return Math.Clamp(Math.Min((1 - ms / preemptTime) * 2.5, (ms / preemptTime) * (1.0 / 0.3)), 0.0, 1.0); + else + return Math.Clamp((1.0 - ms / preemptTime) * 1.5, 0.0, 1.0); + } + private void setDistances(double clockRate) { // We don't need to calculate either angle or distance when one of the last->curr objects is a spinner diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index b45a54f9e7..701670974b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); // Bonus based on how visible the object is. - double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - opacity(cumulativeStrainTime, preemptTime, hidden)); + double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.opacity(cumulativeStrainTime, hidden)); result += Math.Pow(0.8, i) * stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime; } @@ -87,14 +87,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return result; } - private double opacity(double ms, double preemptTime, bool hidden) - { - if (hidden) - return Math.Clamp(Math.Min((1 - ms / preemptTime) * 2.5, (ms / preemptTime) * (1.0 / 0.3)), 0.0, 1.0); - else - return Math.Clamp((1.0 - ms / preemptTime) * 1.5, 0.0, 1.0); - } - private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); protected override double CalculateInitialStrain(double time) => currentStrain * strainDecay(time - Previous[0].StartTime); From a57c277a585573b3081f6834b1cab6f09be60cf8 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sun, 21 Nov 2021 23:43:09 +1100 Subject: [PATCH 0013/1276] Move preempt back to CreateDifficultyAttributes --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 6 ++---- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index fe20ce112e..14101f8302 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -22,7 +22,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty { private const double difficulty_multiplier = 0.0675; private double hitWindowGreat; - private double preempt; public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) @@ -60,6 +59,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; + double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; int maxCombo = beatmap.HitObjects.Count; @@ -110,14 +110,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty hitWindowGreat = hitWindows.WindowFor(HitResult.Great) / clockRate; - preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; - return new Skill[] { new Aim(mods, true), new Aim(mods, false), new Speed(mods, hitWindowGreat), - new Flashlight(mods, preempt) + new Flashlight(mods) }; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 701670974b..8969e95aba 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -16,11 +16,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// public class Flashlight : OsuStrainSkill { - public Flashlight(Mod[] mods, double preemptTime) + public Flashlight(Mod[] mods) : base(mods) { this.mods = mods; - this.preemptTime = preemptTime; } private double skillMultiplier => 0.09; @@ -30,7 +29,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private readonly Mod[] mods; private bool hidden; - private readonly double preemptTime; private const double max_opacity_bonus = 0.7; private const double hidden_bonus = 0.5; From e9a4ee68004b998264f6577fe79050b276b7c1a0 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sun, 21 Nov 2021 23:53:40 +1100 Subject: [PATCH 0014/1276] Cleaning up code --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 3 ++- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index dc8188929a..cc699aa3f9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -71,9 +71,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing setDistances(clockRate); } - public double opacity(double ms, bool hidden) + public double Opacity(double ms, bool hidden) { double preemptTime = BaseObject.TimePreempt / clockRate; + if (hidden) return Math.Clamp(Math.Min((1 - ms / preemptTime) * 2.5, (ms / preemptTime) * (1.0 / 0.3)), 0.0, 1.0); else diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 8969e95aba..2523f66bf6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); // Bonus based on how visible the object is. - double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.opacity(cumulativeStrainTime, hidden)); + double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.Opacity(cumulativeStrainTime, hidden)); result += Math.Pow(0.8, i) * stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime; } From e9745a3ac41c93ec5860505cf833ec5618c5a177 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 22 Nov 2021 08:32:35 +1100 Subject: [PATCH 0015/1276] Fix wrong opacity formula --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index cc699aa3f9..4109c068ea 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -74,11 +74,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing public double Opacity(double ms, bool hidden) { double preemptTime = BaseObject.TimePreempt / clockRate; + double fadeInTime = BaseObject.TimeFadeIn / clockRate; if (hidden) - return Math.Clamp(Math.Min((1 - ms / preemptTime) * 2.5, (ms / preemptTime) * (1.0 / 0.3)), 0.0, 1.0); + return Math.Clamp(Math.Min((1.0 - ms / preemptTime) * 2.5, (ms / preemptTime - 0.3) * (1.0 / 0.3)), 0.0, 1.0); else - return Math.Clamp((1.0 - ms / preemptTime) * 1.5, 0.0, 1.0); + return Math.Clamp((preemptTime - ms) / fadeInTime, 0.0, 1.0); } private void setDistances(double clockRate) From 7833fab02d0f1e3add202aa2f24118b67f4f404d Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 22 Nov 2021 08:41:56 +1100 Subject: [PATCH 0016/1276] Balancing bonuses to adjust for corrected opacity formula --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 2523f66bf6..7518364dd1 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills this.mods = mods; } - private double skillMultiplier => 0.09; + private double skillMultiplier => 0.11; private double strainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -30,8 +30,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private readonly Mod[] mods; private bool hidden; - private const double max_opacity_bonus = 0.7; - private const double hidden_bonus = 0.5; + private const double max_opacity_bonus = 0.5; + private const double hidden_bonus = 0.1; private double currentStrain; From 65ef03034187ed9e65e288b1eee96d030eef145f Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 22 Nov 2021 08:59:41 +1100 Subject: [PATCH 0017/1276] Further balancing --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 7518364dd1..7b4119d354 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills this.mods = mods; } - private double skillMultiplier => 0.11; + private double skillMultiplier => 0.1; private double strainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -30,8 +30,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private readonly Mod[] mods; private bool hidden; - private const double max_opacity_bonus = 0.5; - private const double hidden_bonus = 0.1; + private const double max_opacity_bonus = 0.4; + private const double hidden_bonus = 0.2; private double currentStrain; From 383bf7cdfc7f71d3528ad054fcfd7c16aef751ff Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 30 Nov 2021 10:39:48 +0900 Subject: [PATCH 0018/1276] Only allow HD combination alongside FL --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 1b6eb2915b..79314344ea 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty new OsuModEasy(), new OsuModHardRock(), new OsuModFlashlight(), - new OsuModHidden() + new MultiMod(new OsuModFlashlight(), new OsuModHidden()) }; } -} \ No newline at end of file +} From b0dc8bf0616cbf5ac9cb07edc8f26a7b33845223 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 30 Nov 2021 12:58:49 +1100 Subject: [PATCH 0019/1276] Change Opacity function to take in absolute map time rather than relative time --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 5 ++++- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 4109c068ea..c94fd10db5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -71,8 +71,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing setDistances(clockRate); } - public double Opacity(double ms, bool hidden) + public double Opacity(double T, bool hidden) { + double ms = (BaseObject.StartTime - T) / clockRate; + if (ms < 0) + return 0.0; double preemptTime = BaseObject.TimePreempt / clockRate; double fadeInTime = BaseObject.TimeFadeIn / clockRate; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 7b4119d354..d7f515602e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); // Bonus based on how visible the object is. - double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.Opacity(cumulativeStrainTime, hidden)); + double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.Opacity(osuPreviousHitObject.StartTime, hidden)); result += Math.Pow(0.8, i) * stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime; } From 3339afd6485270b4b3ac9db1d1dbffe599dc31da Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 30 Nov 2021 13:52:58 +1100 Subject: [PATCH 0020/1276] Change input variable name in Opacity function --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index c94fd10db5..7ab12f109f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -71,9 +71,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing setDistances(clockRate); } - public double Opacity(double T, bool hidden) + public double Opacity(double mapTime, bool hidden) { - double ms = (BaseObject.StartTime - T) / clockRate; + double ms = (BaseObject.StartTime - mapTime) / clockRate; if (ms < 0) return 0.0; double preemptTime = BaseObject.TimePreempt / clockRate; From 5884b058b9f15ab2d518316d5e62c690431367b0 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 30 Nov 2021 13:54:41 +1100 Subject: [PATCH 0021/1276] Add blank line --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 7ab12f109f..a497c95990 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -76,6 +76,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing double ms = (BaseObject.StartTime - mapTime) / clockRate; if (ms < 0) return 0.0; + double preemptTime = BaseObject.TimePreempt / clockRate; double fadeInTime = BaseObject.TimeFadeIn / clockRate; From 9824d805eac344ab727d87d2b269ac0fca34d6b2 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 30 Nov 2021 14:36:38 +1100 Subject: [PATCH 0022/1276] Remove unnecessary clockRate in Opacity function --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index a497c95990..dacbb4c51f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -56,14 +56,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing private readonly OsuHitObject lastLastObject; private readonly OsuHitObject lastObject; - private readonly double clockRate; public OsuDifficultyHitObject(HitObject hitObject, HitObject lastLastObject, HitObject lastObject, double clockRate) : base(hitObject, lastObject, clockRate) { this.lastLastObject = (OsuHitObject)lastLastObject; this.lastObject = (OsuHitObject)lastObject; - this.clockRate = clockRate; // Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects. StrainTime = Math.Max(DeltaTime, min_delta_time); @@ -73,12 +71,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing public double Opacity(double mapTime, bool hidden) { - double ms = (BaseObject.StartTime - mapTime) / clockRate; + double ms = BaseObject.StartTime - mapTime; if (ms < 0) return 0.0; - double preemptTime = BaseObject.TimePreempt / clockRate; - double fadeInTime = BaseObject.TimeFadeIn / clockRate; + double preemptTime = BaseObject.TimePreempt; + double fadeInTime = BaseObject.TimeFadeIn; if (hidden) return Math.Clamp(Math.Min((1.0 - ms / preemptTime) * 2.5, (ms / preemptTime - 0.3) * (1.0 / 0.3)), 0.0, 1.0); From a7aea49cb38be1108d91bcc884a25b9526019ee4 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 21 Dec 2021 20:06:07 +1100 Subject: [PATCH 0023/1276] Rename `osuPreviousHitObject` to `currentHitObject` --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 76ce6dc057..1a707b26b5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0); // Bonus based on how visible the object is. - double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.Opacity(osuPreviousHitObject.StartTime, hidden)); + double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.Opacity(currentHitObject.StartTime, hidden)); result += stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } From 3d3de00581648712338aa5237926a143f8275710 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 21 Dec 2021 20:06:53 +1100 Subject: [PATCH 0024/1276] Move `hidden` initialisation to Flashlight constructor --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 1a707b26b5..3b23ec505b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills : base(mods) { this.mods = mods; + this.hidden = mods.Any(m => m is OsuModHidden); } private double skillMultiplier => 0.1; @@ -40,8 +41,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (current.BaseObject is Spinner) return 0; - hidden = mods.Any(m => m is OsuModHidden); - var osuCurrent = (OsuDifficultyHitObject)current; var osuHitObject = (OsuHitObject)(osuCurrent.BaseObject); From 5d8968498cacbadfa704f4aa730652d6a6709228 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 21 Dec 2021 20:08:57 +1100 Subject: [PATCH 0025/1276] Adjust `skillMultiplier` after merging #15728, #15867 --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 3b23ec505b..266f4e8960 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills this.hidden = mods.Any(m => m is OsuModHidden); } - private double skillMultiplier => 0.1; + private double skillMultiplier => 0.05; private double strainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. From c5de203aa5258b66a2b7067c6306c12b75e158f3 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 21 Dec 2021 20:10:19 +1100 Subject: [PATCH 0026/1276] Multiply `opacityBonus` to base strain --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 266f4e8960..8b2e66e0a7 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // Bonus based on how visible the object is. double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.Opacity(currentHitObject.StartTime, hidden)); - result += stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; + result += stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime; } lastObj = currentObj; From 2aafcd3628463ccd2c5c0c8c88b32495ebc8694c Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 21 Dec 2021 20:58:05 +1100 Subject: [PATCH 0027/1276] Refactor code regarding `hidden` boolean --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 8b2e66e0a7..5d2052046b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -19,8 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills public Flashlight(Mod[] mods) : base(mods) { - this.mods = mods; - this.hidden = mods.Any(m => m is OsuModHidden); + hidden = mods.Any(m => m is OsuModHidden); } private double skillMultiplier => 0.05; @@ -28,8 +27,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. - private readonly Mod[] mods; - private bool hidden; + private readonly bool hidden; private const double max_opacity_bonus = 0.4; private const double hidden_bonus = 0.2; From f53ce5aedfd17d18eaa9a882e14707636a806a92 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 23 Jan 2022 11:11:12 +0800 Subject: [PATCH 0028/1276] Fix max combo calculation in osu diffcalc --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index c5b1baaad1..c80b19e1d3 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -63,8 +63,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty double drainRate = beatmap.Difficulty.DrainRate; int maxCombo = beatmap.HitObjects.Count; - // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) - maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); + // Add the ticks + tail of the slider + // 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) + // an additional 1 is subtracted if only nested objects are judged because the hit result of the entire slider would not contribute to combo + maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1 - (s.OnlyJudgeNestedObjects ? 1 : 0)); int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); int sliderCount = beatmap.HitObjects.Count(h => h is Slider); From 44311c1f4e3e18548f9b574de968468d52f8c282 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 23 Jan 2022 11:25:22 +0800 Subject: [PATCH 0029/1276] Add tests for diffcalc max combo --- .../CatchDifficultyCalculatorTest.cs | 12 +++++------ .../ManiaDifficultyCalculatorTest.cs | 12 +++++------ .../OsuDifficultyCalculatorTest.cs | 21 ++++++++++++------- .../TaikoDifficultyCalculatorTest.cs | 16 +++++++------- .../Beatmaps/DifficultyCalculatorTest.cs | 7 +++++-- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs index 7e8d567fbe..48d46636df 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; - [TestCase(4.0505463516206195d, "diffcalc-test")] - public void Test(double expected, string name) - => base.Test(expected, name); + [TestCase(4.0505463516206195d, 127, "diffcalc-test")] + public void Test(double expectedStarRating, int expectedMaxCombo, string name) + => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(5.1696411260785498d, "diffcalc-test")] - public void TestClockRateAdjusted(double expected, string name) - => Test(expected, name, new CatchModDoubleTime()); + [TestCase(5.1696411260785498d, 127, "diffcalc-test")] + public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) + => Test(expectedStarRating, expectedMaxCombo, name, new CatchModDoubleTime()); protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset().RulesetInfo, beatmap); diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs index 6ec49d7634..715614a201 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Mania.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; - [TestCase(2.3449735700206298d, "diffcalc-test")] - public void Test(double expected, string name) - => base.Test(expected, name); + [TestCase(2.3449735700206298d, 151, "diffcalc-test")] + public void Test(double expectedStarRating, int expectedMaxCombo, string name) + => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(2.7879104989252959d, "diffcalc-test")] - public void TestClockRateAdjusted(double expected, string name) - => Test(expected, name, new ManiaModDoubleTime()); + [TestCase(2.7879104989252959d, 151, "diffcalc-test")] + public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) + => Test(expectedStarRating, expectedMaxCombo, name, new ManiaModDoubleTime()); protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset().RulesetInfo, beatmap); diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index b7984e6995..df577ea8d3 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,15 +15,20 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.6972307565739273d, "diffcalc-test")] - [TestCase(1.4484754139145539d, "zero-length-sliders")] - public void Test(double expected, string name) - => base.Test(expected, name); + [TestCase(6.6972307565739273d, 206, "diffcalc-test")] + [TestCase(1.4484754139145539d, 45, "zero-length-sliders")] + public void Test(double expectedStarRating, int expectedMaxCombo, string name) + => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(8.9382559208689809d, "diffcalc-test")] - [TestCase(1.7548875851757628d, "zero-length-sliders")] - public void TestClockRateAdjusted(double expected, string name) - => Test(expected, name, new OsuModDoubleTime()); + [TestCase(8.9382559208689809d, 206, "diffcalc-test")] + [TestCase(1.7548875851757628d, 45, "zero-length-sliders")] + public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) + => Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime()); + + [TestCase(6.6972307218715166d, 239, "diffcalc-test")] + [TestCase(1.4484754139145537d, 54, "zero-length-sliders")] + public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name) + => Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic()); protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap); diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index 2b1cbc580e..226da7df09 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -14,15 +14,15 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - [TestCase(2.2420075288523802d, "diffcalc-test")] - [TestCase(2.2420075288523802d, "diffcalc-test-strong")] - public void Test(double expected, string name) - => base.Test(expected, name); + [TestCase(2.2420075288523802d, 200, "diffcalc-test")] + [TestCase(2.2420075288523802d, 200, "diffcalc-test-strong")] + public void Test(double expectedStarRating, int expectedMaxCombo, string name) + => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(3.134084469440479d, "diffcalc-test")] - [TestCase(3.134084469440479d, "diffcalc-test-strong")] - public void TestClockRateAdjusted(double expected, string name) - => Test(expected, name, new TaikoModDoubleTime()); + [TestCase(3.134084469440479d, 200, "diffcalc-test")] + [TestCase(3.134084469440479d, 200, "diffcalc-test-strong")] + public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) + => Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime()); protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset().RulesetInfo, beatmap); diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index 9f8811c7f9..ed00c7959b 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -22,10 +22,13 @@ namespace osu.Game.Tests.Beatmaps protected abstract string ResourceAssembly { get; } - protected void Test(double expected, string name, params Mod[] mods) + protected void Test(double expectedStarRating, int expectedMaxCombo, string name, params Mod[] mods) { + var attributes = CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods); + // Platform-dependent math functions (Pow, Cbrt, Exp, etc) may result in minute differences. - Assert.That(CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods).StarRating, Is.EqualTo(expected).Within(0.00001)); + Assert.That(attributes.StarRating, Is.EqualTo(expectedStarRating).Within(0.00001)); + Assert.That(attributes.MaxCombo, Is.EqualTo(expectedMaxCombo)); } private IWorkingBeatmap getBeatmap(string name) From 74a55ead7711108c4d6b856e11433b476459c35a Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 23 Jan 2022 13:00:54 +0800 Subject: [PATCH 0030/1276] Simplify combo counting logic --- .../Difficulty/OsuDifficultyCalculator.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index c80b19e1d3..d04d0872d8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Skills; using osu.Game.Rulesets.Osu.Mods; @@ -62,11 +63,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; - int maxCombo = beatmap.HitObjects.Count; - // Add the ticks + tail of the slider - // 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) - // an additional 1 is subtracted if only nested objects are judged because the hit result of the entire slider would not contribute to combo - maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1 - (s.OnlyJudgeNestedObjects ? 1 : 0)); + int maxCombo = 0; + + void countCombo(HitObject ho) + { + if (ho.CreateJudgement().MaxResult.AffectsCombo()) + maxCombo++; + } + + foreach (HitObject ho in beatmap.HitObjects) + { + countCombo(ho); + foreach (HitObject nested in ho.NestedHitObjects) + countCombo(nested); + } int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); int sliderCount = beatmap.HitObjects.Count(h => h is Slider); From 19ee05c232eb86710001bdad3bef2e135f7bc97a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 17 Jan 2022 20:09:02 +0300 Subject: [PATCH 0031/1276] Add "distance spacing" multiplier for osu! ruleset While osu!catch also implements a distance snap grid, it doesn't rely on `GetBeatSnapDistanceAt` (unlike osu!), therefore it can't have the "distance spacing" multiplier yet. --- .../Edit/CatchHitObjectComposer.cs | 2 + .../Edit/ManiaHitObjectComposer.cs | 2 + .../Edit/OsuHitObjectComposer.cs | 2 + .../Edit/TaikoHitObjectComposer.cs | 2 + .../UserInterface/ExpandableSlider.cs | 9 ++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 101 +++++++++++++++++- 6 files changed, 116 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 164f465438..19595de3b1 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -32,6 +32,8 @@ namespace osu.Game.Rulesets.Catch.Edit private readonly Bindable distanceSnapToggle = new Bindable(); + protected override bool SupportsDistanceSpacing => false; + private InputManager inputManager; public CatchHitObjectComposer(CatchRuleset ruleset) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 2baec95c94..5752f9f014 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Mania.Edit private ManiaBeatSnapGrid beatSnapGrid; private InputManager inputManager; + protected override bool SupportsDistanceSpacing => false; + public ManiaHitObjectComposer(Ruleset ruleset) : base(ruleset) { diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 1e84ec80e1..e7eabdc748 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuHitObjectComposer : HitObjectComposer { + protected override bool SupportsDistanceSpacing => true; + public OsuHitObjectComposer(Ruleset ruleset) : base(ruleset) { diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs index 161799c980..af9bc6c3a3 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs @@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Taiko.Edit { public class TaikoHitObjectComposer : HitObjectComposer { + protected override bool SupportsDistanceSpacing => false; + public TaikoHitObjectComposer(TaikoRuleset ruleset) : base(ruleset) { diff --git a/osu.Game/Graphics/UserInterface/ExpandableSlider.cs b/osu.Game/Graphics/UserInterface/ExpandableSlider.cs index 60e83f9c81..a05c0cfab0 100644 --- a/osu.Game/Graphics/UserInterface/ExpandableSlider.cs +++ b/osu.Game/Graphics/UserInterface/ExpandableSlider.cs @@ -70,6 +70,15 @@ namespace osu.Game.Graphics.UserInterface set => slider.Current = value; } + /// + /// A custom step value for each key press which actuates a change on this control. + /// + public float KeyboardStep + { + get => slider.KeyboardStep; + set => slider.KeyboardStep = value; + } + public BindableBool Expanded { get; } = new BindableBool(); public override bool HandlePositionalInput => true; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 39783cc8bb..528ba2fb8b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -14,6 +14,8 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings.Sections; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; @@ -56,6 +58,26 @@ namespace osu.Game.Rulesets.Edit [Resolved] protected IBeatSnapProvider BeatSnapProvider { get; private set; } + /// + /// Whether this composer supports a "distance spacing" multiplier for distance snap grids. + /// + /// + /// Setting this to displays a "distance spacing" slider and allows this composer to configure the value of . + /// + protected abstract bool SupportsDistanceSpacing { get; } + + private readonly BindableFloat distanceSpacing = new BindableFloat + { + Default = 1.0f, + MinValue = 0.1f, + MaxValue = 6.0f, + Precision = 0.01f, + }; + + public override IBindable DistanceSpacingMultiplier => distanceSpacing; + + private SnappingToolboxContainer snappingToolboxContainer; + protected ComposeBlueprintContainer BlueprintContainer { get; private set; } private DrawableEditorRulesetWrapper drawableRulesetWrapper; @@ -117,6 +139,8 @@ namespace osu.Game.Rulesets.Edit }, new LeftToolboxFlow { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, Children = new Drawable[] { new EditorToolboxGroup("toolbox (1-9)") @@ -132,11 +156,41 @@ namespace osu.Game.Rulesets.Edit Direction = FillDirection.Vertical, Spacing = new Vector2(0, 5), }, - } + }, } }, }; + distanceSpacing.Value = (float)EditorBeatmap.BeatmapInfo.DistanceSpacing; + + if (SupportsDistanceSpacing) + { + ExpandableSlider distanceSpacingSlider; + + AddInternal(snappingToolboxContainer = new SnappingToolboxContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Child = new EditorToolboxGroup("snapping") + { + Child = distanceSpacingSlider = new ExpandableSlider + { + Current = { BindTarget = distanceSpacing }, + KeyboardStep = 0.01f, + } + } + }); + + distanceSpacing.BindValueChanged(v => + { + distanceSpacingSlider.ContractedLabelText = $"D. S. ({v.NewValue:0.##x})"; + distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; + EditorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; + }, true); + } + else + distanceSpacing.Disabled = true; + toolboxCollection.Items = CompositionTools .Prepend(new SelectTool()) .Select(t => new RadioButton(t.Name, () => toolSelected(t), t.CreateIcon)) @@ -211,8 +265,17 @@ namespace osu.Game.Rulesets.Edit #region Tool selection logic + private bool distanceSpacingScrollActive; + protected override bool OnKeyDown(KeyDownEvent e) { + if (SupportsDistanceSpacing && e.AltPressed && e.Key == Key.D && !e.Repeat) + { + snappingToolboxContainer.Expanded.Value = true; + distanceSpacingScrollActive = true; + return true; + } + if (e.ControlPressed || e.AltPressed || e.SuperPressed) return false; @@ -242,6 +305,28 @@ namespace osu.Game.Rulesets.Edit return base.OnKeyDown(e); } + protected override void OnKeyUp(KeyUpEvent e) + { + if (distanceSpacingScrollActive && (e.Key == Key.AltLeft || e.Key == Key.AltRight || e.Key == Key.D)) + { + snappingToolboxContainer.Expanded.Value = false; + distanceSpacingScrollActive = false; + } + + base.OnKeyUp(e); + } + + protected override bool OnScroll(ScrollEvent e) + { + if (distanceSpacingScrollActive) + { + distanceSpacing.Value += e.ScrollDelta.Y * (e.ShiftPressed || e.IsPrecise ? 0.01f : 0.1f); + return true; + } + + return base.OnScroll(e); + } + private bool checkLeftToggleFromKey(Key key, out int index) { if (key < Key.Number1 || key > Key.Number9) @@ -383,7 +468,7 @@ namespace osu.Game.Rulesets.Edit public override float GetBeatSnapDistanceAt(HitObject referenceObject) { - return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * referenceObject.DifficultyControlPoint.SliderVelocity / BeatSnapProvider.BeatDivisor); + return (float)(100 * referenceObject.DifficultyControlPoint.SliderVelocity * EditorBeatmap.Difficulty.SliderMultiplier * distanceSpacing.Value / BeatSnapProvider.BeatDivisor); } public override float DurationToDistance(HitObject referenceObject, double duration) @@ -432,6 +517,18 @@ namespace osu.Game.Rulesets.Edit FillFlow.Spacing = new Vector2(10); } } + + private class SnappingToolboxContainer : ExpandingContainer + { + public SnappingToolboxContainer() + : base(130, 250) + { + RelativeSizeAxes = Axes.Y; + Padding = new MarginPadding { Left = 10 }; + + FillFlow.Spacing = new Vector2(10); + } + } } /// From 528dc03b8c06b94d1b45c0d07e8b681ba109fe00 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 Jan 2022 15:25:32 +0300 Subject: [PATCH 0032/1276] Expose distance spacing in `IPositionSnapProvider` for updating distance grid Alternate method is to expose a `SnapDistancesChanged` event in `IPositionSnapProvider` instead, but I chose this way as an analogue to `IBeatSnapProvider.BeatDivisor`, which might even make sense to be exposed as `BindableBeatDivisor` instead of caching that separately. --- .../Editor/TestSceneManiaBeatSnapGrid.cs | 3 +++ .../Editor/TestSceneOsuDistanceSnapGrid.cs | 3 +++ .../Visual/Editing/TestSceneDistanceSnapGrid.cs | 3 +++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 ++ osu.Game/Rulesets/Edit/IPositionSnapProvider.cs | 8 ++++++++ .../Screens/Edit/Compose/Components/DistanceSnapGrid.cs | 6 ++++++ .../Screens/Edit/Compose/Components/Timeline/Timeline.cs | 2 ++ 7 files changed, 27 insertions(+) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs index 50be13c4e0..38779ab47b 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Framework.Timing; @@ -98,6 +99,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor set => InternalChild = value; } + public override IBindable DistanceSpacingMultiplier => throw new System.NotImplementedException(); + public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { throw new System.NotImplementedException(); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index c770e2d96f..c5829d5351 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -4,6 +4,7 @@ using System; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -186,6 +187,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); + public IBindable DistanceSpacingMultiplier { get; } = new BindableFloat(1); + public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length; public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 0d9e06e471..cba6d7692d 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; @@ -166,6 +167,8 @@ namespace osu.Game.Tests.Visual.Editing public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); + public IBindable DistanceSpacingMultiplier { get; } = new BindableFloat(1); + public float GetBeatSnapDistanceAt(HitObject referenceObject) => 10; public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 528ba2fb8b..6cdc2699aa 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -563,6 +563,8 @@ namespace osu.Game.Rulesets.Edit #region IPositionSnapProvider + public abstract IBindable DistanceSpacingMultiplier { get; } + public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); public virtual SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index 743a2f41fc..1cfcb86e10 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Bindables; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osuTK; @@ -8,6 +10,12 @@ namespace osu.Game.Rulesets.Edit { public interface IPositionSnapProvider { + /// + /// The spacing multiplier applied to beat snap distances. + /// + /// + IBindable DistanceSpacingMultiplier { get; } + /// /// Given a position, find a valid time and position snap. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 05bf405f3c..afbe83ec83 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -52,6 +53,8 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved] private BindableBeatDivisor beatDivisor { get; set; } + private IBindable distanceSpacingMultiplier; + private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit); private readonly double? endTime; @@ -82,6 +85,9 @@ namespace osu.Game.Screens.Edit.Compose.Components base.LoadComplete(); beatDivisor.BindValueChanged(_ => updateSpacing(), true); + + distanceSpacingMultiplier = SnapProvider.DistanceSpacingMultiplier.GetBoundCopy(); + distanceSpacingMultiplier.BindValueChanged(_ => updateSpacing(), true); } private void updateSpacing() diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 51cca4ceff..f3a328c6bc 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -317,6 +317,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private double getTimeFromPosition(Vector2 localPosition) => (localPosition.X / Content.DrawWidth) * track.Length; + public IBindable DistanceSpacingMultiplier => throw new NotImplementedException(); + public float GetBeatSnapDistanceAt(HitObject referenceObject) => throw new NotImplementedException(); public float DurationToDistance(HitObject referenceObject, double duration) => throw new NotImplementedException(); From 9504942cfda38af4e6c5c15450a8258f1082de8c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 Jan 2022 18:30:31 +0300 Subject: [PATCH 0033/1276] Add test case for distance spacing hotkeys scrolling behaviour --- .../Editing/TestSceneHitObjectComposer.cs | 24 +++++++++++++++++++ osu.Game/Overlays/SettingsToolboxGroup.cs | 7 ++++++ 2 files changed, 31 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 145d738f60..7ac511e761 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; @@ -21,6 +22,7 @@ using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.RadioButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; +using osuTK.Input; namespace osu.Game.Tests.Visual.Editing { @@ -86,6 +88,28 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is HitCircleCompositionTool); } + [Test] + public void TestDistanceSpacingHotkeys() + { + float originalSpacing = 0; + + AddStep("retrieve original spacing", () => originalSpacing = (float)editorBeatmap.BeatmapInfo.DistanceSpacing); + + AddStep("hold alt", () => InputManager.PressKey(Key.LAlt)); + AddStep("hold D", () => InputManager.PressKey(Key.D)); + + AddStep("scroll mouse 5 steps", () => InputManager.ScrollVerticalBy(5)); + AddAssert("distance spacing increased by 0.5", () => Precision.AlmostEquals(editorBeatmap.BeatmapInfo.DistanceSpacing, originalSpacing + 0.5f)); + + AddStep("hold shift", () => InputManager.PressKey(Key.LShift)); + AddStep("scroll mouse 5 steps", () => InputManager.ScrollVerticalBy(5)); + AddAssert("distance spacing increased by 0.05", () => Precision.AlmostEquals(editorBeatmap.BeatmapInfo.DistanceSpacing, originalSpacing + 0.55f)); + + AddStep("release shift", () => InputManager.ReleaseKey(Key.LShift)); + AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt)); + AddStep("release D", () => InputManager.ReleaseKey(Key.D)); + } + public class EditorBeatmapContainer : Container { private readonly IWorkingBeatmap working; diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 08321f68fe..509410ec20 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -22,6 +22,11 @@ namespace osu.Game.Overlays { public class SettingsToolboxGroup : Container, IExpandable { + /// + /// The title of this toolbox group. + /// + public string Title { get; } + private const float transition_duration = 250; private const int container_width = 270; private const int border_thickness = 2; @@ -48,6 +53,8 @@ namespace osu.Game.Overlays /// The title to be displayed in the header of this group. public SettingsToolboxGroup(string title) { + Title = title; + AutoSizeAxes = Axes.Y; Width = container_width; Masking = true; From 500322ff214753e17a78ca5bb601412ba27e7447 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 23 Jan 2022 06:13:56 +0300 Subject: [PATCH 0034/1276] Update failing snap test scenes --- .../Editor/TestSceneObjectObjectSnap.cs | 7 +++++++ .../Editor/TestSceneSliderSnapping.cs | 10 +++++++++- .../TestSceneHitObjectComposerDistanceSnapping.cs | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs index 7bdf131e0d..41304dcd24 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs @@ -3,9 +3,11 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Bindables; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Tests.Beatmaps; @@ -26,6 +28,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor base.SetUpSteps(); AddStep("get playfield", () => playfield = Editor.ChildrenOfType().First()); AddStep("seek to first control point", () => EditorClock.Seek(Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First().Time)); + AddStep("set distance spacing to 1", () => + { + var distanceSpacing = (BindableFloat)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; + distanceSpacing.Value = 1; + }); } [TestCase(true)] diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index b43b2b1461..23131668a0 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -3,12 +3,15 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Input.Events; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; @@ -61,9 +64,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor })); AddStep("set beat divisor to 1/1", () => { - var beatDivisor = (BindableBeatDivisor)Editor.Dependencies.Get(typeof(BindableBeatDivisor)); + var beatDivisor = Editor.Dependencies.Get(); beatDivisor.Value = 1; }); + AddStep("set distance spacing to 1", () => + { + var distanceSpacing = (BindableFloat)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; + distanceSpacing.Value = 1; + }); } [Test] diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index 43f22e4e90..d9312cb7ae 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -61,6 +61,7 @@ namespace osu.Game.Tests.Editing BeatDivisor.Value = 1; composer.EditorBeatmap.Difficulty.SliderMultiplier = 1; + composer.EditorBeatmap.BeatmapInfo.DistanceSpacing = 1; composer.EditorBeatmap.ControlPointInfo.Clear(); composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }); }); From 868dcd20f56d208d41bdc769f4266b91fd355c84 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 26 Jan 2022 11:39:31 +0300 Subject: [PATCH 0035/1276] Remove `e.ShiftPressed` handling for now Broken on macOS, will be handled differently later on as discussed. --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 6cdc2699aa..948e1d770f 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -320,7 +320,7 @@ namespace osu.Game.Rulesets.Edit { if (distanceSpacingScrollActive) { - distanceSpacing.Value += e.ScrollDelta.Y * (e.ShiftPressed || e.IsPrecise ? 0.01f : 0.1f); + distanceSpacing.Value += e.ScrollDelta.Y * (e.IsPrecise ? 0.01f : 0.1f); return true; } From cbc8f7ff90e95b0d0b1876ac8f0634ac5575f643 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 15 Feb 2022 02:29:09 +0300 Subject: [PATCH 0036/1276] Remove test coverage of "shift" hotkey behaviour --- osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 7ac511e761..d542732e19 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -101,11 +101,6 @@ namespace osu.Game.Tests.Visual.Editing AddStep("scroll mouse 5 steps", () => InputManager.ScrollVerticalBy(5)); AddAssert("distance spacing increased by 0.5", () => Precision.AlmostEquals(editorBeatmap.BeatmapInfo.DistanceSpacing, originalSpacing + 0.5f)); - AddStep("hold shift", () => InputManager.PressKey(Key.LShift)); - AddStep("scroll mouse 5 steps", () => InputManager.ScrollVerticalBy(5)); - AddAssert("distance spacing increased by 0.05", () => Precision.AlmostEquals(editorBeatmap.BeatmapInfo.DistanceSpacing, originalSpacing + 0.55f)); - - AddStep("release shift", () => InputManager.ReleaseKey(Key.LShift)); AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt)); AddStep("release D", () => InputManager.ReleaseKey(Key.D)); } From f7edf25d4c84e2bc61a3f95efd22de52409fc66b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 16 Feb 2022 03:23:02 +0300 Subject: [PATCH 0037/1276] Remove no longer required property --- osu.Game/Overlays/SettingsToolboxGroup.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 509410ec20..08321f68fe 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -22,11 +22,6 @@ namespace osu.Game.Overlays { public class SettingsToolboxGroup : Container, IExpandable { - /// - /// The title of this toolbox group. - /// - public string Title { get; } - private const float transition_duration = 250; private const int container_width = 270; private const int border_thickness = 2; @@ -53,8 +48,6 @@ namespace osu.Game.Overlays /// The title to be displayed in the header of this group. public SettingsToolboxGroup(string title) { - Title = title; - AutoSizeAxes = Axes.Y; Width = container_width; Masking = true; From 7654584e40e8860a20f6bfce1322d6b331da92af Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 16 Feb 2022 03:25:03 +0300 Subject: [PATCH 0038/1276] Allow using `SizeSlider` on different value types --- .../Visual/UserInterface/TestSceneExpandingContainer.cs | 4 ++-- .../Overlays/Settings/Sections/Gameplay/InputSettings.cs | 2 +- osu.Game/Overlays/Settings/Sections/SizeSlider.cs | 6 ++++-- .../Settings/Sections/UserInterface/GeneralSettings.cs | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs index f4920b4412..288c0cb140 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.UserInterface private TestExpandingContainer container; private SettingsToolboxGroup toolboxGroup; - private ExpandableSlider slider1; + private ExpandableSlider> slider1; private ExpandableSlider slider2; [SetUp] @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.UserInterface Width = 1, Children = new Drawable[] { - slider1 = new ExpandableSlider + slider1 = new ExpandableSlider> { Current = new BindableFloat { diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs index 962572ca6e..83ea655601 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { Children = new Drawable[] { - new SettingsSlider + new SettingsSlider> { LabelText = SkinSettingsStrings.GameplayCursorSize, Current = config.GetBindable(OsuSetting.GameplayCursorSize), diff --git a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs index 8aeb440be1..cc2c77adf2 100644 --- a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs +++ b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; @@ -9,8 +10,9 @@ namespace osu.Game.Overlays.Settings.Sections /// /// A slider intended to show a "size" multiplier number, where 1x is 1.0. /// - internal class SizeSlider : OsuSliderBar + internal class SizeSlider : OsuSliderBar + where T : struct, IEquatable, IComparable, IConvertible, IFormattable { - public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x"); + public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", null); } } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 0afbed5df5..66702744d3 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface LabelText = UserInterfaceStrings.CursorRotation, Current = config.GetBindable(OsuSetting.CursorRotation) }, - new SettingsSlider + new SettingsSlider> { LabelText = UserInterfaceStrings.MenuCursorSize, Current = config.GetBindable(OsuSetting.MenuCursorSize), From 0992bec2c849d9e5a7be97d1d9396027a1f8f4e1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 16 Feb 2022 03:28:05 +0300 Subject: [PATCH 0039/1276] Change "distance spacing" multipler type to `double` Avoids losing precision on initial load, causing an unnecessary hash change in `EditorChangeHandler`. Resolves test failures in `TestSceneEditorChangeStates` (https://github.com/ppy/osu/runs/5192493482?check_suite_focus=true). --- .../Editor/TestSceneManiaBeatSnapGrid.cs | 2 +- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 2 +- .../Editing/TestSceneDistanceSnapGrid.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 20 +++++++++---------- .../Rulesets/Edit/IPositionSnapProvider.cs | 2 +- .../Compose/Components/DistanceSnapGrid.cs | 2 +- .../Compose/Components/Timeline/Timeline.cs | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs index 38779ab47b..f32a1a823e 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor set => InternalChild = value; } - public override IBindable DistanceSpacingMultiplier => throw new System.NotImplementedException(); + public override IBindable DistanceSpacingMultiplier => throw new System.NotImplementedException(); public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index c5829d5351..38a442d48b 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); - public IBindable DistanceSpacingMultiplier { get; } = new BindableFloat(1); + public IBindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index cba6d7692d..69fd642143 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.Editing public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); - public IBindable DistanceSpacingMultiplier { get; } = new BindableFloat(1); + public IBindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); public float GetBeatSnapDistanceAt(HitObject referenceObject) => 10; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 948e1d770f..ab82fbad04 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -66,15 +66,15 @@ namespace osu.Game.Rulesets.Edit /// protected abstract bool SupportsDistanceSpacing { get; } - private readonly BindableFloat distanceSpacing = new BindableFloat + private readonly BindableDouble distanceSpacing = new BindableDouble { - Default = 1.0f, - MinValue = 0.1f, - MaxValue = 6.0f, - Precision = 0.01f, + Default = 1.0, + MinValue = 0.1, + MaxValue = 6.0, + Precision = 0.01, }; - public override IBindable DistanceSpacingMultiplier => distanceSpacing; + public override IBindable DistanceSpacingMultiplier => distanceSpacing; private SnappingToolboxContainer snappingToolboxContainer; @@ -161,11 +161,11 @@ namespace osu.Game.Rulesets.Edit }, }; - distanceSpacing.Value = (float)EditorBeatmap.BeatmapInfo.DistanceSpacing; + distanceSpacing.Value = EditorBeatmap.BeatmapInfo.DistanceSpacing; if (SupportsDistanceSpacing) { - ExpandableSlider distanceSpacingSlider; + ExpandableSlider> distanceSpacingSlider; AddInternal(snappingToolboxContainer = new SnappingToolboxContainer { @@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Edit Origin = Anchor.TopRight, Child = new EditorToolboxGroup("snapping") { - Child = distanceSpacingSlider = new ExpandableSlider + Child = distanceSpacingSlider = new ExpandableSlider> { Current = { BindTarget = distanceSpacing }, KeyboardStep = 0.01f, @@ -563,7 +563,7 @@ namespace osu.Game.Rulesets.Edit #region IPositionSnapProvider - public abstract IBindable DistanceSpacingMultiplier { get; } + public abstract IBindable DistanceSpacingMultiplier { get; } public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index 1cfcb86e10..e4adbba47d 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Edit /// The spacing multiplier applied to beat snap distances. /// /// - IBindable DistanceSpacingMultiplier { get; } + IBindable DistanceSpacingMultiplier { get; } /// /// Given a position, find a valid time and position snap. diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index afbe83ec83..f9e4ef086c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved] private BindableBeatDivisor beatDivisor { get; set; } - private IBindable distanceSpacingMultiplier; + private IBindable distanceSpacingMultiplier; private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit); private readonly double? endTime; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index f3a328c6bc..8832c4e89c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -317,7 +317,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private double getTimeFromPosition(Vector2 localPosition) => (localPosition.X / Content.DrawWidth) * track.Length; - public IBindable DistanceSpacingMultiplier => throw new NotImplementedException(); + public IBindable DistanceSpacingMultiplier => throw new NotImplementedException(); public float GetBeatSnapDistanceAt(HitObject referenceObject) => throw new NotImplementedException(); From 215da7e933ded0423618e6fb6f58e28c65ea2339 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 16 Feb 2022 12:05:55 +0900 Subject: [PATCH 0040/1276] Reimplement as extension method on IBeatmap Implementation has changed slightly to support arbitrary levels of nested hitobjects. --- .../Difficulty/OsuDifficultyCalculator.cs | 17 +------------ osu.Game/Beatmaps/IBeatmap.cs | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index d04d0872d8..df6fd19d36 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -9,7 +9,6 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Skills; using osu.Game.Rulesets.Osu.Mods; @@ -62,21 +61,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; - - int maxCombo = 0; - - void countCombo(HitObject ho) - { - if (ho.CreateJudgement().MaxResult.AffectsCombo()) - maxCombo++; - } - - foreach (HitObject ho in beatmap.HitObjects) - { - countCombo(ho); - foreach (HitObject nested in ho.NestedHitObjects) - countCombo(nested); - } + int maxCombo = beatmap.GetMaxCombo(); int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); int sliderCount = beatmap.HitObjects.Count(h => h is Slider); diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 3f598cd1e5..dec1ef4294 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Beatmaps { @@ -70,4 +71,27 @@ namespace osu.Game.Beatmaps /// new IReadOnlyList HitObjects { get; } } + + public static class BeatmapExtensions + { + /// + /// Finds the maximum achievable combo by hitting all s in a beatmap. + /// + public static int GetMaxCombo(this IBeatmap beatmap) + { + int combo = 0; + foreach (var h in beatmap.HitObjects) + addCombo(h, ref combo); + return combo; + + static void addCombo(HitObject hitObject, ref int combo) + { + if (hitObject.CreateJudgement().MaxResult.AffectsCombo()) + combo++; + + foreach (var nested in hitObject.NestedHitObjects) + addCombo(nested, ref combo); + } + } + } } From 8b30c847f9ab3829b8fc8bc0e5f944a217cba8e5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 16 Feb 2022 08:39:08 +0300 Subject: [PATCH 0041/1276] Update outdated bindable casts in tests --- osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs | 2 +- osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs index 41304dcd24..aa0e35102e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("seek to first control point", () => EditorClock.Seek(Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First().Time)); AddStep("set distance spacing to 1", () => { - var distanceSpacing = (BindableFloat)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; + var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; distanceSpacing.Value = 1; }); } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index 23131668a0..13ce340362 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor }); AddStep("set distance spacing to 1", () => { - var distanceSpacing = (BindableFloat)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; + var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; distanceSpacing.Value = 1; }); } From b66566e96d4f22d7e4351443ea8d999fd7f9bce7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 16 Feb 2022 10:35:57 +0300 Subject: [PATCH 0042/1276] Use explicit culture info rather than `null` --- osu.Game/Overlays/Settings/Sections/SizeSlider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs index cc2c77adf2..26db1aa755 100644 --- a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs +++ b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Globalization; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; @@ -13,6 +14,6 @@ namespace osu.Game.Overlays.Settings.Sections internal class SizeSlider : OsuSliderBar where T : struct, IEquatable, IComparable, IConvertible, IFormattable { - public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", null); + public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", NumberFormatInfo.CurrentInfo); } } From 3945cd24ebb84579fe1492c083d820155581f652 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 17 Feb 2022 21:14:49 +0900 Subject: [PATCH 0043/1276] wip --- .../Rulesets/Difficulty/DifficultyCalculator.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 6b61dd3efb..7d6c235fc1 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -15,6 +15,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Utils; namespace osu.Game.Rulesets.Difficulty { @@ -122,12 +123,17 @@ namespace osu.Game.Rulesets.Difficulty /// A collection of structures describing the difficulty of the beatmap for each mod combination. public IEnumerable CalculateAll(CancellationToken cancellationToken = default) { + var rulesetInstance = ruleset.CreateInstance(); + foreach (var combination in CreateDifficultyAdjustmentModCombinations()) { - if (combination is MultiMod multi) - yield return Calculate(multi.Mods, cancellationToken); - else - yield return Calculate(combination.Yield(), cancellationToken); + Mod classicMod = rulesetInstance.CreateAllMods().SingleOrDefault(m => m is ModClassic); + + var finalCombination = ModUtils.FlattenMod(combination); + if (classicMod != null) + finalCombination = finalCombination.Append(classicMod); + + yield return Calculate(finalCombination.ToArray(), cancellationToken); } } From bedd07d2e4dca93e160949614f9f1a75f03a2fe2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 22 Feb 2022 18:12:55 +0900 Subject: [PATCH 0044/1276] Add remark about usage of CalculateAll() --- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 7d6c235fc1..0935f26de6 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -120,6 +120,9 @@ namespace osu.Game.Rulesets.Difficulty /// /// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap. /// + /// + /// This should only be used to compute difficulties for legacy mod combinations. + /// /// A collection of structures describing the difficulty of the beatmap for each mod combination. public IEnumerable CalculateAll(CancellationToken cancellationToken = default) { From 1025e1939ba90b96595c3ab0d9fc5a86a3f0527a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 16 Mar 2022 11:54:18 +0300 Subject: [PATCH 0045/1276] Disable "Adaptive Speed" mod in multiplayer --- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index e30ec36e9c..5ccf89e703 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -117,6 +117,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); + protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && !(mod is ModAdaptiveSpeed); + protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && !(mod is ModTimeRamp) && !(mod is ModRateAdjust); } } From d90a33485318f926e51c6f9da270876756bfab30 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Mar 2022 03:40:15 +0300 Subject: [PATCH 0046/1276] Introduce multiplayer playability and free mod validity in `Mod` --- osu.Game/Rulesets/Mods/IMod.cs | 12 ++++++++++++ osu.Game/Rulesets/Mods/Mod.cs | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index d5d1de91de..cda59bae55 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -39,6 +39,18 @@ namespace osu.Game.Rulesets.Mods /// bool UserPlayable { get; } + /// + /// Whether this mod is playable in a multiplayer match. + /// Should be false for mods that affect the gameplay progress based on user input (e.g. ). + /// + bool PlayableInMultiplayer { get; } + + /// + /// Whether this mod is valid to be a "free mod" in a multiplayer match. + /// Should be false for mods that affect the gameplay progress (e.g. and ). + /// + bool ValidFreeModInMultiplayer { get; } + /// /// Create a fresh instance based on this mod. /// diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b2d4be54ce..a6562b4f4c 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -94,6 +94,12 @@ namespace osu.Game.Rulesets.Mods [JsonIgnore] public virtual bool UserPlayable => true; + [JsonIgnore] + public virtual bool PlayableInMultiplayer => UserPlayable; + + [JsonIgnore] + public virtual bool ValidFreeModInMultiplayer => PlayableInMultiplayer; + [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override and set UserPlayable to false.")] // Can be removed 20211009 public virtual bool Ranked => false; From 187059a37f69ed4ceff8ea4e2e2a0b8dda3b0085 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Mar 2022 03:40:40 +0300 Subject: [PATCH 0047/1276] Replace hardcoded overrides with the newly introduced `Mod` properties --- osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | 2 ++ osu.Game/Rulesets/Mods/ModRateAdjust.cs | 2 ++ osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 ++ .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 4 ++-- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs index 1115b95e6f..54a4c054c9 100644 --- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs +++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs @@ -31,6 +31,8 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 1; + public override bool PlayableInMultiplayer => false; + public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModTimeRamp) }; [SettingSource("Initial rate", "The starting speed of the track")] diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index ebe18f2188..3a11c3034b 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModRateAdjust : Mod, IApplicableToRate { + public override bool ValidFreeModInMultiplayer => false; + public abstract BindableNumber SpeedChange { get; } public virtual void ApplyToTrack(ITrack track) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index b6b2decede..59aac62686 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] public abstract BindableBool AdjustPitch { get; } + public override bool ValidFreeModInMultiplayer => false; + public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModAdaptiveSpeed) }; public override string SettingDescription => $"{InitialRate.Value:N2}x to {FinalRate.Value:N2}x"; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 5ccf89e703..e4f1d9587d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -117,8 +117,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && !(mod is ModAdaptiveSpeed); + protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && mod.PlayableInMultiplayer; - protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && !(mod is ModTimeRamp) && !(mod is ModRateAdjust); + protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && mod.ValidFreeModInMultiplayer; } } From 59741ccee60d9b05466d9b7a1fea6157106ba5d8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Mar 2022 05:15:05 +0300 Subject: [PATCH 0048/1276] Add multiplayer mod validity check methods for server consumption --- osu.Game/OsuGame.cs | 15 ++++++++++----- osu.Game/Utils/ModUtils.cs | 26 +++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ae117d03d2..bc2d2083fe 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -575,11 +575,16 @@ namespace osu.Game if (SelectedMods.Disabled) return; - if (!ModUtils.CheckValidForGameplay(mods.NewValue, out var invalid)) - { - // ensure we always have a valid set of mods. - SelectedMods.Value = mods.NewValue.Except(invalid).ToArray(); - } + var validMods = mods.NewValue; + + if (!ModUtils.CheckCompatibleSet(validMods, out var incompatible)) + validMods = validMods.Except(incompatible).ToArray(); + + if (!ModUtils.CheckValidForGameplay(validMods, out var invalid)) + validMods = validMods.Except(invalid).ToArray(); + + // ensure we always have a valid set of mods. + SelectedMods.Value = validMods; } #endregion diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index d5ea74c404..d169ace80a 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -112,14 +112,34 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Can be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidForGameplay(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) + => checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && !(m is MultiMod), out invalidMods); + + /// + /// Check the provided combination of mods are valid for a multiplayer match session. + /// + /// The mods to check. + /// Invalid mods, if any were found. Can be null if all mods were valid. + /// Whether the input mods were all valid. If false, will contain all invalid entries. + public static bool CheckValidForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) + => checkValid(mods, m => m.PlayableInMultiplayer, out invalidMods); + + /// + /// Check the provided combination of mods are valid as "free mods" in a multiplayer match session. + /// + /// The mods to check. + /// Invalid mods, if any were found. Can be null if all mods were valid. + /// Whether the input mods were all valid. If false, will contain all invalid entries. + public static bool CheckValidFreeModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) + => checkValid(mods, m => m.ValidFreeModInMultiplayer, out invalidMods); + + private static bool checkValid(IEnumerable mods, Predicate valid, [NotNullWhen(false)] out List? invalidMods) { mods = mods.ToArray(); - - CheckCompatibleSet(mods, out invalidMods); + invalidMods = null; foreach (var mod in mods) { - if (mod.Type == ModType.System || !mod.HasImplementation || mod is MultiMod) + if (!valid(mod)) { invalidMods ??= new List(); invalidMods.Add(mod); From 07e9f3780a8335ca07d3e076193c0b7cbdc80982 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Mar 2022 05:15:48 +0300 Subject: [PATCH 0049/1276] Consider `UnknownMod` to be "playable in multiplayer" --- osu.Game/Rulesets/Mods/UnknownMod.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Mods/UnknownMod.cs b/osu.Game/Rulesets/Mods/UnknownMod.cs index b426386d7a..790306c0ca 100644 --- a/osu.Game/Rulesets/Mods/UnknownMod.cs +++ b/osu.Game/Rulesets/Mods/UnknownMod.cs @@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 0; public override bool UserPlayable => false; + public override bool PlayableInMultiplayer => true; public override ModType Type => ModType.System; From b3ac544d655e6846a94b6bf2d31332b471d90de4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 17 Mar 2022 06:31:51 +0300 Subject: [PATCH 0050/1276] Revert "Consider `UnknownMod` to be "playable in multiplayer"" This reverts commit 07e9f3780a8335ca07d3e076193c0b7cbdc80982. --- osu.Game/Rulesets/Mods/UnknownMod.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/UnknownMod.cs b/osu.Game/Rulesets/Mods/UnknownMod.cs index 790306c0ca..b426386d7a 100644 --- a/osu.Game/Rulesets/Mods/UnknownMod.cs +++ b/osu.Game/Rulesets/Mods/UnknownMod.cs @@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 0; public override bool UserPlayable => false; - public override bool PlayableInMultiplayer => true; public override ModType Type => ModType.System; From e3a5299b1acc6bba39f5b9de2726159add8ecb89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 19:23:43 +0900 Subject: [PATCH 0051/1276] Expose the loading player in `PlayerLoader` --- osu.Game/Screens/Play/PlayerLoader.cs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 41eb822e39..384561d616 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Play private bool readyForPush => !playerConsumed // don't push unless the player is completely loaded - && player?.LoadState == LoadState.Ready + && CurrentPlayer?.LoadState == LoadState.Ready // don't push if the user is hovering one of the panes, unless they are idle. && (IsHovered || idleTracker.IsIdle.Value) // don't push if the user is dragging a slider or otherwise. @@ -100,10 +100,14 @@ namespace osu.Game.Screens.Play private readonly Func createPlayer; - private Player player; + /// + /// The instance being loaded by this screen. + /// + [CanBeNull] + public Player CurrentPlayer { get; private set; } /// - /// Whether the curent player instance has been consumed via . + /// Whether the current player instance has been consumed via . /// private bool playerConsumed; @@ -237,12 +241,12 @@ namespace osu.Game.Screens.Play { base.OnResuming(last); - var lastScore = player.Score; + var lastScore = CurrentPlayer.Score; AudioSettings.ReferenceScore.Value = lastScore?.ScoreInfo; // prepare for a retry. - player = null; + CurrentPlayer = null; playerConsumed = false; cancelLoad(); @@ -346,7 +350,7 @@ namespace osu.Game.Screens.Play Debug.Assert(!playerConsumed); playerConsumed = true; - return player; + return CurrentPlayer; } private void prepareNewPlayer() @@ -354,11 +358,11 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - player = createPlayer(); - player.RestartCount = restartCount++; - player.RestartRequested = restartRequested; + CurrentPlayer = createPlayer(); + CurrentPlayer.RestartCount = restartCount++; + CurrentPlayer.RestartRequested = restartRequested; - LoadTask = LoadComponentAsync(player, _ => MetadataInfo.Loading = false); + LoadTask = LoadComponentAsync(CurrentPlayer, _ => MetadataInfo.Loading = false); } private void restartRequested() @@ -472,7 +476,7 @@ namespace osu.Game.Screens.Play if (isDisposing) { // if the player never got pushed, we should explicitly dispose it. - DisposalTask = LoadTask?.ContinueWith(_ => player?.Dispose()); + DisposalTask = LoadTask?.ContinueWith(_ => CurrentPlayer?.Dispose()); } } From 5164b4d6403bcb83fedbd8f83a35999eb1eeb58f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 19:36:33 +0900 Subject: [PATCH 0052/1276] Use `nullable` in `PlayerLoader` --- osu.Game/Screens/Play/PlayerLoader.cs | 54 ++++++++++++++------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 384561d616..ba720af2a1 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -1,10 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Diagnostics; using System.Threading.Tasks; -using JetBrains.Annotations; using ManagedBass.Fx; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -48,31 +49,31 @@ namespace osu.Game.Screens.Play public override bool HandlePositionalInput => true; // We show the previous screen status - protected override UserActivity InitialActivity => null; + protected override UserActivity? InitialActivity => null; protected override bool PlayResumeSound => false; - protected BeatmapMetadataDisplay MetadataInfo { get; private set; } + protected BeatmapMetadataDisplay MetadataInfo { get; private set; } = null!; /// /// A fill flow containing the player settings groups, exposed for the ability to hide it from inheritors of the player loader. /// - protected FillFlowContainer PlayerSettings { get; private set; } + protected FillFlowContainer PlayerSettings { get; private set; } = null!; - protected VisualSettings VisualSettings { get; private set; } + protected VisualSettings VisualSettings { get; private set; } = null!; - protected AudioSettings AudioSettings { get; private set; } + protected AudioSettings AudioSettings { get; private set; } = null!; - protected Task LoadTask { get; private set; } + protected Task? LoadTask { get; private set; } - protected Task DisposalTask { get; private set; } + protected Task? DisposalTask { get; private set; } private bool backgroundBrightnessReduction; private readonly BindableDouble volumeAdjustment = new BindableDouble(1); - private AudioFilter lowPassFilter; - private AudioFilter highPassFilter; + private AudioFilter lowPassFilter = null!; + private AudioFilter highPassFilter = null!; protected bool BackgroundBrightnessReduction { @@ -94,47 +95,45 @@ namespace osu.Game.Screens.Play // don't push if the user is hovering one of the panes, unless they are idle. && (IsHovered || idleTracker.IsIdle.Value) // don't push if the user is dragging a slider or otherwise. - && inputManager?.DraggedDrawable == null + && inputManager.DraggedDrawable == null // don't push if a focused overlay is visible, like settings. - && inputManager?.FocusedDrawable == null; + && inputManager.FocusedDrawable == null; private readonly Func createPlayer; /// /// The instance being loaded by this screen. /// - [CanBeNull] - public Player CurrentPlayer { get; private set; } + public Player? CurrentPlayer { get; private set; } /// /// Whether the current player instance has been consumed via . /// private bool playerConsumed; - private LogoTrackingContainer content; + private LogoTrackingContainer content = null!; private bool hideOverlays; - private InputManager inputManager; + private InputManager inputManager = null!; - private IdleTracker idleTracker; + private IdleTracker idleTracker = null!; - private ScheduledDelegate scheduledPushPlayer; + private ScheduledDelegate? scheduledPushPlayer; - [CanBeNull] - private EpilepsyWarning epilepsyWarning; + private EpilepsyWarning? epilepsyWarning; [Resolved(CanBeNull = true)] - private NotificationOverlay notificationOverlay { get; set; } + private NotificationOverlay? notificationOverlay { get; set; } [Resolved(CanBeNull = true)] - private VolumeOverlay volumeOverlay { get; set; } + private VolumeOverlay? volumeOverlay { get; set; } [Resolved] - private AudioManager audioManager { get; set; } + private AudioManager audioManager { get; set; } = null!; [Resolved(CanBeNull = true)] - private BatteryInfo batteryInfo { get; set; } + private BatteryInfo? batteryInfo { get; set; } public PlayerLoader(Func createPlayer) { @@ -241,6 +240,8 @@ namespace osu.Game.Screens.Play { base.OnResuming(last); + Debug.Assert(CurrentPlayer != null); + var lastScore = CurrentPlayer.Score; AudioSettings.ReferenceScore.Value = lastScore?.ScoreInfo; @@ -348,6 +349,7 @@ namespace osu.Game.Screens.Play private Player consumePlayer() { Debug.Assert(!playerConsumed); + Debug.Assert(CurrentPlayer != null); playerConsumed = true; return CurrentPlayer; @@ -484,7 +486,7 @@ namespace osu.Game.Screens.Play #region Mute warning - private Bindable muteWarningShownOnce; + private Bindable muteWarningShownOnce = null!; private int restartCount; @@ -539,7 +541,7 @@ namespace osu.Game.Screens.Play #region Low battery warning - private Bindable batteryWarningShownOnce; + private Bindable batteryWarningShownOnce = null!; private void showBatteryWarningIfNeeded() { From e3a8bb2d1c36c4995177a2826126e47dc2a0c6eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 19:23:49 +0900 Subject: [PATCH 0053/1276] Add test coverage of `SpectatorPlayer` failing to seek on inopportune frame arrival time --- .../Visual/Gameplay/TestSceneSpectator.cs | 54 ++++++++++++++++++- .../Visual/Spectator/TestSpectatorClient.cs | 5 +- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index d614815316..8b420cebc8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -70,6 +70,56 @@ namespace osu.Game.Tests.Visual.Gameplay }); } + [Test] + public void TestSeekToGameplayStartFramesArriveAfterPlayerLoad() + { + const double gameplay_start = 10000; + + loadSpectatingScreen(); + + start(); + + waitForPlayer(); + + sendFrames(startTime: gameplay_start); + + AddAssert("time is greater than seek target", () => currentFrameStableTime > gameplay_start); + } + + /// + /// Tests the same as but with the frames arriving just as is transitioning into existence. + /// + [Test] + public void TestSeekToGameplayStartFramesArriveAsPlayerLoaded() + { + const double gameplay_start = 10000; + + loadSpectatingScreen(); + + start(); + + AddUntilStep("wait for player loader", () => (Stack.CurrentScreen as PlayerLoader)?.IsLoaded == true); + + AddUntilStep("queue send frames on player load", () => + { + var loadingPlayer = (Stack.CurrentScreen as PlayerLoader)?.CurrentPlayer; + + if (loadingPlayer == null) + return false; + + loadingPlayer.OnLoadComplete += _ => + { + spectatorClient.SendFramesFromUser(streamingUser.Id, 10, gameplay_start); + }; + return true; + }); + + waitForPlayer(); + + AddUntilStep("state is playing", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Playing); + AddAssert("time is greater than seek target", () => currentFrameStableTime > gameplay_start); + } + [Test] public void TestFrameStarvationAndResume() { @@ -319,9 +369,9 @@ namespace osu.Game.Tests.Visual.Gameplay private void checkPaused(bool state) => AddUntilStep($"game is {(state ? "paused" : "playing")}", () => player.ChildrenOfType().First().IsPaused.Value == state); - private void sendFrames(int count = 10) + private void sendFrames(int count = 10, double startTime = 0) { - AddStep("send frames", () => spectatorClient.SendFramesFromUser(streamingUser.Id, count)); + AddStep("send frames", () => spectatorClient.SendFramesFromUser(streamingUser.Id, count, startTime)); } private void loadSpectatingScreen() diff --git a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs index f5da95bd7b..ac7cb43e02 100644 --- a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs +++ b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs @@ -88,7 +88,8 @@ namespace osu.Game.Tests.Visual.Spectator /// /// The user to send frames for. /// The total number of frames to send. - public void SendFramesFromUser(int userId, int count) + /// The time to start gameplay frames from. + public void SendFramesFromUser(int userId, int count, double startTime = 0) { var frames = new List(); @@ -102,7 +103,7 @@ namespace osu.Game.Tests.Visual.Spectator flush(); var buttonState = currentFrameIndex == lastFrameIndex ? ReplayButtonState.None : ReplayButtonState.Left1; - frames.Add(new LegacyReplayFrame(currentFrameIndex * 100, RNG.Next(0, 512), RNG.Next(0, 512), buttonState)); + frames.Add(new LegacyReplayFrame(currentFrameIndex * 100 + startTime, RNG.Next(0, 512), RNG.Next(0, 512), buttonState)); } flush(); From a4a0241800934b62349ac11b913e5e0a1c94c929 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 20:54:42 +0900 Subject: [PATCH 0054/1276] Use a more explicit flow to set and reset `GameplayClockContainer` start time --- .../Screens/Play/GameplayClockContainer.cs | 20 ++++++-- .../Play/MasterGameplayClockContainer.cs | 49 ++++++++++--------- osu.Game/Screens/Play/Player.cs | 10 ++-- osu.Game/Screens/Play/SpectatorPlayer.cs | 2 +- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 0fd524f976..b60a3e306e 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -41,6 +41,15 @@ namespace osu.Game.Screens.Play /// public event Action OnSeek; + /// + /// The time from which gameplay should start. Will be seeked to on calling . + /// + /// + /// If not set, a value of zero will be used. + /// Importantly, the value will be inferred from the current ruleset in unless specified. + /// + protected double? GameplayStartTime { get; private set; } + /// /// Creates a new . /// @@ -106,15 +115,20 @@ namespace osu.Game.Screens.Play /// /// Resets this and the source to an initial state ready for gameplay. /// - public virtual void Reset() + /// Whether to start the clock immediately. + /// A time to use for future calls as the definite start of gameplay. + public void Reset(bool startClock = false, double? gameplayStartTime = null) { + if (gameplayStartTime != null) + GameplayStartTime = gameplayStartTime; + ensureSourceClockSet(); - Seek(0); + Seek(GameplayStartTime ?? 0); // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); - if (!IsPaused.Value) + if (!IsPaused.Value && startClock) Start(); } diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index af58e9d910..ce7b6ef7f0 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -58,7 +58,6 @@ namespace osu.Game.Screens.Play private HardwareCorrectionOffsetClock platformOffsetClock; private MasterGameplayClock masterGameplayClock; private Bindable userAudioOffset; - private double startOffset; private IDisposable beatmapOffsetSubscription; @@ -90,26 +89,33 @@ namespace osu.Game.Screens.Play settings => settings.Offset, val => userBeatmapOffsetClock.Offset = val); - // sane default provided by ruleset. - startOffset = gameplayStartTime; - - if (!startAtGameplayStart) + if (GameplayStartTime == null) { - startOffset = Math.Min(0, startOffset); + // sane default provided by ruleset. + double offset = gameplayStartTime; - // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. - // this is commonly used to display an intro before the audio track start. - double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; - if (firstStoryboardEvent != null) - startOffset = Math.Min(startOffset, firstStoryboardEvent.Value); + if (!startAtGameplayStart) + { + offset = Math.Min(0, offset); - // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. - // this is not available as an option in the live editor but can still be applied via .osu editing. - if (beatmap.BeatmapInfo.AudioLeadIn > 0) - startOffset = Math.Min(startOffset, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn); + // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. + // this is commonly used to display an intro before the audio track start. + double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; + if (firstStoryboardEvent != null) + offset = Math.Min(offset, firstStoryboardEvent.Value); + + // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. + // this is not available as an option in the live editor but can still be applied via .osu editing. + if (beatmap.BeatmapInfo.AudioLeadIn > 0) + offset = Math.Min(offset, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn); + } + + // Reset may have been called externally before LoadComplete. + // If it was and the clock is in a playing state, we want to ensure that it isn't stopped here. + bool isStarted = !IsPaused.Value; + + Reset(startClock: isStarted, gameplayStartTime: offset); } - - Seek(startOffset); } protected override void OnIsPausedChanged(ValueChangedEvent isPaused) @@ -164,12 +170,6 @@ namespace osu.Game.Screens.Play Seek(skipTarget); } - public override void Reset() - { - base.Reset(); - Seek(startOffset); - } - protected override GameplayClock CreateGameplayClock(IFrameBasedClock source) { // Lazer's audio timings in general doesn't match stable. This is the result of user testing, albeit limited. @@ -231,6 +231,7 @@ namespace osu.Game.Screens.Play } private class HardwareCorrectionOffsetClock : FramedOffsetClock + { private readonly BindableDouble pauseRateAdjust; @@ -276,9 +277,9 @@ namespace osu.Game.Screens.Play } private class MasterGameplayClock : GameplayClock + { public readonly List> MutableNonGameplayAdjustments = new List>(); - public override IEnumerable> NonGameplayAdjustments => MutableNonGameplayAdjustments; public MasterGameplayClock(FramedOffsetClock underlyingClock) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cb8f4b6020..6ec21ef924 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -607,13 +607,13 @@ namespace osu.Game.Screens.Play private ScheduledDelegate frameStablePlaybackResetDelegate; /// - /// Seeks to a specific time in gameplay, bypassing frame stability. + /// Specify and seek to a custom start time from which gameplay should be observed. /// /// - /// Intermediate hitobject judgements may not be applied or reverted correctly during this seek. + /// This performance a non-frame-stable seek. Intermediate hitobject judgements may not be applied or reverted correctly during this seek. /// /// The destination time to seek to. - internal void NonFrameStableSeek(double time) + protected void SetGameplayStartTime(double time) { if (frameStablePlaybackResetDelegate?.Cancelled == false && !frameStablePlaybackResetDelegate.Completed) frameStablePlaybackResetDelegate.RunTask(); @@ -621,7 +621,7 @@ namespace osu.Game.Screens.Play bool wasFrameStable = DrawableRuleset.FrameStablePlayback; DrawableRuleset.FrameStablePlayback = false; - Seek(time); + GameplayClockContainer.Reset(gameplayStartTime: time); // Delay resetting frame-stable playback for one frame to give the FrameStabilityContainer a chance to seek. frameStablePlaybackResetDelegate = ScheduleAfterChildren(() => DrawableRuleset.FrameStablePlayback = wasFrameStable); @@ -981,7 +981,7 @@ namespace osu.Game.Screens.Play if (GameplayClockContainer.GameplayClock.IsRunning) throw new InvalidOperationException($"{nameof(StartGameplay)} should not be called when the gameplay clock is already running"); - GameplayClockContainer.Reset(); + GameplayClockContainer.Reset(true); } public override void OnSuspending(IScreen next) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index c415041081..c0682952c3 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Play } if (isFirstBundle && score.Replay.Frames.Count > 0) - NonFrameStableSeek(score.Replay.Frames[0].Time); + SetGameplayStartTime(score.Replay.Frames[0].Time); } protected override Score CreateScore(IBeatmap beatmap) => score; From e3ab5de8cd944a35228d47bba2623fbd5ee712ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 23:39:45 +0900 Subject: [PATCH 0055/1276] Tidy up constructor logic overlap with `gameplayStartTime` --- .../TestSceneMultiSpectatorScreen.cs | 2 +- .../Screens/Play/GameplayClockContainer.cs | 8 +-- .../Play/MasterGameplayClockContainer.cs | 69 ++++++++++--------- 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index e5e3fecd06..ed8b0bdd96 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -473,7 +473,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } protected override MasterGameplayClockContainer CreateMasterGameplayClockContainer(WorkingBeatmap beatmap) - => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0, gameplayStartTime.HasValue); + => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0); } } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index b60a3e306e..66f5cb0bd4 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -42,13 +42,13 @@ namespace osu.Game.Screens.Play public event Action OnSeek; /// - /// The time from which gameplay should start. Will be seeked to on calling . + /// The time from which the clock should start. Will be seeked to on calling . /// /// /// If not set, a value of zero will be used. /// Importantly, the value will be inferred from the current ruleset in unless specified. /// - protected double? GameplayStartTime { get; private set; } + protected double? StartTime { get; set; } /// /// Creates a new . @@ -120,10 +120,10 @@ namespace osu.Game.Screens.Play public void Reset(bool startClock = false, double? gameplayStartTime = null) { if (gameplayStartTime != null) - GameplayStartTime = gameplayStartTime; + StartTime = gameplayStartTime; ensureSourceClockSet(); - Seek(GameplayStartTime ?? 0); + Seek(StartTime ?? 0); // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index ce7b6ef7f0..58e8b5f1ad 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -49,9 +49,6 @@ namespace osu.Game.Screens.Play private readonly BindableDouble pauseFreqAdjust = new BindableDouble(1); private readonly WorkingBeatmap beatmap; - private readonly double gameplayStartTime; - private readonly bool startAtGameplayStart; - private readonly double firstHitObjectTime; private HardwareCorrectionOffsetClock userGlobalOffsetClock; private HardwareCorrectionOffsetClock userBeatmapOffsetClock; @@ -61,20 +58,29 @@ namespace osu.Game.Screens.Play private IDisposable beatmapOffsetSubscription; + private readonly double latestGameplayStartTime; + [Resolved] private RealmAccess realm { get; set; } [Resolved] private OsuConfigManager config { get; set; } - public MasterGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStartTime, bool startAtGameplayStart = false) + /// + /// Create a new master gameplay clock container. + /// + /// The beatmap to be used for time and metadata references. + /// The latest time which should be used when introducing gameplay. Will be used when skipping forward. + /// Whether to start from the provided latest start time rather than zero. + public MasterGameplayClockContainer(WorkingBeatmap beatmap, double latestGameplayStartTime, bool startFromLatestStartTime = false) : base(beatmap.Track) { this.beatmap = beatmap; - this.gameplayStartTime = gameplayStartTime; - this.startAtGameplayStart = startAtGameplayStart; - firstHitObjectTime = beatmap.Beatmap.HitObjects.First().StartTime; + this.latestGameplayStartTime = latestGameplayStartTime; + + if (startFromLatestStartTime) + StartTime = latestGameplayStartTime; } protected override void LoadComplete() @@ -89,33 +95,34 @@ namespace osu.Game.Screens.Play settings => settings.Offset, val => userBeatmapOffsetClock.Offset = val); - if (GameplayStartTime == null) - { - // sane default provided by ruleset. - double offset = gameplayStartTime; + // Reset may have been called externally before LoadComplete. + // If it was, and the clock is in a playing state, we want to ensure that it isn't stopped here. + bool isStarted = !IsPaused.Value; - if (!startAtGameplayStart) - { - offset = Math.Min(0, offset); + // If a custom start time was not specified, calculate the best value to use. + double gameplayStartTime = StartTime ?? findBeatmapStartTime(); - // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. - // this is commonly used to display an intro before the audio track start. - double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; - if (firstStoryboardEvent != null) - offset = Math.Min(offset, firstStoryboardEvent.Value); + Reset(startClock: isStarted, gameplayStartTime: gameplayStartTime); + } - // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. - // this is not available as an option in the live editor but can still be applied via .osu editing. - if (beatmap.BeatmapInfo.AudioLeadIn > 0) - offset = Math.Min(offset, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn); - } + private double findBeatmapStartTime() + { + // start with the originally provided latest time as a sane default. + double time = latestGameplayStartTime; - // Reset may have been called externally before LoadComplete. - // If it was and the clock is in a playing state, we want to ensure that it isn't stopped here. - bool isStarted = !IsPaused.Value; + // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. + // this is commonly used to display an intro before the audio track start. + double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; + if (firstStoryboardEvent != null) + time = Math.Min(time, firstStoryboardEvent.Value); - Reset(startClock: isStarted, gameplayStartTime: offset); - } + // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. + // this is not available as an option in the live editor but can still be applied via .osu editing. + double firstHitObjectTime = beatmap.Beatmap.HitObjects.First().StartTime; + if (beatmap.BeatmapInfo.AudioLeadIn > 0) + time = Math.Min(time, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn); + + return time; } protected override void OnIsPausedChanged(ValueChangedEvent isPaused) @@ -158,10 +165,10 @@ namespace osu.Game.Screens.Play /// public void Skip() { - if (GameplayClock.CurrentTime > gameplayStartTime - MINIMUM_SKIP_TIME) + if (GameplayClock.CurrentTime > latestGameplayStartTime - MINIMUM_SKIP_TIME) return; - double skipTarget = gameplayStartTime - MINIMUM_SKIP_TIME; + double skipTarget = latestGameplayStartTime - MINIMUM_SKIP_TIME; if (GameplayClock.CurrentTime < 0 && skipTarget > 6000) // double skip exception for storyboards with very long intros From c6be26eb018a4f26701f0b2bcbe58c742fa4a2e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 01:15:17 +0900 Subject: [PATCH 0056/1276] Rename start time calculation method and add more commenting to explain purpose better --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 58e8b5f1ad..529503e020 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -100,15 +100,18 @@ namespace osu.Game.Screens.Play bool isStarted = !IsPaused.Value; // If a custom start time was not specified, calculate the best value to use. - double gameplayStartTime = StartTime ?? findBeatmapStartTime(); + double gameplayStartTime = StartTime ?? findEarliestStartTime(); Reset(startClock: isStarted, gameplayStartTime: gameplayStartTime); } - private double findBeatmapStartTime() + private double findEarliestStartTime() { - // start with the originally provided latest time as a sane default. - double time = latestGameplayStartTime; + // here we are trying to find the time to start playback from the "zero" point. + // generally this is either zero, or some point earlier than zero in the case of storyboards, lead-ins etc. + + // start with the originally provided latest time (if before zero). + double time = Math.Min(0, latestGameplayStartTime); // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. // this is commonly used to display an intro before the audio track start. From d90f21e140bea785d07fd33c8ef1af126c16a16b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Mar 2022 00:13:40 +0300 Subject: [PATCH 0057/1276] Reword mod documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Rulesets/Mods/IMod.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index cda59bae55..5f4fecb649 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -41,13 +41,13 @@ namespace osu.Game.Rulesets.Mods /// /// Whether this mod is playable in a multiplayer match. - /// Should be false for mods that affect the gameplay progress based on user input (e.g. ). + /// Should be false for mods that make gameplay duration dependent on user input (e.g. ). /// bool PlayableInMultiplayer { get; } /// /// Whether this mod is valid to be a "free mod" in a multiplayer match. - /// Should be false for mods that affect the gameplay progress (e.g. and ). + /// Should be false for mods that affect the gameplay duration (e.g. and ). /// bool ValidFreeModInMultiplayer { get; } From b0d04a78f7d2e4b7fadaee32841dca3387c44f34 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Mar 2022 00:21:16 +0300 Subject: [PATCH 0058/1276] Reword mod utility documentation regarding nullability --- osu.Game/Utils/ModUtils.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index d169ace80a..81b78c18ac 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -109,7 +109,7 @@ namespace osu.Game.Utils /// Check the provided combination of mods are valid for a local gameplay session. /// /// The mods to check. - /// Invalid mods, if any were found. Can be null if all mods were valid. + /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidForGameplay(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) => checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && !(m is MultiMod), out invalidMods); @@ -118,7 +118,7 @@ namespace osu.Game.Utils /// Check the provided combination of mods are valid for a multiplayer match session. /// /// The mods to check. - /// Invalid mods, if any were found. Can be null if all mods were valid. + /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) => checkValid(mods, m => m.PlayableInMultiplayer, out invalidMods); @@ -127,7 +127,7 @@ namespace osu.Game.Utils /// Check the provided combination of mods are valid as "free mods" in a multiplayer match session. /// /// The mods to check. - /// Invalid mods, if any were found. Can be null if all mods were valid. + /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidFreeModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) => checkValid(mods, m => m.ValidFreeModInMultiplayer, out invalidMods); From 51e5dd7d0e58fb681db6da9e34e9d435e1026a58 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Mar 2022 02:08:11 +0300 Subject: [PATCH 0059/1276] Introduce `IsPlayable(...)` and obsolete `UserPlayable` --- .../BeatmapSet/LeaderboardModSelector.cs | 2 +- osu.Game/Rulesets/Mods/IMod.cs | 26 ++++++++++--------- osu.Game/Rulesets/Mods/Mod.cs | 11 +++----- osu.Game/Rulesets/Mods/ModUsage.cs | 26 +++++++++++++++++++ 4 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/ModUsage.cs diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 25aed4c980..97b89c6f74 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet return; modsContainer.Add(new ModButton(new ModNoMod())); - modsContainer.AddRange(rulesetInstance.AllMods.Where(m => m.UserPlayable).Select(m => new ModButton(m))); + modsContainer.AddRange(rulesetInstance.AllMods.Where(m => m.IsPlayable(ModUsage.Solo)).Select(m => new ModButton(m))); modsContainer.ForEach(button => { diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index 5f4fecb649..bdfb273b13 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -33,24 +33,26 @@ namespace osu.Game.Rulesets.Mods /// IconUsage? Icon { get; } + /// + /// Whether this mod is playable for the given usage. + /// + /// + /// + /// Should be always false for cases where the user is not interacting with the game. + /// Should be false in for mods that make gameplay duration dependent on user input (e.g. ). + /// Should be false in for mods that affect the gameplay duration (e.g. and ). + /// + /// + /// The mod usage. + bool IsPlayable(ModUsage usage); + /// /// Whether this mod is playable by an end user. /// Should be false for cases where the user is not interacting with the game (so it can be excluded from multiplayer selection, for example). /// + [Obsolete("Override IsPlayable instead.")] // Can be removed 20220918 bool UserPlayable { get; } - /// - /// Whether this mod is playable in a multiplayer match. - /// Should be false for mods that make gameplay duration dependent on user input (e.g. ). - /// - bool PlayableInMultiplayer { get; } - - /// - /// Whether this mod is valid to be a "free mod" in a multiplayer match. - /// Should be false for mods that affect the gameplay duration (e.g. and ). - /// - bool ValidFreeModInMultiplayer { get; } - /// /// Create a fresh instance based on this mod. /// diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index a6562b4f4c..00aef1a598 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -91,16 +91,13 @@ namespace osu.Game.Rulesets.Mods [JsonIgnore] public virtual bool HasImplementation => this is IApplicableMod; + public virtual bool IsPlayable(ModUsage usage) => true; + [JsonIgnore] + [Obsolete("Override IsPlayable instead.")] // Can be removed 20220918 public virtual bool UserPlayable => true; - [JsonIgnore] - public virtual bool PlayableInMultiplayer => UserPlayable; - - [JsonIgnore] - public virtual bool ValidFreeModInMultiplayer => PlayableInMultiplayer; - - [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override and set UserPlayable to false.")] // Can be removed 20211009 + [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override IsPlayable to false.")] // Can be removed 20211009 public virtual bool Ranked => false; /// diff --git a/osu.Game/Rulesets/Mods/ModUsage.cs b/osu.Game/Rulesets/Mods/ModUsage.cs new file mode 100644 index 0000000000..82ff6bc418 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModUsage.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Mods +{ + /// + /// The usage of this mod to determine its playability. + /// + public enum ModUsage + { + /// + /// In a solo gameplay session. + /// + Solo, + + /// + /// In a multiplayer match, as a required mod. + /// + MultiplayerRequired, + + /// + /// In a multiplayer match, as a "free" mod. + /// + MultiplayerFree, + } +} From f2248ecc08000c1c259e306ad8d599b0a1c48c8c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 18 Mar 2022 02:11:18 +0300 Subject: [PATCH 0060/1276] Update usages to use `IsPlayable` instead --- osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | 2 +- osu.Game/Rulesets/Mods/ModAutoplay.cs | 2 +- osu.Game/Rulesets/Mods/ModRateAdjust.cs | 2 +- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- osu.Game/Rulesets/Mods/UnknownMod.cs | 2 +- osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs | 2 +- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 4 ++-- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 2 +- .../Screens/Play/PlayerSettings/BeatmapOffsetControl.cs | 3 ++- osu.Game/Screens/Play/SubmittingPlayer.cs | 3 ++- osu.Game/Screens/Ranking/ResultsScreen.cs | 3 ++- osu.Game/Utils/ModUtils.cs | 8 ++++---- 12 files changed, 19 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs index 54a4c054c9..17ef9f926e 100644 --- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs +++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 1; - public override bool PlayableInMultiplayer => false; + public override bool IsPlayable(ModUsage usage) => usage == ModUsage.Solo; public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModTimeRamp) }; diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index 60b9c29fe0..5491cbec07 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mods public bool RestartOnFail => false; - public override bool UserPlayable => false; + public override bool IsPlayable(ModUsage usage) => false; public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail) }; diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index 3a11c3034b..810b93c4dd 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModRateAdjust : Mod, IApplicableToRate { - public override bool ValidFreeModInMultiplayer => false; + public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerFree; public abstract BindableNumber SpeedChange { get; } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 59aac62686..fa6a9f3e5b 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] public abstract BindableBool AdjustPitch { get; } - public override bool ValidFreeModInMultiplayer => false; + public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerFree; public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModAdaptiveSpeed) }; diff --git a/osu.Game/Rulesets/Mods/UnknownMod.cs b/osu.Game/Rulesets/Mods/UnknownMod.cs index b426386d7a..75d86e67bc 100644 --- a/osu.Game/Rulesets/Mods/UnknownMod.cs +++ b/osu.Game/Rulesets/Mods/UnknownMod.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mods public override string Description => "This mod could not be resolved by the game."; public override double ScoreMultiplier => 0; - public override bool UserPlayable => false; + public override bool IsPlayable(ModUsage usage) => false; public override ModType Type => ModType.System; diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index d5abaaab4e..974e2b9305 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay public new Func IsValidMod { get => base.IsValidMod; - set => base.IsValidMod = m => m.HasImplementation && m.UserPlayable && value(m); + set => base.IsValidMod = m => m.HasImplementation && m.IsPlayable(ModUsage.Solo) && value(m); } public FreeModSelectOverlay() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index e4f1d9587d..4c1350a56b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -117,8 +117,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && mod.PlayableInMultiplayer; + protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && mod.IsPlayable(ModUsage.MultiplayerRequired); - protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && mod.ValidFreeModInMultiplayer; + protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && mod.IsPlayable(ModUsage.MultiplayerFree); } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 7b64784316..75e5ea60a4 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -170,7 +170,7 @@ namespace osu.Game.Screens.OnlinePlay /// /// The to check. /// Whether is a valid mod for online play. - protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && ModUtils.FlattenMod(mod).All(m => m.UserPlayable); + protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && ModUtils.FlattenMod(mod).All(m => m.IsPlayable(ModUsage.Solo)); /// /// Checks whether a given is valid for per-player free-mod selection. diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 42091c521f..27aa4fe1f1 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Localisation; using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; @@ -187,7 +188,7 @@ namespace osu.Game.Screens.Play.PlayerSettings if (score.NewValue == null) return; - if (score.NewValue.Mods.Any(m => !m.UserPlayable)) + if (score.NewValue.Mods.Any(m => !m.IsPlayable(ModUsage.Solo))) return; var hitEvents = score.NewValue.HitEvents; diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index b1f2bccddf..5de8aa1dc1 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -11,6 +11,7 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.API; using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -47,7 +48,7 @@ namespace osu.Game.Screens.Play // Token request construction should happen post-load to allow derived classes to potentially prepare DI backings that are used to create the request. var tcs = new TaskCompletionSource(); - if (Mods.Value.Any(m => !m.UserPlayable)) + if (Mods.Value.Any(m => !m.IsPlayable(ModUsage.Solo))) { handleTokenFailure(new InvalidOperationException("Non-user playable mod selected.")); return false; diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index cb842ce4a0..09b6cc0f23 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -18,6 +18,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Online.API; +using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking.Statistics; @@ -145,7 +146,7 @@ namespace osu.Game.Screens.Ranking if (Score != null) { // only show flair / animation when arriving after watching a play that isn't autoplay. - bool shouldFlair = player != null && Score.Mods.All(m => m.UserPlayable); + bool shouldFlair = player != null && Score.Mods.All(m => m.IsPlayable(ModUsage.Solo)); ScorePanelList.AddScore(Score, shouldFlair); } diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 81b78c18ac..4915f5bdc6 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -115,13 +115,13 @@ namespace osu.Game.Utils => checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && !(m is MultiMod), out invalidMods); /// - /// Check the provided combination of mods are valid for a multiplayer match session. + /// Check the provided combination of mods are valid as "required mods" in a multiplayer match session. /// /// The mods to check. /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. - public static bool CheckValidForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.PlayableInMultiplayer, out invalidMods); + public static bool CheckValidRequiredModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) + => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerRequired), out invalidMods); /// /// Check the provided combination of mods are valid as "free mods" in a multiplayer match session. @@ -130,7 +130,7 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidFreeModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.ValidFreeModInMultiplayer, out invalidMods); + => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerFree), out invalidMods); private static bool checkValid(IEnumerable mods, Predicate valid, [NotNullWhen(false)] out List? invalidMods) { From b5ff9ed13a0d6e936d949606af3ca44fe5a00df0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 14:27:58 +0900 Subject: [PATCH 0061/1276] Add test coverage of multiplayer being paused when users are still loading --- .../Multiplayer/TestSceneMultiplayer.cs | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index e38da96bd5..2a3dbdaf95 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -494,17 +494,20 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true); - AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + AddAssert("Mods match current item", + () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); AddStep("Switch required mods", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Mods.Value = new Mod[] { new OsuModDoubleTime() }); - AddAssert("Mods don't match current item", () => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + AddAssert("Mods don't match current item", + () => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); AddStep("start match externally", () => multiplayerClient.StartMatch()); AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player); - AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + AddAssert("Mods match current item", + () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); } [Test] @@ -664,6 +667,41 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for results", () => multiplayerComponents.CurrentScreen is ResultsScreen); } + [Test] + public void TestGameplayDoesntStartWithNonLoadedUser() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, + } + } + }); + + pressReadyButton(); + + AddStep("join other user and ready", () => + { + multiplayerClient.AddUser(new APIUser { Id = 1234 }); + multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Ready); + }); + + AddStep("start match", () => + { + multiplayerClient.StartMatch(); + }); + + AddUntilStep("wait for player", () => multiplayerComponents.CurrentScreen is Player); + + AddWaitStep("wait some", 20); + + AddAssert("ensure gameplay hasn't started", () => this.ChildrenOfType().SingleOrDefault()?.IsRunning == false); + } + [Test] public void TestRoomSettingsReQueriedWhenJoiningRoom() { From 59aef88504a8408eb49b61d7362941ef177d7520 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 15:07:57 +0900 Subject: [PATCH 0062/1276] Simplify clock reset/start flow in `MultiSpectatorScreen` --- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 6747b8fc66..523301f4fd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -164,7 +164,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate base.LoadComplete(); masterClockContainer.Reset(); - masterClockContainer.Stop(); syncManager.ReadyToStart += onReadyToStart; syncManager.MasterState.BindValueChanged(onMasterStateChanged, true); @@ -198,8 +197,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate .DefaultIfEmpty(0) .Min(); - masterClockContainer.Seek(startTime); - masterClockContainer.Start(); + masterClockContainer.Reset(true, startTime); // Although the clock has been started, this flag is set to allow for later synchronisation state changes to also be able to start it. canStartMasterClock = true; From f09a9467226f335c95c823bb03b09fc99f13de4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 15:08:25 +0900 Subject: [PATCH 0063/1276] Start `GameplayClockContainer` paused for better state control --- .../Gameplay/TestSceneMasterGameplayClockContainer.cs | 5 +---- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs | 2 -- osu.Game/Screens/Play/GameplayClockContainer.cs | 6 +++--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs index 77b402ad3c..0ed432bbea 100644 --- a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs @@ -85,10 +85,7 @@ namespace osu.Game.Tests.Gameplay Add(gameplayClockContainer = new MasterGameplayClockContainer(working, 0)); - if (whileStopped) - gameplayClockContainer.Stop(); - - gameplayClockContainer.Reset(); + gameplayClockContainer.Reset(startClock: !whileStopped); }); AddStep($"set clock rate to {clockRate}", () => working.Track.AddAdjustment(AdjustableProperty.Frequency, new BindableDouble(clockRate))); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index 615bd41f3f..ececa1e497 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -55,8 +55,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public SpectatorGameplayClockContainer([NotNull] IClock sourceClock) : base(sourceClock) { - // the container should initially be in a stopped state until the catch-up clock is started by the sync manager. - Stop(); } protected override void Update() diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 66f5cb0bd4..226f5709c7 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Play /// /// Whether gameplay is paused. /// - public readonly BindableBool IsPaused = new BindableBool(); + public readonly BindableBool IsPaused = new BindableBool(true); /// /// The adjustable source clock used for gameplay. Should be used for seeks and clock control. @@ -115,7 +115,7 @@ namespace osu.Game.Screens.Play /// /// Resets this and the source to an initial state ready for gameplay. /// - /// Whether to start the clock immediately. + /// Whether to start the clock immediately, if not already started. /// A time to use for future calls as the definite start of gameplay. public void Reset(bool startClock = false, double? gameplayStartTime = null) { @@ -128,7 +128,7 @@ namespace osu.Game.Screens.Play // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); - if (!IsPaused.Value && startClock) + if (!IsPaused.Value || startClock) Start(); } From 0988c2b0fabd104792f7ebd7a4b3be49b1a26fb9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 15:28:48 +0900 Subject: [PATCH 0064/1276] Move `DrawableRuleset` binding to `LoadComplete` to avoid exceptions on `InputManager` access --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 29559f5036..be1105e7ff 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -133,6 +133,11 @@ namespace osu.Game.Rulesets.UI p.NewResult += (_, r) => NewResult?.Invoke(r); p.RevertResult += (_, r) => RevertResult?.Invoke(r); })); + } + + protected override void LoadComplete() + { + base.LoadComplete(); IsPaused.ValueChanged += paused => { From 611562c6502aaedeeba6f1c8ff883d51fb539518 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 16:21:14 +0900 Subject: [PATCH 0065/1276] Add more comments around catch up logic --- .../OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs | 5 ++++- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs index 1a5231e602..de23b4fef7 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs @@ -17,8 +17,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate Bindable WaitingOnFrames { get; } /// - /// Whether this clock is resynchronising to the master clock. + /// Whether this clock is behind the master clock and running at a higher rate to catch up to it. /// + /// + /// Of note, this will be false if this clock is *ahead* of the master clock. + /// bool IsCatchingUp { get; set; } /// diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index ececa1e497..29afaf00d8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -59,6 +59,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate protected override void Update() { + // The SourceClock here is always a CatchUpSpectatorPlayerClock. // The player clock's running state is controlled externally, but the local pausing state needs to be updated to stop gameplay. if (SourceClock.IsRunning) Start(); From 4bc7c69bf70e7aa1bf91deec0e587523da0ea368 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 16:27:14 +0900 Subject: [PATCH 0066/1276] Fix test regression due to missing argument --- .../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index ed8b0bdd96..653a35417e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -473,7 +473,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } protected override MasterGameplayClockContainer CreateMasterGameplayClockContainer(WorkingBeatmap beatmap) - => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0); + => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0, true); } } } From 7cb7b03cee678db55530d8134d39295089a6a216 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 18:16:45 +0900 Subject: [PATCH 0067/1276] Fix `TestSceneMasterGameplayClockContainer` not clearing previous test clocks --- .../Gameplay/TestSceneMasterGameplayClockContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs index 0ed432bbea..b2dd6cbd97 100644 --- a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Gameplay var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); working.LoadTrack(); - Add(gameplayClockContainer = new MasterGameplayClockContainer(working, 0)); + Child = gameplayClockContainer = new MasterGameplayClockContainer(working, 0); }); AddStep("start clock", () => gameplayClockContainer.Start()); @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Gameplay var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); working.LoadTrack(); - Add(gameplayClockContainer = new MasterGameplayClockContainer(working, 0)); + Child = gameplayClockContainer = new MasterGameplayClockContainer(working, 0); }); AddStep("start clock", () => gameplayClockContainer.Start()); @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Gameplay working = new ClockBackedTestWorkingBeatmap(new OsuRuleset().RulesetInfo, new FramedClock(new ManualClock()), Audio); working.LoadTrack(); - Add(gameplayClockContainer = new MasterGameplayClockContainer(working, 0)); + Child = gameplayClockContainer = new MasterGameplayClockContainer(working, 0); gameplayClockContainer.Reset(startClock: !whileStopped); }); From 70e943fbccb1d1e2746aeb605f63b208cf8b6a76 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 20 Mar 2022 06:36:51 +0300 Subject: [PATCH 0068/1276] `ModUsage.Solo` -> `ModUsage.User` --- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 2 +- osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | 2 +- osu.Game/Rulesets/Mods/ModUsage.cs | 2 +- osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs | 2 +- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 2 +- osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs | 2 +- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 97b89c6f74..caeb757d7a 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet return; modsContainer.Add(new ModButton(new ModNoMod())); - modsContainer.AddRange(rulesetInstance.AllMods.Where(m => m.IsPlayable(ModUsage.Solo)).Select(m => new ModButton(m))); + modsContainer.AddRange(rulesetInstance.AllMods.Where(m => m.IsPlayable(ModUsage.User)).Select(m => new ModButton(m))); modsContainer.ForEach(button => { diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs index 17ef9f926e..801cd3cba7 100644 --- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs +++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 1; - public override bool IsPlayable(ModUsage usage) => usage == ModUsage.Solo; + public override bool IsPlayable(ModUsage usage) => usage == ModUsage.User; public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModTimeRamp) }; diff --git a/osu.Game/Rulesets/Mods/ModUsage.cs b/osu.Game/Rulesets/Mods/ModUsage.cs index 82ff6bc418..714a99056b 100644 --- a/osu.Game/Rulesets/Mods/ModUsage.cs +++ b/osu.Game/Rulesets/Mods/ModUsage.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods /// /// In a solo gameplay session. /// - Solo, + User, /// /// In a multiplayer match, as a required mod. diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 974e2b9305..37cffd8343 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay public new Func IsValidMod { get => base.IsValidMod; - set => base.IsValidMod = m => m.HasImplementation && m.IsPlayable(ModUsage.Solo) && value(m); + set => base.IsValidMod = m => m.HasImplementation && m.IsPlayable(ModUsage.User) && value(m); } public FreeModSelectOverlay() diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 75e5ea60a4..d4f89b5206 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -170,7 +170,7 @@ namespace osu.Game.Screens.OnlinePlay /// /// The to check. /// Whether is a valid mod for online play. - protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && ModUtils.FlattenMod(mod).All(m => m.IsPlayable(ModUsage.Solo)); + protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && ModUtils.FlattenMod(mod).All(m => m.IsPlayable(ModUsage.User)); /// /// Checks whether a given is valid for per-player free-mod selection. diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 27aa4fe1f1..bab391337a 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -188,7 +188,7 @@ namespace osu.Game.Screens.Play.PlayerSettings if (score.NewValue == null) return; - if (score.NewValue.Mods.Any(m => !m.IsPlayable(ModUsage.Solo))) + if (score.NewValue.Mods.Any(m => !m.IsPlayable(ModUsage.User))) return; var hitEvents = score.NewValue.HitEvents; diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 5de8aa1dc1..205ee9ab9c 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Play // Token request construction should happen post-load to allow derived classes to potentially prepare DI backings that are used to create the request. var tcs = new TaskCompletionSource(); - if (Mods.Value.Any(m => !m.IsPlayable(ModUsage.Solo))) + if (Mods.Value.Any(m => !m.IsPlayable(ModUsage.User))) { handleTokenFailure(new InvalidOperationException("Non-user playable mod selected.")); return false; diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 09b6cc0f23..ac3816c4ec 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -146,7 +146,7 @@ namespace osu.Game.Screens.Ranking if (Score != null) { // only show flair / animation when arriving after watching a play that isn't autoplay. - bool shouldFlair = player != null && Score.Mods.All(m => m.IsPlayable(ModUsage.Solo)); + bool shouldFlair = player != null && Score.Mods.All(m => m.IsPlayable(ModUsage.User)); ScorePanelList.AddScore(Score, shouldFlair); } From 820a672940b151a43b54f510dc03b8476bd95ce9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 20 Mar 2022 06:37:08 +0300 Subject: [PATCH 0069/1276] Reword xmldoc to make more sense --- osu.Game/Rulesets/Mods/ModUsage.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModUsage.cs b/osu.Game/Rulesets/Mods/ModUsage.cs index 714a99056b..0892f528d5 100644 --- a/osu.Game/Rulesets/Mods/ModUsage.cs +++ b/osu.Game/Rulesets/Mods/ModUsage.cs @@ -4,22 +4,22 @@ namespace osu.Game.Rulesets.Mods { /// - /// The usage of this mod to determine its playability. + /// The usage of this mod to determine whether it's playable in such context. /// public enum ModUsage { /// - /// In a solo gameplay session. + /// Used for a per-user gameplay session. Determines whether the mod is playable by an end user. /// User, /// - /// In a multiplayer match, as a required mod. + /// Used as a "required mod" for a multiplayer match. /// MultiplayerRequired, /// - /// In a multiplayer match, as a "free" mod. + /// Used as a "free mod" for a multiplayer match. /// MultiplayerFree, } From add9f3ec9177815f7dab386927c47ad436d9576f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 20 Mar 2022 13:12:24 +0300 Subject: [PATCH 0070/1276] Rename multiplayer mod usages to make more sense --- osu.Game/Rulesets/Mods/IMod.cs | 4 ++-- osu.Game/Rulesets/Mods/ModRateAdjust.cs | 2 +- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- osu.Game/Rulesets/Mods/ModUsage.cs | 12 +++++++----- .../Multiplayer/MultiplayerMatchSongSelect.cs | 4 ++-- osu.Game/Utils/ModUtils.cs | 4 ++-- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index bdfb273b13..325b75b76e 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -39,8 +39,8 @@ namespace osu.Game.Rulesets.Mods /// /// /// Should be always false for cases where the user is not interacting with the game. - /// Should be false in for mods that make gameplay duration dependent on user input (e.g. ). - /// Should be false in for mods that affect the gameplay duration (e.g. and ). + /// Should be false in for mods that make gameplay duration dependent on user input (e.g. ). + /// Should be false in for mods that affect the gameplay duration (e.g. and ). /// /// /// The mod usage. diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index 810b93c4dd..ab724673b6 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModRateAdjust : Mod, IApplicableToRate { - public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerFree; + public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerPerPlayer; public abstract BindableNumber SpeedChange { get; } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index fa6a9f3e5b..96b38301b5 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] public abstract BindableBool AdjustPitch { get; } - public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerFree; + public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerPerPlayer; public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModAdaptiveSpeed) }; diff --git a/osu.Game/Rulesets/Mods/ModUsage.cs b/osu.Game/Rulesets/Mods/ModUsage.cs index 0892f528d5..59a62bfc6f 100644 --- a/osu.Game/Rulesets/Mods/ModUsage.cs +++ b/osu.Game/Rulesets/Mods/ModUsage.cs @@ -9,18 +9,20 @@ namespace osu.Game.Rulesets.Mods public enum ModUsage { /// - /// Used for a per-user gameplay session. Determines whether the mod is playable by an end user. + /// Used for a per-user gameplay session. + /// Determines whether the mod is playable by an end user. /// User, /// - /// Used as a "required mod" for a multiplayer match. + /// Used in multiplayer but must be applied to all users. + /// This is generally the case for mods which affect the length of gameplay. /// - MultiplayerRequired, + MultiplayerRoomWide, /// - /// Used as a "free mod" for a multiplayer match. + /// Used in multiplayer either at a room or per-player level (i.e. "free mod"). /// - MultiplayerFree, + MultiplayerPerPlayer, } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 4c1350a56b..63467dbb9d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -117,8 +117,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && mod.IsPlayable(ModUsage.MultiplayerRequired); + protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && mod.IsPlayable(ModUsage.MultiplayerRoomWide); - protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && mod.IsPlayable(ModUsage.MultiplayerFree); + protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && mod.IsPlayable(ModUsage.MultiplayerPerPlayer); } } diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 4915f5bdc6..946386a7ce 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -121,7 +121,7 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidRequiredModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerRequired), out invalidMods); + => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerRoomWide), out invalidMods); /// /// Check the provided combination of mods are valid as "free mods" in a multiplayer match session. @@ -130,7 +130,7 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidFreeModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerFree), out invalidMods); + => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerPerPlayer), out invalidMods); private static bool checkValid(IEnumerable mods, Predicate valid, [NotNullWhen(false)] out List? invalidMods) { From 5f878ed82b98a1d3ec14af96879c4d68bee8d167 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 20 Mar 2022 16:07:08 +0300 Subject: [PATCH 0071/1276] Delegate `IsPlayable` to the obsoleted `UserPlayable` by default Handles consumers who still haven't updated to use `IsPlayable` yet. --- osu.Game/Rulesets/Mods/Mod.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 00aef1a598..debb1a1e48 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -91,7 +91,9 @@ namespace osu.Game.Rulesets.Mods [JsonIgnore] public virtual bool HasImplementation => this is IApplicableMod; - public virtual bool IsPlayable(ModUsage usage) => true; +#pragma warning disable 618 + public virtual bool IsPlayable(ModUsage usage) => UserPlayable; +#pragma warning restore 618 [JsonIgnore] [Obsolete("Override IsPlayable instead.")] // Can be removed 20220918 From 145fca2704457fe58baaf294b960812ce41dd49d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 20 Mar 2022 16:17:19 +0300 Subject: [PATCH 0072/1276] Fix failing test scenes --- osu.Game.Tests/Mods/ModUtilsTest.cs | 12 ------------ osu.Game/Utils/ModUtils.cs | 6 +++--- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index 4c126f0a3b..e858abfe44 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -134,18 +134,6 @@ namespace osu.Game.Tests.Mods private static readonly object[] invalid_mod_test_scenarios = { - // incompatible pair. - new object[] - { - new Mod[] { new OsuModDoubleTime(), new OsuModHalfTime() }, - new[] { typeof(OsuModDoubleTime), typeof(OsuModHalfTime) } - }, - // incompatible pair with derived class. - new object[] - { - new Mod[] { new OsuModNightcore(), new OsuModHalfTime() }, - new[] { typeof(OsuModNightcore), typeof(OsuModHalfTime) } - }, // system mod. new object[] { diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 946386a7ce..0d46494319 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -106,7 +106,7 @@ namespace osu.Game.Utils } /// - /// Check the provided combination of mods are valid for a local gameplay session. + /// Checks that all s in a combination are valid for a local gameplay session. /// /// The mods to check. /// Invalid mods, if any were found. Will be null if all mods were valid. @@ -115,7 +115,7 @@ namespace osu.Game.Utils => checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && !(m is MultiMod), out invalidMods); /// - /// Check the provided combination of mods are valid as "required mods" in a multiplayer match session. + /// Checks that all s in a combination are valid as "required mods" in a multiplayer match session. /// /// The mods to check. /// Invalid mods, if any were found. Will be null if all mods were valid. @@ -124,7 +124,7 @@ namespace osu.Game.Utils => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerRoomWide), out invalidMods); /// - /// Check the provided combination of mods are valid as "free mods" in a multiplayer match session. + /// Checks that all s in a combination are valid as "free mods" in a multiplayer match session. /// /// The mods to check. /// Invalid mods, if any were found. Will be null if all mods were valid. From b218046fa28ef9ebb257663adf5388d9174345ee Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 23 Mar 2022 15:38:48 +0300 Subject: [PATCH 0073/1276] Remove redundant line from mod usage --- osu.Game/Rulesets/Mods/ModUsage.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModUsage.cs b/osu.Game/Rulesets/Mods/ModUsage.cs index 59a62bfc6f..746387a062 100644 --- a/osu.Game/Rulesets/Mods/ModUsage.cs +++ b/osu.Game/Rulesets/Mods/ModUsage.cs @@ -10,7 +10,6 @@ namespace osu.Game.Rulesets.Mods { /// /// Used for a per-user gameplay session. - /// Determines whether the mod is playable by an end user. /// User, From 6cd67928aba27e2936fd8f0a6cfc0ae7838e1e67 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 23 Mar 2022 15:48:50 +0300 Subject: [PATCH 0074/1276] Simplify documentation of `ModUsage` --- osu.Game/Rulesets/Mods/ModUsage.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModUsage.cs b/osu.Game/Rulesets/Mods/ModUsage.cs index 746387a062..04d99149dd 100644 --- a/osu.Game/Rulesets/Mods/ModUsage.cs +++ b/osu.Game/Rulesets/Mods/ModUsage.cs @@ -4,23 +4,23 @@ namespace osu.Game.Rulesets.Mods { /// - /// The usage of this mod to determine whether it's playable in such context. + /// The context in which a is playable. /// public enum ModUsage { /// - /// Used for a per-user gameplay session. + /// This mod can be used for a per-user gameplay session. /// User, /// - /// Used in multiplayer but must be applied to all users. + /// This mod can be used in multiplayer but must be applied to all users. /// This is generally the case for mods which affect the length of gameplay. /// MultiplayerRoomWide, /// - /// Used in multiplayer either at a room or per-player level (i.e. "free mod"). + /// This mod can be used in multiplayer either at a room or per-player level (i.e. "free mod"). /// MultiplayerPerPlayer, } From 8ca9cbc866e089338b09585890c97fc5dfce8da4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 14:43:33 +0900 Subject: [PATCH 0075/1276] Set a more correct initial value for `pauseFreqAdjust` As the `GameplayClock` now starts paused, this value needs to match to ensure things work correctly. For a better explanation of how we got here, see discussion at https://github.com/ppy/osu/pull/17302#discussion_r830017735. --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 529503e020..9bcbbf789b 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Play private double totalAppliedOffset => userBeatmapOffsetClock.RateAdjustedOffset + userGlobalOffsetClock.RateAdjustedOffset + platformOffsetClock.RateAdjustedOffset; - private readonly BindableDouble pauseFreqAdjust = new BindableDouble(1); + private readonly BindableDouble pauseFreqAdjust = new BindableDouble(); // Important that this starts at zero, matching the paused state of the clock. private readonly WorkingBeatmap beatmap; From 16dc2f6ef50bccada9e9cba2f3e934324204140c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 14:51:21 +0900 Subject: [PATCH 0076/1276] Adjust `TestSceneFailAnimation` to account for initial frequency change --- osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs index 744227c55e..83d7d769df 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs @@ -56,10 +56,11 @@ namespace osu.Game.Tests.Visual.Gameplay private double lastFrequency = double.MaxValue; - protected override void Update() + protected override void UpdateAfterChildren() { - base.Update(); + base.UpdateAfterChildren(); + // This must be done in UpdateAfterChildren to allow the gameplay clock to have updated before checking values. double freq = Beatmap.Value.Track.AggregateFrequency.Value; FrequencyIncreased |= freq > lastFrequency; From 1049e349e39a8944c223b71ad8d0a621a3965781 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 14:55:01 +0900 Subject: [PATCH 0077/1276] Add test coverage of user offset being set before construction of `MasterGameplayClockContainer` --- .../Gameplay/TestSceneMasterGameplayClockContainer.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs index b2dd6cbd97..75be7b0362 100644 --- a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs @@ -73,11 +73,15 @@ namespace osu.Game.Tests.Gameplay public void TestSeekPerformsInGameplayTime( [Values(1.0, 0.5, 2.0)] double clockRate, [Values(0.0, 200.0, -200.0)] double userOffset, - [Values(false, true)] bool whileStopped) + [Values(false, true)] bool whileStopped, + [Values(false, true)] bool setAudioOffsetBeforeConstruction) { ClockBackedTestWorkingBeatmap working = null; GameplayClockContainer gameplayClockContainer = null; + if (setAudioOffsetBeforeConstruction) + AddStep($"preset audio offset to {userOffset}", () => localConfig.SetValue(OsuSetting.AudioOffset, userOffset)); + AddStep("create container", () => { working = new ClockBackedTestWorkingBeatmap(new OsuRuleset().RulesetInfo, new FramedClock(new ManualClock()), Audio); @@ -89,7 +93,9 @@ namespace osu.Game.Tests.Gameplay }); AddStep($"set clock rate to {clockRate}", () => working.Track.AddAdjustment(AdjustableProperty.Frequency, new BindableDouble(clockRate))); - AddStep($"set audio offset to {userOffset}", () => localConfig.SetValue(OsuSetting.AudioOffset, userOffset)); + + if (!setAudioOffsetBeforeConstruction) + AddStep($"set audio offset to {userOffset}", () => localConfig.SetValue(OsuSetting.AudioOffset, userOffset)); AddStep("seek to 2500", () => gameplayClockContainer.Seek(2500)); AddAssert("gameplay clock time = 2500", () => Precision.AlmostEquals(gameplayClockContainer.CurrentTime, 2500, 10f)); From de1fbda648a1ce13522b5f0a8faa602161df5e58 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 27 Mar 2022 20:54:56 +0300 Subject: [PATCH 0078/1276] Clarify that searching includes both issues and Q&A discussions --- .github/ISSUE_TEMPLATE/bug-issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index 5b19c3732c..d77b28316a 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -9,7 +9,7 @@ body: Important to note that your issue may have already been reported before. Please check: - Pinned issues, at the top of https://github.com/ppy/osu/issues. - Current open `priority:0` issues, filterable [here](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Apriority%3A0). - - And most importantly, search for your issue. If you find that it already exists, respond with a reaction or add any further information that may be helpful. + - And most importantly, search across the [issue listing](https://github.com/ppy/osu/issues) and [Q&A discussion listing](https://github.com/ppy/osu/discussions/categories/q-a) for your issue. If you find that it already exists, respond with a reaction or add any further information that may be helpful. - type: dropdown attributes: From f847f9a31592d98e5070eaad37ef8e48cf313ae9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 27 Mar 2022 20:57:00 +0300 Subject: [PATCH 0079/1276] Exclude "open osu! folder" logs procedure from mobile platforms --- .github/ISSUE_TEMPLATE/bug-issue.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index d77b28316a..8f54b5d1c8 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -48,19 +48,27 @@ body: Attaching log files is required for every reported bug. See instructions below on how to find them. + **Logs are reset when you reopen the game.** If the game crashed or has been closed since you found the bug, retrieve the logs using the file explorer instead. + + ### Desktop platforms + If the game has not yet been closed since you found the bug: 1. Head on to game settings and click on "Open osu! folder" 2. Then open the `logs` folder located there - **Logs are reset when you reopen the game.** If the game crashed or has been closed since you found the bug, retrieve the logs using the file explorer instead. - - The default places to find the logs are as follows: + The default places to find the logs on desktop platforms are as follows: - `%AppData%/osu/logs` *on Windows* - `~/.local/share/osu/logs` *on Linux & macOS* + + If you have selected a custom location for the game files, you can find the `logs` folder there. + + ### Mobile platforms + + The places to find the logs on mobile platforms are as follows: - `Android/data/sh.ppy.osulazer/files/logs` *on Android* - *On iOS*, they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer) - If you have selected a custom location for the game files, you can find the `logs` folder there. + --- After locating the `logs` folder, select all log files inside and drag them into the "Logs" box below. From ebf520921520d486fbf99cd8c07b0e611b81fc0b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 27 Mar 2022 21:38:55 +0300 Subject: [PATCH 0080/1276] Reword issue searching note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .github/ISSUE_TEMPLATE/bug-issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index 8f54b5d1c8..ea5ee298fb 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -9,7 +9,7 @@ body: Important to note that your issue may have already been reported before. Please check: - Pinned issues, at the top of https://github.com/ppy/osu/issues. - Current open `priority:0` issues, filterable [here](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Apriority%3A0). - - And most importantly, search across the [issue listing](https://github.com/ppy/osu/issues) and [Q&A discussion listing](https://github.com/ppy/osu/discussions/categories/q-a) for your issue. If you find that it already exists, respond with a reaction or add any further information that may be helpful. + - And most importantly, search for your issue both in the [issue listing](https://github.com/ppy/osu/issues) and the [Q&A discussion listing](https://github.com/ppy/osu/discussions/categories/q-a). If you find that it already exists, respond with a reaction or add any further information that may be helpful. - type: dropdown attributes: From f049d7cb677e899f9984611ae19410b01a18c978 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 29 Mar 2022 21:36:08 +0100 Subject: [PATCH 0081/1276] Implement `ChatTextBox` for new chat design Reference design: https://www.figma.com/file/f8b2dHp9LJCMOqYP4mdrPZ/Client%2FChat?node-id=1%3A297 Adds new component `ChatTextBox`. Exposes `BindableBool` `ShowSearch` to change text input behaviour between normal and search behaviour. Adds new component `ChatTextBar`. Exposes `BindableBool` `ShowSearch` which toggles between showing current chat channel or search icon. Additionally binds to child `ChatTextBox` components. Requires a cached `Bindable` instance to be managed by a parent component. --- InspectCode.sh | 2 +- .../Visual/Online/TestSceneChatTextBox.cs | 96 ++++++++++ osu.Game/Overlays/Chat/ChatTextBar.cs | 174 ++++++++++++++++++ osu.Game/Overlays/Chat/ChatTextBox.cs | 36 ++++ 4 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs create mode 100644 osu.Game/Overlays/Chat/ChatTextBar.cs create mode 100644 osu.Game/Overlays/Chat/ChatTextBox.cs diff --git a/InspectCode.sh b/InspectCode.sh index cf2bc18175..5a72324dd4 100755 --- a/InspectCode.sh +++ b/InspectCode.sh @@ -2,5 +2,5 @@ dotnet tool restore dotnet CodeFileSanity -dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN +dotnet jb inspectcode "osu.Game/osu.Game.csproj" --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs new file mode 100644 index 0000000000..e72a1d6652 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -0,0 +1,96 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; +using osu.Game.Overlays; +using osu.Game.Overlays.Chat; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneChatTextBox : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + + [Cached] + private readonly Bindable currentChannel = new Bindable(); + + private OsuSpriteText commitText; + private ChatTextBar bar; + + [SetUp] + public void SetUp() + { + Schedule(() => + { + Child = new GridContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 30), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + commitText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = OsuFont.Default.With(size: 20), + }, + }, + new Drawable[] + { + bar = new ChatTextBar + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 0.99f, + }, + }, + }, + }; + + bar.TextBox.OnCommit += (sender, newText) => + { + commitText.Text = $"Commit: {sender.Text}"; + commitText.FadeOutFromOne(1000, Easing.InQuint); + sender.Text = string.Empty; + }; + }); + } + + [Test] + public void TestVisual() + { + AddStep("Public Channel", () => currentChannel.Value = createPublicChannel("#osu")); + AddStep("Public Channel Long Name", () => currentChannel.Value = createPublicChannel("#public-channel-long-name")); + AddStep("Private Channel", () => currentChannel.Value = createPrivateChannel("peppy", 2)); + AddStep("Private Long Name", () => currentChannel.Value = createPrivateChannel("test user long name", 3)); + + AddStep("Chat Mode Channel", () => bar.ShowSearch.Value = false); + AddStep("Chat Mode Search", () => bar.ShowSearch.Value = true); + } + + private static Channel createPublicChannel(string name) + => new Channel { Name = name, Type = ChannelType.Public, Id = 1234 }; + + private static Channel createPrivateChannel(string username, int id) + => new Channel(new APIUser { Id = id, Username = username }); + } +} diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs new file mode 100644 index 0000000000..00284fdd33 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -0,0 +1,174 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using osuTK; + +namespace osu.Game.Overlays.Chat +{ + public class ChatTextBar : Container + { + public readonly BindableBool ShowSearch = new BindableBool(); + + public ChatTextBox TextBox => chatTextBox; + + [Resolved] + private Bindable currentChannel { get; set; } = null!; + + private OsuTextFlowContainer chattingTextContainer = null!; + private Container searchIconContainer = null!; + private ChatTextBox chatTextBox = null!; + private Container enterContainer = null!; + + private const float chatting_text_width = 180; + private const float search_icon_width = 40; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + RelativeSizeAxes = Axes.X; + Height = 60; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background5, + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + chattingTextContainer = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 20)) + { + Masking = true, + Width = chatting_text_width, + Padding = new MarginPadding { Left = 10 }, + RelativeSizeAxes = Axes.Y, + TextAnchor = Anchor.CentreRight, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = colourProvider.Background1, + }, + searchIconContainer = new Container + { + RelativeSizeAxes = Axes.Y, + Width = search_icon_width, + Child = new SpriteIcon + { + Icon = FontAwesome.Solid.Search, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Size = new Vector2(20), + Margin = new MarginPadding { Right = 2 }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 5 }, + Child = chatTextBox = new ChatTextBox + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + ShowSearch = { BindTarget = ShowSearch }, + HoldFocus = true, + ReleaseFocusOnCommit = false, + }, + }, + enterContainer = new Container + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Masking = true, + BorderColour = colourProvider.Background1, + BorderThickness = 2, + CornerRadius = 10, + Margin = new MarginPadding { Right = 10 }, + Size = new Vector2(60, 30), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Transparent, + }, + new OsuSpriteText + { + Text = "Enter", + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Colour = colourProvider.Background1, + Font = OsuFont.Torus.With(size: 20), + Margin = new MarginPadding { Bottom = 2 }, + }, + }, + }, + }, + }, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + ShowSearch.BindValueChanged(change => + { + if (change.NewValue) + { + chattingTextContainer.Hide(); + enterContainer.Hide(); + searchIconContainer.Show(); + } + else + { + chattingTextContainer.Show(); + enterContainer.Show(); + searchIconContainer.Hide(); + } + }, true); + + currentChannel.BindValueChanged(change => + { + Channel newChannel = change.NewValue; + switch (newChannel?.Type) + { + case ChannelType.Public: + chattingTextContainer.Text = $"chatting in {newChannel.Name}"; + break; + case ChannelType.PM: + chattingTextContainer.Text = $"chatting with {newChannel.Name}"; + break; + default: + chattingTextContainer.Text = ""; + break; + } + }, true); + } + } +} diff --git a/osu.Game/Overlays/Chat/ChatTextBox.cs b/osu.Game/Overlays/Chat/ChatTextBox.cs new file mode 100644 index 0000000000..35ed26cda3 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChatTextBox.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Bindables; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Chat +{ + public class ChatTextBox : FocusedTextBox + { + public readonly BindableBool ShowSearch = new BindableBool(); + + public override bool HandleLeftRightArrows => !ShowSearch.Value; + + protected override void LoadComplete() + { + base.LoadComplete(); + + ShowSearch.BindValueChanged(change => + { + PlaceholderText = change.NewValue ? "type here to search" : "type here"; + Schedule(() => Text = string.Empty); + }, true); + } + + protected override void Commit() + { + if (ShowSearch.Value) + return; + + base.Commit(); + } + } +} From 06c32aa1362c5f31d0ca068f9b2cf799d00997ad Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 29 Mar 2022 22:50:24 +0100 Subject: [PATCH 0082/1276] Remove changes to `InspectCode.sh` --- InspectCode.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InspectCode.sh b/InspectCode.sh index 5a72324dd4..cf2bc18175 100755 --- a/InspectCode.sh +++ b/InspectCode.sh @@ -2,5 +2,5 @@ dotnet tool restore dotnet CodeFileSanity -dotnet jb inspectcode "osu.Game/osu.Game.csproj" --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN +dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors From e7d2d94eeef6b11cc13660855b8267247ffc202c Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 30 Mar 2022 02:16:50 +0100 Subject: [PATCH 0083/1276] Fix code quality issues in `ChatTextBar` --- osu.Game/Overlays/Chat/ChatTextBar.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 00284fdd33..66f9f281c9 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -21,14 +21,13 @@ namespace osu.Game.Overlays.Chat { public readonly BindableBool ShowSearch = new BindableBool(); - public ChatTextBox TextBox => chatTextBox; + public ChatTextBox TextBox { get; private set; } = null!; [Resolved] private Bindable currentChannel { get; set; } = null!; private OsuTextFlowContainer chattingTextContainer = null!; private Container searchIconContainer = null!; - private ChatTextBox chatTextBox = null!; private Container enterContainer = null!; private const float chatting_text_width = 180; @@ -89,7 +88,7 @@ namespace osu.Game.Overlays.Chat { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 5 }, - Child = chatTextBox = new ChatTextBox + Child = TextBox = new ChatTextBox { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -156,16 +155,19 @@ namespace osu.Game.Overlays.Chat currentChannel.BindValueChanged(change => { Channel newChannel = change.NewValue; + switch (newChannel?.Type) { case ChannelType.Public: chattingTextContainer.Text = $"chatting in {newChannel.Name}"; break; + case ChannelType.PM: chattingTextContainer.Text = $"chatting with {newChannel.Name}"; break; + default: - chattingTextContainer.Text = ""; + chattingTextContainer.Text = string.Empty; break; } }, true); From eec3fef7a66382337c2c2ee99c7f28ebb951407e Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 30 Mar 2022 20:25:23 +0100 Subject: [PATCH 0084/1276] Remove the enter box in `ChatTextBar` --- osu.Game/Overlays/Chat/ChatTextBar.cs | 32 --------------------------- 1 file changed, 32 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 66f9f281c9..7ff1b8d1d3 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -28,7 +28,6 @@ namespace osu.Game.Overlays.Chat private OsuTextFlowContainer chattingTextContainer = null!; private Container searchIconContainer = null!; - private Container enterContainer = null!; private const float chatting_text_width = 180; private const float search_icon_width = 40; @@ -54,7 +53,6 @@ namespace osu.Game.Overlays.Chat new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize), new Dimension(), - new Dimension(GridSizeMode.AutoSize), }, Content = new[] { @@ -98,34 +96,6 @@ namespace osu.Game.Overlays.Chat ReleaseFocusOnCommit = false, }, }, - enterContainer = new Container - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Masking = true, - BorderColour = colourProvider.Background1, - BorderThickness = 2, - CornerRadius = 10, - Margin = new MarginPadding { Right = 10 }, - Size = new Vector2(60, 30), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Colour4.Transparent, - }, - new OsuSpriteText - { - Text = "Enter", - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Colour = colourProvider.Background1, - Font = OsuFont.Torus.With(size: 20), - Margin = new MarginPadding { Bottom = 2 }, - }, - }, - }, }, }, }, @@ -141,13 +111,11 @@ namespace osu.Game.Overlays.Chat if (change.NewValue) { chattingTextContainer.Hide(); - enterContainer.Hide(); searchIconContainer.Show(); } else { chattingTextContainer.Show(); - enterContainer.Show(); searchIconContainer.Hide(); } }, true); From fff30e8a6ea20b9189f87547e86be90cd01ddfa7 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 30 Mar 2022 21:01:28 +0100 Subject: [PATCH 0085/1276] Simplify show/hide of text and search in `ChatTextBar` --- osu.Game/Overlays/Chat/ChatTextBar.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 7ff1b8d1d3..d7edbb83b6 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -108,16 +108,10 @@ namespace osu.Game.Overlays.Chat ShowSearch.BindValueChanged(change => { - if (change.NewValue) - { - chattingTextContainer.Hide(); - searchIconContainer.Show(); - } - else - { - chattingTextContainer.Show(); - searchIconContainer.Hide(); - } + bool showSearch = change.NewValue; + + chattingTextContainer.FadeTo(showSearch ? 0 : 1); + searchIconContainer.FadeTo(showSearch ? 1 : 0); }, true); currentChannel.BindValueChanged(change => From 3ac0da2da305feac1ea30999e257027ffe37e51f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 26 Mar 2022 21:33:52 +0100 Subject: [PATCH 0086/1276] Implement sheared toggle button --- .../TestSceneShearedToggleButton.cs | 88 ++++++++++ .../UserInterface/ShearedToggleButton.cs | 155 ++++++++++++++++++ 2 files changed, 243 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs create mode 100644 osu.Game/Graphics/UserInterface/ShearedToggleButton.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs new file mode 100644 index 0000000000..a969858157 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs @@ -0,0 +1,88 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.UserInterface +{ + [TestFixture] + public class TestSceneShearedToggleButton : OsuManualInputManagerTestScene + { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + + [Test] + public void TestShearedToggleButton() + { + ShearedToggleButton button = null; + + AddStep("create button", () => + { + Child = button = new ShearedToggleButton(0.2f) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Toggle me", + Width = 200 + }; + }); + + AddToggleStep("toggle button", active => button.Active.Value = active); + AddToggleStep("toggle disabled", disabled => button.Active.Disabled = disabled); + } + + [Test] + public void TestDisabledState() + { + ShearedToggleButton button = null; + + AddStep("create button", () => + { + Child = button = new ShearedToggleButton(0.2f) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Toggle me", + Width = 200 + }; + }); + + clickToggle(); + assertToggleState(true); + + clickToggle(); + assertToggleState(false); + + setToggleDisabledState(true); + + assertToggleState(false); + clickToggle(); + assertToggleState(false); + + setToggleDisabledState(false); + assertToggleState(false); + clickToggle(); + assertToggleState(true); + + setToggleDisabledState(true); + assertToggleState(true); + clickToggle(); + assertToggleState(true); + + void clickToggle() => AddStep("click toggle", () => + { + InputManager.MoveMouseTo(button); + InputManager.Click(MouseButton.Left); + }); + + void assertToggleState(bool active) => AddAssert($"toggle is {(active ? "" : "not ")}active", () => button.Active.Value == active); + + void setToggleDisabledState(bool disabled) => AddStep($"{(disabled ? "disable" : "enable")} toggle", () => button.Active.Disabled = disabled); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs b/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs new file mode 100644 index 0000000000..acbed29279 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs @@ -0,0 +1,155 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Graphics.UserInterface +{ + public class ShearedToggleButton : OsuClickableContainer + { + public BindableBool Active { get; } = new BindableBool(); + + public LocalisableString Text + { + get => text.Text; + set => text.Text = value; + } + + private readonly Box background; + private readonly OsuSpriteText text; + + private Sample? sampleOff; + private Sample? sampleOn; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + public ShearedToggleButton(float shear) + { + Height = 50; + Padding = new MarginPadding { Horizontal = shear * 50 }; + + Content.CornerRadius = 7; + Content.Shear = new Vector2(shear, 0); + Content.Masking = true; + Content.BorderThickness = 2; + Content.Anchor = Content.Origin = Anchor.Centre; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.TorusAlternate.With(size: 17), + Shear = new Vector2(-shear, 0) + } + }; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleOn = audio.Samples.Get(@"UI/check-on"); + sampleOff = audio.Samples.Get(@"UI/check-off"); + } + + protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet); + + protected override void LoadComplete() + { + base.LoadComplete(); + + Active.BindValueChanged(_ => + { + updateState(); + playSample(); + }); + Active.BindDisabledChanged(disabled => + { + updateState(); + Action = disabled ? (Action?)null : Active.Toggle; + }, true); + + FinishTransforms(true); + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateState(); + base.OnHoverLost(e); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + Content.ScaleTo(0.8f, 2000, Easing.OutQuint); + return base.OnMouseDown(e); + } + + protected override void OnMouseUp(MouseUpEvent e) + { + Content.ScaleTo(1, 1000, Easing.OutElastic); + base.OnMouseUp(e); + } + + private void updateState() + { + var darkerColour = Active.Value ? colourProvider.Highlight1 : colourProvider.Background3; + var lighterColour = Active.Value ? colourProvider.Colour0 : colourProvider.Background1; + + if (Active.Disabled) + { + darkerColour = darkerColour.Darken(0.3f); + lighterColour = lighterColour.Darken(0.3f); + } + else if (IsHovered) + { + darkerColour = darkerColour.Lighten(0.3f); + lighterColour = lighterColour.Lighten(0.3f); + } + + background.FadeColour(darkerColour, 150, Easing.OutQuint); + Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(darkerColour, lighterColour), 150, Easing.OutQuint); + + var textColour = Active.Value ? colourProvider.Background6 : colourProvider.Content1; + if (Active.Disabled) + textColour = textColour.Opacity(0.6f); + + text.FadeColour(textColour, 150, Easing.OutQuint); + } + + private void playSample() + { + if (Active.Value) + sampleOn?.Play(); + else + sampleOff?.Play(); + } + } +} From 40b6f3ff0a1f21da5f2b98b7ed6398906e8500c0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 31 Mar 2022 15:09:06 +0900 Subject: [PATCH 0087/1276] Rename method to CalculateAllLegacyCombinations() --- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 0935f26de6..b5aec0d659 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -121,10 +121,10 @@ namespace osu.Game.Rulesets.Difficulty /// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap. /// /// - /// This should only be used to compute difficulties for legacy mod combinations. + /// This can only be used to compute difficulties for legacy mod combinations. /// /// A collection of structures describing the difficulty of the beatmap for each mod combination. - public IEnumerable CalculateAll(CancellationToken cancellationToken = default) + public IEnumerable CalculateAllLegacyCombinations(CancellationToken cancellationToken = default) { var rulesetInstance = ruleset.CreateInstance(); From 5b3eb2d6f443ea90322787f28033e77335648cbe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Mar 2022 23:54:00 +0900 Subject: [PATCH 0088/1276] Add helper class to handle firing async multiplayer methods --- .../MultiplayerClientExtensions.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs diff --git a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs new file mode 100644 index 0000000000..98c593236a --- /dev/null +++ b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs @@ -0,0 +1,43 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using osu.Framework.Logging; + +namespace osu.Game.Online.Multiplayer +{ + public static class MultiplayerClientExtensions + { + public static void FireAndForget(this Task task, Action? onSuccess = null, Action? onError = null) => + task.ContinueWith(t => + { + if (t.IsFaulted) + { + Exception? exception = t.Exception; + + if (exception is AggregateException ae) + exception = ae.InnerException; + + Debug.Assert(exception != null); + + string message = exception is HubException + // HubExceptions arrive with additional message context added, but we want to display the human readable message: + // "An unexpected error occurred invoking 'AddPlaylistItem' on the server.InvalidStateException: Can't enqueue more than 3 items at once." + // We generally use the message field for a user-parseable error (eventually to be replaced), so drop the first part for now. + ? exception.Message.Substring(exception.Message.IndexOf(':') + 1).Trim() + : exception.Message; + + Logger.Log(message, level: LogLevel.Important); + onError?.Invoke(exception); + } + else + { + onSuccess?.Invoke(); + } + }); + } +} From e0c125a628c5848f01b4f8c9616c4cf75e6c2667 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Mar 2022 23:57:19 +0900 Subject: [PATCH 0089/1276] Replace existing usage with helper method --- .../Multiplayer/MultiplayerMatchSongSelect.cs | 49 ++++++------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index d49c122bd1..2e8f625eba 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -1,13 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Diagnostics; using System.Linq; using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR; using osu.Framework.Allocation; -using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; @@ -76,40 +72,23 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Task task = itemToEdit != null ? client.EditPlaylistItem(multiplayerItem) : client.AddPlaylistItem(multiplayerItem); - task.ContinueWith(t => + task.FireAndForget(onSuccess: () => Schedule(() => { - Schedule(() => - { - // If an error or server side trigger occurred this screen may have already exited by external means. - if (!this.IsCurrentScreen()) - return; + // If an error or server side trigger occurred this screen may have already exited by external means. + if (!this.IsCurrentScreen()) + return; - loadingLayer.Hide(); + loadingLayer.Hide(); + this.Exit(); + }), onError: _ => Schedule(() => + { + // If an error or server side trigger occurred this screen may have already exited by external means. + if (!this.IsCurrentScreen()) + return; - if (t.IsFaulted) - { - Exception exception = t.Exception; - - if (exception is AggregateException ae) - exception = ae.InnerException; - - Debug.Assert(exception != null); - - string message = exception is HubException - // HubExceptions arrive with additional message context added, but we want to display the human readable message: - // "An unexpected error occurred invoking 'AddPlaylistItem' on the server.InvalidStateException: Can't enqueue more than 3 items at once." - // We generally use the message field for a user-parseable error (eventually to be replaced), so drop the first part for now. - ? exception.Message.Substring(exception.Message.IndexOf(':') + 1).Trim() - : exception.Message; - - Logger.Log(message, level: LogLevel.Important); - Carousel.AllowSelection = true; - return; - } - - this.Exit(); - }); - }); + loadingLayer.Hide(); + Carousel.AllowSelection = true; + })); } else { From 0b6db315111cdc4756c7eda240836f2a633a6d59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Mar 2022 23:58:30 +0900 Subject: [PATCH 0090/1276] Guard other multiplayer client calls with exception handling --- .../Multiplayer/Match/MatchStartControl.cs | 17 ++++++++--------- .../Match/Playlist/MultiplayerQueueList.cs | 2 +- .../OnlinePlay/Multiplayer/Multiplayer.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 6 +++--- .../Participants/ParticipantPanel.cs | 6 +++--- .../Multiplayer/Participants/TeamDisplay.cs | 2 +- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index 1201279929..d048676872 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -114,18 +114,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match bool isReady() => Client.LocalUser?.State == MultiplayerUserState.Ready || Client.LocalUser?.State == MultiplayerUserState.Spectating; - void toggleReady() => Client.ToggleReady().ContinueWith(_ => endOperation()); + void toggleReady() => Client.ToggleReady().FireAndForget( + onSuccess: endOperation, + onError: _ => endOperation()); - void startMatch() => Client.StartMatch().ContinueWith(t => + void startMatch() => Client.StartMatch().FireAndForget(onSuccess: () => { - // accessing Exception here silences any potential errors from the antecedent task - if (t.Exception != null) - { - // gameplay was not started due to an exception; unblock button. - endOperation(); - } - // gameplay is starting, the button will be unblocked on load requested. + }, onError: _ => + { + // gameplay was not started due to an exception; unblock button. + endOperation(); }); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs index 1653d416d8..d72ce5e960 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist { base.LoadComplete(); - RequestDeletion = item => multiplayerClient.RemovePlaylistItem(item.ID); + RequestDeletion = item => multiplayerClient.RemovePlaylistItem(item.ID).FireAndForget(); multiplayerClient.RoomUpdated += onRoomUpdated; onRoomUpdated(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 28c9bef3f0..2482d52492 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // If gameplay wasn't finished, then we have a simple path back to the idle state by aborting gameplay. if (!playerLoader.GameplayPassed) { - client.AbortGameplay(); + client.AbortGameplay().FireAndForget(); return; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index e53153e017..7ef0bb5923 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -281,7 +281,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client.Room == null) return; - client.ChangeUserMods(mods.NewValue); + client.ChangeUserMods(mods.NewValue).FireAndForget(); modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue); modSettingChangeTracker.SettingChanged += onModSettingsChanged; @@ -296,7 +296,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client.Room == null) return; - client.ChangeUserMods(UserMods.Value); + client.ChangeUserMods(UserMods.Value).FireAndForget(); }, 500); } @@ -305,7 +305,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client.Room == null) return; - client.ChangeBeatmapAvailability(availability.NewValue); + client.ChangeBeatmapAvailability(availability.NewValue).FireAndForget(); if (availability.NewValue.State != DownloadState.LocallyAvailable) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 7ba0a63856..e091559046 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -169,7 +169,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Origin = Anchor.Centre, Alpha = 0, Margin = new MarginPadding(4), - Action = () => Client.KickUser(User.UserID), + Action = () => Client.KickUser(User.UserID).FireAndForget(), }, }, } @@ -231,7 +231,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants if (!Client.IsHost) return; - Client.TransferHost(targetUser); + Client.TransferHost(targetUser).FireAndForget(); }), new OsuMenuItem("Kick", MenuItemType.Destructive, () => { @@ -239,7 +239,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants if (!Client.IsHost) return; - Client.KickUser(targetUser); + Client.KickUser(targetUser).FireAndForget(); }) }; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs index 73aca0acdc..aca2c6073a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs @@ -83,7 +83,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Client.SendMatchRequest(new ChangeTeamRequest { TeamID = ((Client.LocalUser?.MatchState as TeamVersusUserState)?.TeamID + 1) % 2 ?? 0, - }); + }).FireAndForget(); } public int? DisplayedTeam { get; private set; } From f1aa60c0f1c2fec417db5bc05d8d6dd2eb3dd391 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Thu, 31 Mar 2022 16:26:53 +0200 Subject: [PATCH 0091/1276] Make the clock feel more like a button --- osu.Game/Overlays/Toolbar/ToolbarClock.cs | 92 +++++++++++++++++------ 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarClock.cs b/osu.Game/Overlays/Toolbar/ToolbarClock.cs index ad5c9ac7a1..02230c13cf 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarClock.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarClock.cs @@ -3,54 +3,83 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osuTK; +using osuTK.Graphics; + namespace osu.Game.Overlays.Toolbar { - public class ToolbarClock : CompositeDrawable + public class ToolbarClock : OsuClickableContainer { private Bindable clockDisplayMode; + protected Box HoverBackground; + private readonly Box flashBackground; + private readonly FillFlowContainer clockContainer; + private DigitalClockDisplay digital; private AnalogClockDisplay analog; public ToolbarClock() + : base(HoverSampleSet.Toolbar) { RelativeSizeAxes = Axes.Y; AutoSizeAxes = Axes.X; - Padding = new MarginPadding(10); + Children = new Drawable[] + { + HoverBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(80).Opacity(180), + Blending = BlendingParameters.Additive, + Alpha = 0, + }, + flashBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Colour = Color4.White.Opacity(100), + Blending = BlendingParameters.Additive, + }, + + clockContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Padding = new MarginPadding(10), + Children = new Drawable[] + { + analog = new AnalogClockDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + digital = new DigitalClockDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + } + } + } + }; } [BackgroundDependencyLoader] private void load(OsuConfigManager config) { clockDisplayMode = config.GetBindable(OsuSetting.ToolbarClockDisplayMode); - - InternalChild = new FillFlowContainer - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Children = new Drawable[] - { - analog = new AnalogClockDisplay - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - digital = new DigitalClockDisplay - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - } - } - }; } protected override void LoadComplete() @@ -72,8 +101,23 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnClick(ClickEvent e) { + flashBackground.FadeOutFromOne(800, Easing.OutQuint); + cycleDisplayMode(); - return true; + + return base.OnClick(e); + } + + protected override bool OnHover(HoverEvent e) + { + HoverBackground.FadeIn(200); + + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + HoverBackground.FadeOut(200); } private void cycleDisplayMode() From c64a90b39e86a38b7ce5a54ff950ee8f7b986321 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Thu, 31 Mar 2022 17:21:50 +0200 Subject: [PATCH 0092/1276] Remove a newline to comply with codefactor --- osu.Game/Overlays/Toolbar/ToolbarClock.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarClock.cs b/osu.Game/Overlays/Toolbar/ToolbarClock.cs index 02230c13cf..48de803b3a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarClock.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarClock.cs @@ -15,7 +15,6 @@ using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; - namespace osu.Game.Overlays.Toolbar { public class ToolbarClock : OsuClickableContainer From bdb21b17f721ab0a9b9f302439cd62033eafae14 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Thu, 31 Mar 2022 20:39:26 +0200 Subject: [PATCH 0093/1276] Fix my code according to the changes @bdach requested --- osu.Game/Overlays/Toolbar/ToolbarClock.cs | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarClock.cs b/osu.Game/Overlays/Toolbar/ToolbarClock.cs index 48de803b3a..22a96603dc 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarClock.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarClock.cs @@ -21,9 +21,8 @@ namespace osu.Game.Overlays.Toolbar { private Bindable clockDisplayMode; - protected Box HoverBackground; - private readonly Box flashBackground; - private readonly FillFlowContainer clockContainer; + private Box hoverBackground; + private Box flashBackground; private DigitalClockDisplay digital; private AnalogClockDisplay analog; @@ -33,10 +32,16 @@ namespace osu.Game.Overlays.Toolbar { RelativeSizeAxes = Axes.Y; AutoSizeAxes = Axes.X; + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + clockDisplayMode = config.GetBindable(OsuSetting.ToolbarClockDisplayMode); Children = new Drawable[] { - HoverBackground = new Box + hoverBackground = new Box { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(80).Opacity(180), @@ -50,8 +55,7 @@ namespace osu.Game.Overlays.Toolbar Colour = Color4.White.Opacity(100), Blending = BlendingParameters.Additive, }, - - clockContainer = new FillFlowContainer + new FillFlowContainer { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, @@ -75,12 +79,6 @@ namespace osu.Game.Overlays.Toolbar }; } - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - clockDisplayMode = config.GetBindable(OsuSetting.ToolbarClockDisplayMode); - } - protected override void LoadComplete() { base.LoadComplete(); @@ -109,14 +107,16 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnHover(HoverEvent e) { - HoverBackground.FadeIn(200); + hoverBackground.FadeIn(200); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - HoverBackground.FadeOut(200); + hoverBackground.FadeOut(200); + + base.OnHoverLost(e); } private void cycleDisplayMode() From b3896257ca0721e50ce5e21f8572ade3da40dccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 31 Mar 2022 22:09:03 +0200 Subject: [PATCH 0094/1276] Move shear amount to constant --- .../Visual/UserInterface/TestSceneShearedToggleButton.cs | 4 ++-- osu.Game/Graphics/UserInterface/ShearedToggleButton.cs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs index a969858157..5082e93f37 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create button", () => { - Child = button = new ShearedToggleButton(0.2f) + Child = button = new ShearedToggleButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create button", () => { - Child = button = new ShearedToggleButton(0.2f) + Child = button = new ShearedToggleButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs b/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs index acbed29279..27d2611983 100644 --- a/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs @@ -37,10 +37,12 @@ namespace osu.Game.Graphics.UserInterface private Sample? sampleOff; private Sample? sampleOn; + private const float shear = 0.2f; + [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - public ShearedToggleButton(float shear) + public ShearedToggleButton() { Height = 50; Padding = new MarginPadding { Horizontal = shear * 50 }; From e180db145dcee071d8e559345a1bdb25f66856ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 31 Mar 2022 22:19:08 +0200 Subject: [PATCH 0095/1276] Add constructor argument to facilitate fixed width/autosizing --- .../TestSceneShearedToggleButton.cs | 25 +++++++++++++++++-- .../UserInterface/ShearedToggleButton.cs | 22 +++++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs index 5082e93f37..f12cbc4979 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs @@ -23,12 +23,11 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create button", () => { - Child = button = new ShearedToggleButton + Child = button = new ShearedToggleButton(200) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = "Toggle me", - Width = 200 }; }); @@ -36,6 +35,28 @@ namespace osu.Game.Tests.Visual.UserInterface AddToggleStep("toggle disabled", disabled => button.Active.Disabled = disabled); } + [Test] + public void TestSizing() + { + ShearedToggleButton toggleButton = null; + + AddStep("create fixed width button", () => Child = toggleButton = new ShearedToggleButton(200) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Fixed width" + }); + AddStep("change text", () => toggleButton.Text = "New text"); + + AddStep("create auto-sizing button", () => Child = toggleButton = new ShearedToggleButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "This button autosizes to its text!" + }); + AddStep("change text", () => toggleButton.Text = "New text"); + } + [Test] public void TestDisabledState() { diff --git a/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs b/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs index 27d2611983..aed3be20a0 100644 --- a/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs @@ -42,7 +42,17 @@ namespace osu.Game.Graphics.UserInterface [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - public ShearedToggleButton() + /// + /// Creates a new + /// + /// + /// The width of the button. + /// + /// If a non- value is provided, this button will have a fixed width equal to the provided value. + /// If a value is provided (or the argument is omitted entirely), the button will autosize in width to fit the text. + /// + /// + public ShearedToggleButton(float? width = null) { Height = 50; Padding = new MarginPadding { Horizontal = shear * 50 }; @@ -67,6 +77,16 @@ namespace osu.Game.Graphics.UserInterface Shear = new Vector2(-shear, 0) } }; + + if (width != null) + { + Width = width.Value; + } + else + { + AutoSizeAxes = Axes.X; + text.Margin = new MarginPadding { Horizontal = 15 }; + } } [BackgroundDependencyLoader] From 058350dfd8bf7c7754ccc25bb20357185a750817 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 10:53:14 +0900 Subject: [PATCH 0096/1276] Fix failing test due to incorrect sizing specification --- .../Visual/UserInterface/TestSceneShearedToggleButton.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs index f12cbc4979..b5109aa58d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs @@ -64,12 +64,11 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create button", () => { - Child = button = new ShearedToggleButton + Child = button = new ShearedToggleButton(200) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = "Toggle me", - Width = 200 }; }); From d0873d67e1569b1345113d34407bc0878bbf718c Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 20 Mar 2022 13:36:18 -0700 Subject: [PATCH 0097/1276] Move difficulty graph toggle to individual skin settings --- osu.Game/Configuration/OsuConfigManager.cs | 2 -- osu.Game/Localisation/GameplaySettingsStrings.cs | 5 ----- .../Overlays/Settings/Sections/Gameplay/HUDSettings.cs | 5 ----- osu.Game/Screens/Play/SongProgress.cs | 7 +++---- 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index e8f13ba902..1b10456c8e 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -109,7 +109,6 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.HitLighting, true); SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always); - SetDefault(OsuSetting.ShowProgressGraph, true); SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true); SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true); SetDefault(OsuSetting.KeyOverlay, false); @@ -266,7 +265,6 @@ namespace osu.Game.Configuration AlwaysPlayFirstComboBreak, FloatingComments, HUDVisibilityMode, - ShowProgressGraph, ShowHealthDisplayWhenCantFail, FadePlayfieldWhenHealthLow, MouseDisableButtons, diff --git a/osu.Game/Localisation/GameplaySettingsStrings.cs b/osu.Game/Localisation/GameplaySettingsStrings.cs index 84c3704e26..8a0f773551 100644 --- a/osu.Game/Localisation/GameplaySettingsStrings.cs +++ b/osu.Game/Localisation/GameplaySettingsStrings.cs @@ -64,11 +64,6 @@ namespace osu.Game.Localisation /// public static LocalisableString HUDVisibilityMode => new TranslatableString(getKey(@"hud_visibility_mode"), @"HUD overlay visibility mode"); - /// - /// "Show difficulty graph on progress bar" - /// - public static LocalisableString ShowDifficultyGraph => new TranslatableString(getKey(@"show_difficulty_graph"), @"Show difficulty graph on progress bar"); - /// /// "Show health display even when you can't fail" /// diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs index ba9779d650..e30df6b868 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs @@ -24,11 +24,6 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay Current = config.GetBindable(OsuSetting.HUDVisibilityMode) }, new SettingsCheckbox - { - LabelText = GameplaySettingsStrings.ShowDifficultyGraph, - Current = config.GetBindable(OsuSetting.ShowProgressGraph) - }, - new SettingsCheckbox { LabelText = GameplaySettingsStrings.ShowHealthDisplayWhenCantFail, Current = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail), diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index e620abb90f..9470c60fe6 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -42,7 +42,8 @@ namespace osu.Game.Screens.Play /// public readonly Bindable AllowSeeking = new Bindable(); - public readonly Bindable ShowGraph = new Bindable(); + [SettingSource("Show difficulty graph", "Whether a graph should show difficulty throughout the beatmap")] + public Bindable ShowGraph { get; } = new BindableBool(true); public override bool HandleNonPositionalInput => AllowSeeking.Value; public override bool HandlePositionalInput => AllowSeeking.Value; @@ -116,7 +117,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, OsuConfigManager config) + private void load(OsuColour colours) { base.LoadComplete(); @@ -129,8 +130,6 @@ namespace osu.Game.Screens.Play Objects = drawableRuleset.Objects; } - config.BindWith(OsuSetting.ShowProgressGraph, ShowGraph); - graph.FillColour = bar.FillColour = colours.BlueLighter; } From a987cda30daa1a235b035fafeb4add95618ddcc8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 12:15:26 +0900 Subject: [PATCH 0098/1276] Rename "Aim Assist" to "Magnetised" to better suit the mod's behaviour As proposed in https://github.com/ppy/osu/discussions/17375. --- ...uModAimAssist.cs => TestSceneOsuModMagnetised.cs} | 6 +++--- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs | 2 +- .../Mods/{OsuModAimAssist.cs => OsuModMagnetised.cs} | 12 ++++++------ osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) rename osu.Game.Rulesets.Osu.Tests/Mods/{TestSceneOsuModAimAssist.cs => TestSceneOsuModMagnetised.cs} (79%) rename osu.Game.Rulesets.Osu/Mods/{OsuModAimAssist.cs => OsuModMagnetised.cs} (87%) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAimAssist.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs similarity index 79% rename from osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAimAssist.cs rename to osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs index b8310bc4e7..e7a40d6337 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAimAssist.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs @@ -6,16 +6,16 @@ using osu.Game.Rulesets.Osu.Mods; namespace osu.Game.Rulesets.Osu.Tests.Mods { - public class TestSceneOsuModAimAssist : OsuModTestScene + public class TestSceneOsuModMagnetised : OsuModTestScene { [TestCase(0.1f)] [TestCase(0.5f)] [TestCase(1)] - public void TestAimAssist(float strength) + public void TestMagnetised(float strength) { CreateModTest(new ModTestData { - Mod = new OsuModAimAssist + Mod = new OsuModMagnetised { AssistStrength = { Value = strength }, }, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 983964d639..aaf455e95f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Automation; public override string Description => @"Automatic cursor movement - just follow the rhythm."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModAimAssist) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModMagnetised) }; public bool PerformFail() => false; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs index 31179cdf4a..b31ef5d2fd 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModAutoplay : ModAutoplay { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAimAssist), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" }); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs b/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs index d677ab43d0..5b42772358 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModCinema : ModCinema { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAimAssist), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" }); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAimAssist.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs similarity index 87% rename from osu.Game.Rulesets.Osu/Mods/OsuModAimAssist.cs rename to osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs index 1abbd67d8f..31598c50e7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAimAssist.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs @@ -16,19 +16,19 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModAimAssist : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset + internal class OsuModMagnetised : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset { - public override string Name => "Aim Assist"; - public override string Acronym => "AA"; - public override IconUsage? Icon => FontAwesome.Solid.MousePointer; + public override string Name => "Magnetised"; + public override string Acronym => "MG"; + public override IconUsage? Icon => FontAwesome.Solid.Magnet; public override ModType Type => ModType.Fun; - public override string Description => "No need to chase the circle – the circle chases you!"; + public override string Description => "No need to chase the circles – your cursor is a magnet!"; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax) }; private IFrameStableClock gameplayClock; - [SettingSource("Assist strength", "How much this mod will assist you.", 0)] + [SettingSource("Attraction strength", "How strong the pull is.", 0)] public BindableFloat AssistStrength { get; } = new BindableFloat(0.5f) { Precision = 0.05f, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 9719de441e..6b81efdca6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset, IApplicableToPlayer { public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModAimAssist) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised) }).ToArray(); /// /// How early before a hitobject's start time to trigger a hit. diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs index 28c3b069b6..45ce4d555a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override string Description => "Everything rotates. EVERYTHING."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle), typeof(OsuModAimAssist) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle), typeof(OsuModMagnetised) }; private float theta; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs index 40a05400ea..693a5bee0b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override string Description => "They just won't stay still..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModAimAssist) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModMagnetised) }; private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles private const int wiggle_strength = 10; // Higher = stronger wiggles diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 47a2618ddd..207e7a4ab0 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu new OsuModApproachDifferent(), new OsuModMuted(), new OsuModNoScope(), - new OsuModAimAssist(), + new OsuModMagnetised(), new ModAdaptiveSpeed() }; From ea672745b0d026ce7ca08d45239dca083e614635 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 12:44:49 +0900 Subject: [PATCH 0099/1276] Add ability to switch between most common tournament scenes using key bindings --- osu.Game.Tournament/TournamentSceneManager.cs | 66 ++++++++++++++++--- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament/TournamentSceneManager.cs b/osu.Game.Tournament/TournamentSceneManager.cs index 80a9c07cde..98338244e4 100644 --- a/osu.Game.Tournament/TournamentSceneManager.cs +++ b/osu.Game.Tournament/TournamentSceneManager.cs @@ -7,8 +7,10 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens; using osu.Game.Tournament.Screens.Drawings; @@ -23,6 +25,7 @@ using osu.Game.Tournament.Screens.TeamIntro; using osu.Game.Tournament.Screens.TeamWin; using osuTK; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tournament { @@ -123,16 +126,16 @@ namespace osu.Game.Tournament new ScreenButton(typeof(RoundEditorScreen)) { Text = "Rounds Editor", RequestSelection = SetScreen }, new ScreenButton(typeof(LadderEditorScreen)) { Text = "Bracket Editor", RequestSelection = SetScreen }, new Separator(), - new ScreenButton(typeof(ScheduleScreen)) { Text = "Schedule", RequestSelection = SetScreen }, - new ScreenButton(typeof(LadderScreen)) { Text = "Bracket", RequestSelection = SetScreen }, + new ScreenButton(typeof(ScheduleScreen), Key.S) { Text = "Schedule", RequestSelection = SetScreen }, + new ScreenButton(typeof(LadderScreen), Key.B) { Text = "Bracket", RequestSelection = SetScreen }, new Separator(), - new ScreenButton(typeof(TeamIntroScreen)) { Text = "Team Intro", RequestSelection = SetScreen }, - new ScreenButton(typeof(SeedingScreen)) { Text = "Seeding", RequestSelection = SetScreen }, + new ScreenButton(typeof(TeamIntroScreen), Key.I) { Text = "Team Intro", RequestSelection = SetScreen }, + new ScreenButton(typeof(SeedingScreen), Key.D) { Text = "Seeding", RequestSelection = SetScreen }, new Separator(), - new ScreenButton(typeof(MapPoolScreen)) { Text = "Map Pool", RequestSelection = SetScreen }, - new ScreenButton(typeof(GameplayScreen)) { Text = "Gameplay", RequestSelection = SetScreen }, + new ScreenButton(typeof(MapPoolScreen), Key.M) { Text = "Map Pool", RequestSelection = SetScreen }, + new ScreenButton(typeof(GameplayScreen), Key.G) { Text = "Gameplay", RequestSelection = SetScreen }, new Separator(), - new ScreenButton(typeof(TeamWinScreen)) { Text = "Win", RequestSelection = SetScreen }, + new ScreenButton(typeof(TeamWinScreen), Key.W) { Text = "Win", RequestSelection = SetScreen }, new Separator(), new ScreenButton(typeof(DrawingsScreen)) { Text = "Drawings", RequestSelection = SetScreen }, new ScreenButton(typeof(ShowcaseScreen)) { Text = "Showcase", RequestSelection = SetScreen }, @@ -231,13 +234,60 @@ namespace osu.Game.Tournament { public readonly Type Type; - public ScreenButton(Type type) + private readonly Key? shortcutKey; + + public ScreenButton(Type type, Key? shortcutKey = null) { + this.shortcutKey = shortcutKey; + Type = type; + BackgroundColour = OsuColour.Gray(0.2f); Action = () => RequestSelection?.Invoke(type); RelativeSizeAxes = Axes.X; + + if (shortcutKey != null) + { + Add(new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(24), + Margin = new MarginPadding(5), + Masking = true, + CornerRadius = 4, + Alpha = 0.5f, + Blending = BlendingParameters.Additive, + Children = new Drawable[] + { + new Box + { + Colour = OsuColour.Gray(0.1f), + RelativeSizeAxes = Axes.Both, + }, + new OsuSpriteText + { + Font = OsuFont.Default.With(size: 24), + Y = -2, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = shortcutKey.ToString(), + } + } + }); + } + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.Key == shortcutKey) + { + TriggerClick(); + return true; + } + + return base.OnKeyDown(e); } private bool isSelected; From de625125d6d04b97d8571d62267e6e04328dfde8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 13:03:48 +0900 Subject: [PATCH 0100/1276] Rename magnetised mod attraction strength property to match new naming --- osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs index e7a40d6337..9b49e60363 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new OsuModMagnetised { - AssistStrength = { Value = strength }, + AttractionStrength = { Value = strength }, }, PassCondition = () => true, Autoplay = false, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs index 31598c50e7..ca6e9cfb1d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Mods private IFrameStableClock gameplayClock; [SettingSource("Attraction strength", "How strong the pull is.", 0)] - public BindableFloat AssistStrength { get; } = new BindableFloat(0.5f) + public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f) { Precision = 0.05f, MinValue = 0.05f, @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Mods private void easeTo(DrawableHitObject hitObject, Vector2 destination) { - double dampLength = Interpolation.Lerp(3000, 40, AssistStrength.Value); + double dampLength = Interpolation.Lerp(3000, 40, AttractionStrength.Value); float x = (float)Interpolation.DampContinuously(hitObject.X, destination.X, dampLength, gameplayClock.ElapsedFrameTime); float y = (float)Interpolation.DampContinuously(hitObject.Y, destination.Y, dampLength, gameplayClock.ElapsedFrameTime); From 69d4f8612268ab280408f765d44256e385f25f9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 14:11:53 +0900 Subject: [PATCH 0101/1276] Fix automatically created "(modified)" skins getting conflicting names Applies the already tested and proven method that is used in the editor to the mutable skin creation flow. --- osu.Game/Skinning/SkinManager.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index bad559d9fe..44b9c69794 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -24,6 +24,7 @@ using osu.Game.Database; using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Overlays.Notifications; +using osu.Game.Utils; namespace osu.Game.Skinning { @@ -144,20 +145,26 @@ namespace osu.Game.Skinning if (!s.Protected) return; + var existingSkinNames = realm.Run(r => r.All() + .Where(skin => !skin.DeletePending) + .AsEnumerable() + .Select(skin => skin.Name)); + // if the user is attempting to save one of the default skin implementations, create a copy first. - var result = skinModelManager.Import(new SkinInfo + var skinInfo = new SkinInfo { - Name = s.Name + @" (modified)", Creator = s.Creator, InstantiationInfo = s.InstantiationInfo, - }); + Name = NamingUtils.GetNextBestName(existingSkinNames, $"{s.Name} (modified)") + }; + + var result = skinModelManager.Import(skinInfo); if (result != null) { // save once to ensure the required json content is populated. // currently this only happens on save. result.PerformRead(skin => Save(skin.CreateInstance(this))); - CurrentSkinInfo.Value = result; } }); From 88306a61804c4d960f0c1b06090f9c914fb3f6b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 14:22:26 +0900 Subject: [PATCH 0102/1276] Disable ability to select random skin from within the skin editor Reasoning is explained in inline comment. I knowingly only applied this to the shortcut key. It's still feasible a user can choose the option from the skin dropdown while the editor is open, but that's less of an issue (because a user won't get the same compulsion that I get to mash the key, only to be greeted with 100 new mutable skins created). --- osu.Game/OsuGame.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4cd954a646..73121f6e7d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1061,6 +1061,12 @@ namespace osu.Game return true; case GlobalAction.RandomSkin: + // Don't allow random skin selection while in the skin editor. + // This is mainly to stop many "osu! default (modified)" skins being created via the SkinManager.EnsureMutableSkin() path. + // If people want this to work we can potentially avoid selecting default skins when the editor is open, or allow a maximum of one mutable skin somehow. + if (skinEditor.State.Value == Visibility.Visible) + return false; + SkinManager.SelectRandomSkin(); return true; } From 01829cf2d89ebec7056a5e5cb082b8d91f5cd46d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 14:30:02 +0900 Subject: [PATCH 0103/1276] Move `SkinnableInfo` error handling to lower level Handling was recently added to handle the usage in `Skin.GetDrawableCompoent`, but it turns out this is also required for `DrawableExtensions.ApplySkinnableInfo` which can throw in a similar fashion. Found while working on sprite support for the editor, where this becomes an actual issue (ie. switching to a branch where the new sprite support is not present can cause unexpected crashes). --- osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 15 ++++++++++++--- osu.Game/Skinning/Skin.cs | 11 +---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index 95395f8181..1f659fd5bf 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -9,6 +9,7 @@ using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; using osu.Game.Configuration; using osu.Game.Extensions; using osu.Game.Skinning; @@ -84,9 +85,17 @@ namespace osu.Game.Screens.Play.HUD /// The new instance. public Drawable CreateInstance() { - Drawable d = (Drawable)Activator.CreateInstance(Type); - d.ApplySkinnableInfo(this); - return d; + try + { + Drawable d = (Drawable)Activator.CreateInstance(Type); + d.ApplySkinnableInfo(this); + return d; + } + catch (Exception e) + { + Logger.Error(e, $"Unable to create skin component {Type.Name}"); + return Drawable.Empty(); + } } } } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 2f01bb7301..5d4afc00c4 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -155,16 +155,7 @@ namespace osu.Game.Skinning var components = new List(); foreach (var i in skinnableInfo) - { - try - { - components.Add(i.CreateInstance()); - } - catch (Exception e) - { - Logger.Error(e, $"Unable to create skin component {i.Type.Name}"); - } - } + components.Add(i.CreateInstance()); return new SkinnableTargetComponentsContainer { From 3a16483214bb564dbf75f5bd61f6c7623e0783b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Mar 2022 15:10:26 +0900 Subject: [PATCH 0104/1276] Add prioritised user lookups for default skin This allows user resources to be consumed before falling back to the game bundled assets. --- osu.Game/Skinning/DefaultSkin.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 7c6d138f4c..43ada59bcb 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -46,13 +46,13 @@ namespace osu.Game.Skinning this.resources = resources; } - public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; + public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => Textures?.Get(componentName, wrapModeS, wrapModeT); public override ISample GetSample(ISampleInfo sampleInfo) { foreach (string lookup in sampleInfo.LookupNames) { - var sample = resources.AudioManager.Samples.Get(lookup); + var sample = Samples?.Get(lookup) ?? resources.AudioManager.Samples.Get(lookup); if (sample != null) return sample; } From fca9faac9b87eb9c24d86e2d52764619bbe2ddf0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Mar 2022 15:11:35 +0900 Subject: [PATCH 0105/1276] Add `SkinnableSprite` for arbitrary sprite additions --- .../Skinning/Components/SkinnableSprite.cs | 49 ++++++++++++++++ osu.Game/Skinning/Editor/SkinEditor.cs | 56 +++++++++++++++++-- osu.Game/Skinning/SkinManager.cs | 44 ++++++++++++++- osu.Game/Skinning/SkinnableSprite.cs | 4 +- 4 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Skinning/Components/SkinnableSprite.cs diff --git a/osu.Game/Skinning/Components/SkinnableSprite.cs b/osu.Game/Skinning/Components/SkinnableSprite.cs new file mode 100644 index 0000000000..292bbe2321 --- /dev/null +++ b/osu.Game/Skinning/Components/SkinnableSprite.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; + +namespace osu.Game.Skinning.Components +{ + /// + /// Intended to be a test bed for skinning. May be removed at some point in the future. + /// + [UsedImplicitly] + public class SkinSprite : CompositeDrawable, ISkinnableDrawable + { + public bool UsesFixedAnchor { get; set; } + + [SettingSource("Sprite name", "The filename of the sprite")] + public Bindable SpriteName { get; } = new Bindable(string.Empty); + + [Resolved] + private ISkinSource source { get; set; } + + public SkinSprite() + { + AutoSizeAxes = Axes.Both; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + SpriteName.BindValueChanged(spriteName => + { + InternalChildren = new Drawable[] + { + new Sprite + { + Texture = source.GetTexture(SpriteName.Value), + } + }; + }, true); + } + } +} diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 7bf4e94662..7701fafbfc 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -11,6 +13,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Testing; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; @@ -18,11 +21,12 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Edit.Components.Menus; +using osu.Game.Skinning.Components; namespace osu.Game.Skinning.Editor { [Cached(typeof(SkinEditor))] - public class SkinEditor : VisibilityContainer + public class SkinEditor : VisibilityContainer, ICanAcceptFiles { public const double TRANSITION_DURATION = 500; @@ -36,6 +40,9 @@ namespace osu.Game.Skinning.Editor private Bindable currentSkin; + [Resolved(canBeNull: true)] + private OsuGame game { get; set; } + [Resolved] private SkinManager skins { get; set; } @@ -171,6 +178,8 @@ namespace osu.Game.Skinning.Editor Show(); + game?.RegisterImportHandler(this); + // as long as the skin editor is loaded, let's make sure we can modify the current skin. currentSkin = skins.CurrentSkin.GetBoundCopy(); @@ -186,6 +195,13 @@ namespace osu.Game.Skinning.Editor SelectedComponents.BindCollectionChanged((_, __) => Scheduler.AddOnce(populateSettings), true); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + game?.UnregisterImportHandler(this); + } + public void UpdateTargetScreen(Drawable targetScreen) { this.targetScreen = targetScreen; @@ -229,15 +245,20 @@ namespace osu.Game.Skinning.Editor } private void placeComponent(Type type) + { + if (!(Activator.CreateInstance(type) is ISkinnableDrawable component)) + throw new InvalidOperationException($"Attempted to instantiate a component for placement which was not an {typeof(ISkinnableDrawable)}."); + + placeComponent(component); + } + + private void placeComponent(ISkinnableDrawable component) { var targetContainer = getFirstTarget(); if (targetContainer == null) return; - if (!(Activator.CreateInstance(type) is ISkinnableDrawable component)) - throw new InvalidOperationException($"Attempted to instantiate a component for placement which was not an {typeof(ISkinnableDrawable)}."); - var drawableComponent = (Drawable)component; // give newly added components a sane starting location. @@ -313,5 +334,32 @@ namespace osu.Game.Skinning.Editor foreach (var item in items) availableTargets.FirstOrDefault(t => t.Components.Contains(item))?.Remove(item); } + + public Task Import(params string[] paths) + { + Schedule(() => + { + var file = new FileInfo(paths.First()); + + // import to skin + currentSkin.Value.SkinInfo.PerformWrite(skinInfo => + { + using (var contents = file.OpenRead()) + skins.AddFile(skinInfo, contents, file.Name); + }); + + // place component + placeComponent(new SkinSprite + { + SpriteName = { Value = Path.GetFileNameWithoutExtension(file.Name) } + }); + }); + + return Task.CompletedTask; + } + + public Task Import(params ImportTask[] tasks) => throw new NotImplementedException(); + + public IEnumerable HandledExtensions => new[] { ".jpg", ".jpeg", ".png" }; } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index bad559d9fe..5333b58625 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Linq.Expressions; using System.Threading; @@ -23,6 +24,7 @@ using osu.Game.Audio; using osu.Game.Database; using osu.Game.IO; using osu.Game.IO.Archives; +using osu.Game.Models; using osu.Game.Overlays.Notifications; namespace osu.Game.Skinning @@ -35,7 +37,7 @@ namespace osu.Game.Skinning /// For gameplay components, see which adds extra legacy and toggle logic that may affect the lookup process. /// [ExcludeFromDynamicCompile] - public class SkinManager : ISkinSource, IStorageResourceProvider, IModelImporter + public class SkinManager : ISkinSource, IStorageResourceProvider, IModelImporter, IModelManager, IModelFileManager { private readonly AudioManager audio; @@ -306,5 +308,45 @@ namespace osu.Game.Skinning } #endregion + + public bool Delete(SkinInfo item) + { + return skinModelManager.Delete(item); + } + + public void Delete(List items, bool silent = false) + { + skinModelManager.Delete(items, silent); + } + + public void Undelete(List items, bool silent = false) + { + skinModelManager.Undelete(items, silent); + } + + public void Undelete(SkinInfo item) + { + skinModelManager.Undelete(item); + } + + public bool IsAvailableLocally(SkinInfo model) + { + return skinModelManager.IsAvailableLocally(model); + } + + public void ReplaceFile(SkinInfo model, RealmNamedFileUsage file, Stream contents) + { + skinModelManager.ReplaceFile(model, file, contents); + } + + public void DeleteFile(SkinInfo model, RealmNamedFileUsage file) + { + skinModelManager.DeleteFile(model, file); + } + + public void AddFile(SkinInfo model, Stream contents, string filename) + { + skinModelManager.AddFile(model, contents, filename); + } } } diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index 56e576d081..38803bd8e3 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -11,7 +11,7 @@ namespace osu.Game.Skinning /// /// A skinnable element which uses a stable sprite and can therefore share implementation logic. /// - public class SkinnableSprite : SkinnableDrawable + public class SkinnableSprite : SkinnableDrawable, ISkinnableDrawable { protected override bool ApplySizeRestrictionsToDefault => true; @@ -42,5 +42,7 @@ namespace osu.Game.Skinning public string LookupName { get; } } + + public bool UsesFixedAnchor { get; set; } } } From 66f5eae530cac55975b46655825d2b159e73976d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 19:32:34 +0900 Subject: [PATCH 0106/1276] Hook up a dropdown to show all available sprites for the current skin --- .../Configuration/SettingSourceAttribute.cs | 1 + osu.Game/Overlays/Settings/SettingsItem.cs | 6 ++++++ .../Skinning/Components/SkinnableSprite.cs | 19 ++++++++++++++++++- osu.Game/Skinning/Editor/SkinEditor.cs | 2 +- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index 4111a67b24..8c84707b88 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -88,6 +88,7 @@ namespace osu.Game.Configuration throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} had an unsupported custom control type ({controlType.ReadableName()})"); var control = (Drawable)Activator.CreateInstance(controlType); + controlType.GetProperty(nameof(SettingsItem.Source))?.SetValue(control, obj); controlType.GetProperty(nameof(SettingsItem.LabelText))?.SetValue(control, attr.Label); controlType.GetProperty(nameof(SettingsItem.TooltipText))?.SetValue(control, attr.Description); controlType.GetProperty(nameof(SettingsItem.Current))?.SetValue(control, value); diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index e709be1343..6ac5351270 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Containers; @@ -24,6 +25,11 @@ namespace osu.Game.Overlays.Settings protected Drawable Control { get; } + /// + /// The source component if this was created via . + /// + public Drawable Source { get; internal set; } + private IHasCurrentValue controlWithCurrent => Control as IHasCurrentValue; protected override Container Content => FlowContent; diff --git a/osu.Game/Skinning/Components/SkinnableSprite.cs b/osu.Game/Skinning/Components/SkinnableSprite.cs index 292bbe2321..aa23e428d1 100644 --- a/osu.Game/Skinning/Components/SkinnableSprite.cs +++ b/osu.Game/Skinning/Components/SkinnableSprite.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -8,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; +using osu.Game.Overlays.Settings; namespace osu.Game.Skinning.Components { @@ -19,12 +22,14 @@ namespace osu.Game.Skinning.Components { public bool UsesFixedAnchor { get; set; } - [SettingSource("Sprite name", "The filename of the sprite")] + [SettingSource("Sprite name", "The filename of the sprite", SettingControlType = typeof(SpriteSelectorControl))] public Bindable SpriteName { get; } = new Bindable(string.Empty); [Resolved] private ISkinSource source { get; set; } + public IEnumerable AvailableFiles => (source.AllSources.First() as Skin)?.SkinInfo.PerformRead(s => s.Files.Select(f => f.Filename)); + public SkinSprite() { AutoSizeAxes = Axes.Both; @@ -45,5 +50,17 @@ namespace osu.Game.Skinning.Components }; }, true); } + + public class SpriteSelectorControl : SettingsDropdown + { + public SkinSprite Source { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Items = Source.AvailableFiles; + } + } } } diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 7701fafbfc..392cb2f32b 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -351,7 +351,7 @@ namespace osu.Game.Skinning.Editor // place component placeComponent(new SkinSprite { - SpriteName = { Value = Path.GetFileNameWithoutExtension(file.Name) } + SpriteName = { Value = file.Name } }); }); From 9c3dad9fbf72f63cbb79fdc8f2ebc687a2d66f69 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 19:09:17 +0900 Subject: [PATCH 0107/1276] Add proof of concept flow to ensure `RealmBackedResourceStore` is invalidated on realm file changes I'm not at all happy with this, but it does work so let's go with it for now. --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 5 +- osu.Game/Skinning/RealmBackedResourceStore.cs | 57 ++++++++++++------- osu.Game/Skinning/Skin.cs | 8 ++- osu.Game/Skinning/SkinManager.cs | 21 ++++++- 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 16a05f4197..70f5b35d00 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -11,6 +11,7 @@ using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; +using osu.Game.Database; using osu.Game.IO; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Types; @@ -37,11 +38,11 @@ namespace osu.Game.Skinning private static IResourceStore createRealmBackedStore(BeatmapInfo beatmapInfo, IStorageResourceProvider? resources) { - if (resources == null) + if (resources == null || beatmapInfo.BeatmapSet == null) // should only ever be used in tests. return new ResourceStore(); - return new RealmBackedResourceStore(beatmapInfo.BeatmapSet, resources.Files, new[] { @"ogg" }); + return new RealmBackedResourceStore(beatmapInfo.BeatmapSet.ToLive(resources.RealmAccess), resources.Files, resources.RealmAccess); } public override Drawable? GetDrawableComponent(ISkinComponent component) diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs index fc9036727f..115d563575 100644 --- a/osu.Game/Skinning/RealmBackedResourceStore.cs +++ b/osu.Game/Skinning/RealmBackedResourceStore.cs @@ -1,51 +1,68 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + +using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; using osu.Framework.Extensions; using osu.Framework.IO.Stores; using osu.Game.Database; using osu.Game.Extensions; +using Realms; namespace osu.Game.Skinning { - public class RealmBackedResourceStore : ResourceStore + public class RealmBackedResourceStore : ResourceStore + where T : RealmObject, IHasRealmFiles, IHasGuidPrimaryKey { - private readonly Dictionary fileToStoragePathMapping = new Dictionary(); + private Lazy> fileToStoragePathMapping; - public RealmBackedResourceStore(IHasRealmFiles source, IResourceStore underlyingStore, string[] extensions = null) + private readonly Live liveSource; + + public RealmBackedResourceStore(Live source, IResourceStore underlyingStore, RealmAccess? realm) : base(underlyingStore) { - // Must be initialised before the file cache. - if (extensions != null) - { - foreach (string extension in extensions) - AddExtension(extension); - } + liveSource = source; - initialiseFileCache(source); + invalidateCache(); + Debug.Assert(fileToStoragePathMapping != null); } - private void initialiseFileCache(IHasRealmFiles source) - { - fileToStoragePathMapping.Clear(); - foreach (var f in source.Files) - fileToStoragePathMapping[f.Filename.ToLowerInvariant()] = f.File.GetStoragePath(); - } + public void Invalidate() => invalidateCache(); protected override IEnumerable GetFilenames(string name) { foreach (string filename in base.GetFilenames(name)) { - string path = getPathForFile(filename.ToStandardisedPath()); + string? path = getPathForFile(filename.ToStandardisedPath()); if (path != null) yield return path; } } - private string getPathForFile(string filename) => - fileToStoragePathMapping.TryGetValue(filename.ToLower(), out string path) ? path : null; + private string? getPathForFile(string filename) + { + if (fileToStoragePathMapping.Value.TryGetValue(filename.ToLowerInvariant(), out string path)) + return path; - public override IEnumerable GetAvailableResources() => fileToStoragePathMapping.Keys; + return null; + } + + private void invalidateCache() => fileToStoragePathMapping = new Lazy>(initialiseFileCache, LazyThreadSafetyMode.ExecutionAndPublication); + + private Dictionary initialiseFileCache() => liveSource.PerformRead(source => + { + var dictionary = new Dictionary(); + dictionary.Clear(); + foreach (var f in source.Files) + dictionary[f.Filename.ToLowerInvariant()] = f.File.GetStoragePath(); + + return dictionary; + }); + + public override IEnumerable GetAvailableResources() => fileToStoragePathMapping.Value.Keys; } } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 2f01bb7301..fb9914cd9e 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -54,6 +54,10 @@ namespace osu.Game.Skinning where TLookup : notnull where TValue : notnull; + public void InvalidateCaches() => realmBackedStorage?.Invalidate(); + + private readonly RealmBackedResourceStore realmBackedStorage; + /// /// Construct a new skin. /// @@ -67,7 +71,9 @@ namespace osu.Game.Skinning { SkinInfo = skin.ToLive(resources.RealmAccess); - storage ??= new RealmBackedResourceStore(skin, resources.Files, new[] { @"ogg" }); + storage ??= realmBackedStorage = new RealmBackedResourceStore(SkinInfo, resources.Files, resources.RealmAccess); + + (storage as ResourceStore)?.AddExtension("ogg"); var samples = resources.AudioManager?.GetSampleStore(storage); if (samples != null) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 5333b58625..bafb088f68 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -16,6 +16,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Threading; @@ -26,6 +27,7 @@ using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Models; using osu.Game.Overlays.Notifications; +using Realms; namespace osu.Game.Skinning { @@ -59,6 +61,8 @@ namespace osu.Game.Skinning private readonly IResourceStore userFiles; + private IDisposable currentSkinSubscription; + /// /// The default skin. /// @@ -97,7 +101,16 @@ namespace osu.Game.Skinning } }); - CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = skin.NewValue.PerformRead(GetSkin); + CurrentSkinInfo.ValueChanged += skin => + { + CurrentSkin.Value = skin.NewValue.PerformRead(GetSkin); + + scheduler.Add(() => + { + currentSkinSubscription?.Dispose(); + currentSkinSubscription = realm.RegisterForNotifications(r => r.All().Where(s => s.ID == skin.NewValue.ID), realmSkinChanged); + }); + }; CurrentSkin.Value = DefaultSkin; CurrentSkin.ValueChanged += skin => @@ -109,6 +122,12 @@ namespace osu.Game.Skinning }; } + private void realmSkinChanged(IRealmCollection sender, ChangeSet changes, Exception error) where T : RealmObjectBase + { + Logger.Log("Detected a skin change"); + CurrentSkin.Value.InvalidateCaches(); + } + public void SelectRandomSkin() { realm.Run(r => From 762de3cc9780e76e744ea4e3c5d532e16117e4fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 22:53:49 +0900 Subject: [PATCH 0108/1276] Replace invalidation logic with local realm notification subscription --- osu.Game/Skinning/RealmBackedResourceStore.cs | 12 +++++++++++- osu.Game/Skinning/Skin.cs | 4 ++-- osu.Game/Skinning/SkinManager.cs | 16 ---------------- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs index 115d563575..e727a7e59a 100644 --- a/osu.Game/Skinning/RealmBackedResourceStore.cs +++ b/osu.Game/Skinning/RealmBackedResourceStore.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Threading; using osu.Framework.Extensions; using osu.Framework.IO.Stores; @@ -21,6 +22,7 @@ namespace osu.Game.Skinning private Lazy> fileToStoragePathMapping; private readonly Live liveSource; + private readonly IDisposable? realmSubscription; public RealmBackedResourceStore(Live source, IResourceStore underlyingStore, RealmAccess? realm) : base(underlyingStore) @@ -29,9 +31,17 @@ namespace osu.Game.Skinning invalidateCache(); Debug.Assert(fileToStoragePathMapping != null); + + realmSubscription = realm?.RegisterForNotifications(r => r.All().Where(s => s.ID == source.ID), skinChanged); } - public void Invalidate() => invalidateCache(); + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + realmSubscription?.Dispose(); + } + + private void skinChanged(IRealmCollection sender, ChangeSet changes, Exception error) => invalidateCache(); protected override IEnumerable GetFilenames(string name) { diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index fb9914cd9e..4cd1d952db 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -54,8 +54,6 @@ namespace osu.Game.Skinning where TLookup : notnull where TValue : notnull; - public void InvalidateCaches() => realmBackedStorage?.Invalidate(); - private readonly RealmBackedResourceStore realmBackedStorage; /// @@ -206,6 +204,8 @@ namespace osu.Game.Skinning Textures?.Dispose(); Samples?.Dispose(); + + realmBackedStorage?.Dispose(); } #endregion diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index bafb088f68..5e85f9e4ca 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -16,7 +16,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; -using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Threading; @@ -27,7 +26,6 @@ using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Models; using osu.Game.Overlays.Notifications; -using Realms; namespace osu.Game.Skinning { @@ -61,8 +59,6 @@ namespace osu.Game.Skinning private readonly IResourceStore userFiles; - private IDisposable currentSkinSubscription; - /// /// The default skin. /// @@ -104,12 +100,6 @@ namespace osu.Game.Skinning CurrentSkinInfo.ValueChanged += skin => { CurrentSkin.Value = skin.NewValue.PerformRead(GetSkin); - - scheduler.Add(() => - { - currentSkinSubscription?.Dispose(); - currentSkinSubscription = realm.RegisterForNotifications(r => r.All().Where(s => s.ID == skin.NewValue.ID), realmSkinChanged); - }); }; CurrentSkin.Value = DefaultSkin; @@ -122,12 +112,6 @@ namespace osu.Game.Skinning }; } - private void realmSkinChanged(IRealmCollection sender, ChangeSet changes, Exception error) where T : RealmObjectBase - { - Logger.Log("Detected a skin change"); - CurrentSkin.Value.InvalidateCaches(); - } - public void SelectRandomSkin() { realm.Run(r => From d1be229d74806794f8f136f3919885f915c118fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Mar 2022 17:31:03 +0900 Subject: [PATCH 0109/1276] Combine `SkinSprite` into `SkinnableSprite` --- .../Skinning/Components/SkinnableSprite.cs | 66 ------------------- osu.Game/Skinning/DefaultSkin.cs | 4 ++ osu.Game/Skinning/Editor/SkinEditor.cs | 5 +- osu.Game/Skinning/Skin.cs | 2 +- osu.Game/Skinning/SkinnableDrawable.cs | 8 +-- osu.Game/Skinning/SkinnableSprite.cs | 47 ++++++++++++- 6 files changed, 55 insertions(+), 77 deletions(-) delete mode 100644 osu.Game/Skinning/Components/SkinnableSprite.cs diff --git a/osu.Game/Skinning/Components/SkinnableSprite.cs b/osu.Game/Skinning/Components/SkinnableSprite.cs deleted file mode 100644 index aa23e428d1..0000000000 --- a/osu.Game/Skinning/Components/SkinnableSprite.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Linq; -using JetBrains.Annotations; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Configuration; -using osu.Game.Overlays.Settings; - -namespace osu.Game.Skinning.Components -{ - /// - /// Intended to be a test bed for skinning. May be removed at some point in the future. - /// - [UsedImplicitly] - public class SkinSprite : CompositeDrawable, ISkinnableDrawable - { - public bool UsesFixedAnchor { get; set; } - - [SettingSource("Sprite name", "The filename of the sprite", SettingControlType = typeof(SpriteSelectorControl))] - public Bindable SpriteName { get; } = new Bindable(string.Empty); - - [Resolved] - private ISkinSource source { get; set; } - - public IEnumerable AvailableFiles => (source.AllSources.First() as Skin)?.SkinInfo.PerformRead(s => s.Files.Select(f => f.Filename)); - - public SkinSprite() - { - AutoSizeAxes = Axes.Both; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - SpriteName.BindValueChanged(spriteName => - { - InternalChildren = new Drawable[] - { - new Sprite - { - Texture = source.GetTexture(SpriteName.Value), - } - }; - }, true); - } - - public class SpriteSelectorControl : SettingsDropdown - { - public SkinSprite Source { get; set; } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Items = Source.AvailableFiles; - } - } - } -} diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 43ada59bcb..c645b0fae4 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -8,6 +8,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Beatmaps.Formats; @@ -157,6 +158,9 @@ namespace osu.Game.Skinning break; } + if (GetTexture(component.LookupName) is Texture t) + return new Sprite { Texture = t }; + return null; } diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 392cb2f32b..484faebdc0 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -21,7 +21,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Edit.Components.Menus; -using osu.Game.Skinning.Components; namespace osu.Game.Skinning.Editor { @@ -349,9 +348,9 @@ namespace osu.Game.Skinning.Editor }); // place component - placeComponent(new SkinSprite + placeComponent(new SkinnableSprite { - SpriteName = { Value = file.Name } + SpriteName = { Value = Path.GetFileNameWithoutExtension(file.Name) } }); }); diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 4cd1d952db..f2d095f880 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -54,7 +54,7 @@ namespace osu.Game.Skinning where TLookup : notnull where TValue : notnull; - private readonly RealmBackedResourceStore realmBackedStorage; + private readonly RealmBackedResourceStore? realmBackedStorage; /// /// Construct a new skin. diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 72f64e2e12..45409694b5 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -31,7 +31,7 @@ namespace osu.Game.Skinning set => base.AutoSizeAxes = value; } - private readonly ISkinComponent component; + protected readonly ISkinComponent Component; private readonly ConfineMode confineMode; @@ -49,7 +49,7 @@ namespace osu.Game.Skinning protected SkinnableDrawable(ISkinComponent component, ConfineMode confineMode = ConfineMode.NoScaling) { - this.component = component; + Component = component; this.confineMode = confineMode; RelativeSizeAxes = Axes.Both; @@ -75,13 +75,13 @@ namespace osu.Game.Skinning protected override void SkinChanged(ISkinSource skin) { - Drawable = skin.GetDrawableComponent(component); + Drawable = skin.GetDrawableComponent(Component); isDefault = false; if (Drawable == null) { - Drawable = CreateDefault(component); + Drawable = CreateDefault(Component); isDefault = true; } diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index 38803bd8e3..aa3001fe45 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -1,10 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.IO; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Game.Configuration; +using osu.Game.Overlays.Settings; namespace osu.Game.Skinning { @@ -18,9 +24,32 @@ namespace osu.Game.Skinning [Resolved] private TextureStore textures { get; set; } + [SettingSource("Sprite name", "The filename of the sprite", SettingControlType = typeof(SpriteSelectorControl))] + public Bindable SpriteName { get; } = new Bindable(string.Empty); + + [Resolved] + private ISkinSource source { get; set; } + + public IEnumerable AvailableFiles => (source.AllSources.First() as Skin)?.SkinInfo.PerformRead(s => s.Files.Select(f => Path.GetFileNameWithoutExtension(f.Filename)).Distinct()); + public SkinnableSprite(string textureName, ConfineMode confineMode = ConfineMode.NoScaling) : base(new SpriteComponent(textureName), confineMode) { + SpriteName.Value = textureName; + } + + public SkinnableSprite() + : base(new SpriteComponent(string.Empty), ConfineMode.NoScaling) + { + RelativeSizeAxes = Axes.None; + AutoSizeAxes = Axes.Both; + + SpriteName.BindValueChanged(name => + { + ((SpriteComponent)Component).LookupName = name.NewValue ?? string.Empty; + if (IsLoaded) + SkinChanged(CurrentSkin); + }); } protected override Drawable CreateDefault(ISkinComponent component) @@ -33,16 +62,28 @@ namespace osu.Game.Skinning return new Sprite { Texture = texture }; } + public bool UsesFixedAnchor { get; set; } + private class SpriteComponent : ISkinComponent { + public string LookupName { get; set; } + public SpriteComponent(string textureName) { LookupName = textureName; } - - public string LookupName { get; } } - public bool UsesFixedAnchor { get; set; } + public class SpriteSelectorControl : SettingsDropdown + { + public SkinnableSprite Source { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Items = Source.AvailableFiles; + } + } } } From 52eeaffce333df069e325c9b4bc69b0ed00bc4d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Mar 2022 17:36:02 +0900 Subject: [PATCH 0110/1276] Limit lookup resources to images --- osu.Game/Skinning/SkinnableSprite.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index aa3001fe45..f36ae89e25 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -1,8 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; -using System.IO; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -30,7 +30,12 @@ namespace osu.Game.Skinning [Resolved] private ISkinSource source { get; set; } - public IEnumerable AvailableFiles => (source.AllSources.First() as Skin)?.SkinInfo.PerformRead(s => s.Files.Select(f => Path.GetFileNameWithoutExtension(f.Filename)).Distinct()); + public IEnumerable AvailableFiles => (source.AllSources.First() as Skin)?.SkinInfo.PerformRead(s => s.Files + .Where(f => + f.Filename.EndsWith(".png", StringComparison.Ordinal) + || f.Filename.EndsWith(".jpg", StringComparison.Ordinal) + ) + .Select(f => f.Filename).Distinct()); public SkinnableSprite(string textureName, ConfineMode confineMode = ConfineMode.NoScaling) : base(new SpriteComponent(textureName), confineMode) From 2b7105ac4fc0ab4663a5e7d3fd72c836028a4714 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 14:46:14 +0900 Subject: [PATCH 0111/1276] Add a default sprite representation to allow better placeholder display in skin editor toolbox --- osu.Game/Skinning/SkinnableSprite.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index f36ae89e25..0005045c00 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Configuration; using osu.Game.Overlays.Settings; +using osuTK; namespace osu.Game.Skinning { @@ -62,7 +63,13 @@ namespace osu.Game.Skinning var texture = textures.Get(component.LookupName); if (texture == null) - return null; + { + return new SpriteIcon + { + Size = new Vector2(100), + Icon = FontAwesome.Solid.QuestionCircle + }; + } return new Sprite { Texture = texture }; } @@ -87,7 +94,8 @@ namespace osu.Game.Skinning { base.LoadComplete(); - Items = Source.AvailableFiles; + if (Source.AvailableFiles.Any()) + Items = Source.AvailableFiles; } } } From 314ad63c6eee361142d0129bab387301bf442231 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 15:14:53 +0900 Subject: [PATCH 0112/1276] Simplify available file lookup and include file extension --- osu.Game/Skinning/Editor/SkinEditor.cs | 2 +- osu.Game/Skinning/SkinnableSprite.cs | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 484faebdc0..d36806a1b3 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -350,7 +350,7 @@ namespace osu.Game.Skinning.Editor // place component placeComponent(new SkinnableSprite { - SpriteName = { Value = Path.GetFileNameWithoutExtension(file.Name) } + SpriteName = { Value = file.Name } }); }); diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index 0005045c00..87490b4397 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -31,13 +30,6 @@ namespace osu.Game.Skinning [Resolved] private ISkinSource source { get; set; } - public IEnumerable AvailableFiles => (source.AllSources.First() as Skin)?.SkinInfo.PerformRead(s => s.Files - .Where(f => - f.Filename.EndsWith(".png", StringComparison.Ordinal) - || f.Filename.EndsWith(".jpg", StringComparison.Ordinal) - ) - .Select(f => f.Filename).Distinct()); - public SkinnableSprite(string textureName, ConfineMode confineMode = ConfineMode.NoScaling) : base(new SpriteComponent(textureName), confineMode) { @@ -88,14 +80,22 @@ namespace osu.Game.Skinning public class SpriteSelectorControl : SettingsDropdown { - public SkinnableSprite Source { get; set; } - protected override void LoadComplete() { base.LoadComplete(); - if (Source.AvailableFiles.Any()) - Items = Source.AvailableFiles; + // Round-about way of getting the user's skin to find available resources. + // In the future we'll probably want to allow access to resources from the fallbacks, or potentially other skins + // but that requires further thought. + var highestPrioritySkin = ((SkinnableSprite)Source).source.AllSources.First() as Skin; + + string[] availableFiles = highestPrioritySkin?.SkinInfo.PerformRead(s => s.Files + .Where(f => f.Filename.EndsWith(".png", StringComparison.Ordinal) + || f.Filename.EndsWith(".jpg", StringComparison.Ordinal)) + .Select(f => f.Filename).Distinct()).ToArray(); + + if (availableFiles?.Length > 0) + Items = availableFiles; } } } From bfd3406f5f69149a251112ce7f3d94ee81fecce0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 15:49:05 +0900 Subject: [PATCH 0113/1276] Ensure that file is imported and caches are invalidated before placing new sprites --- osu.Game/Skinning/Editor/SkinEditor.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index d36806a1b3..df0bb7a70c 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -48,6 +48,9 @@ namespace osu.Game.Skinning.Editor [Resolved] private OsuColour colours { get; set; } + [Resolved] + private RealmAccess realm { get; set; } + [Resolved(canBeNull: true)] private SkinEditorOverlay skinEditorOverlay { get; set; } @@ -347,6 +350,11 @@ namespace osu.Game.Skinning.Editor skins.AddFile(skinInfo, contents, file.Name); }); + // Even though we are 100% on an update thread, we need to wait for realm callbacks to fire (to correctly invalidate caches in RealmBackedResourceStore). + // See https://github.com/realm/realm-dotnet/discussions/2634#discussioncomment-2483573 for further discussion. + // This is the best we can do for now. + realm.Run(r => r.Refresh()); + // place component placeComponent(new SkinnableSprite { From 6afed5e865ff278970db643719f1377c8cb08660 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 16:00:51 +0900 Subject: [PATCH 0114/1276] Fix new `SettingsItem` attribute not playing well with non-`Drawable`s --- osu.Game/Configuration/SettingSourceAttribute.cs | 2 +- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- osu.Game/Skinning/SkinnableSprite.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index 8c84707b88..89f0e73f4f 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -88,7 +88,7 @@ namespace osu.Game.Configuration throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} had an unsupported custom control type ({controlType.ReadableName()})"); var control = (Drawable)Activator.CreateInstance(controlType); - controlType.GetProperty(nameof(SettingsItem.Source))?.SetValue(control, obj); + controlType.GetProperty(nameof(SettingsItem.SettingSourceObject))?.SetValue(control, obj); controlType.GetProperty(nameof(SettingsItem.LabelText))?.SetValue(control, attr.Label); controlType.GetProperty(nameof(SettingsItem.TooltipText))?.SetValue(control, attr.Description); controlType.GetProperty(nameof(SettingsItem.Current))?.SetValue(control, value); diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 6ac5351270..098090bf78 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Settings /// /// The source component if this was created via . /// - public Drawable Source { get; internal set; } + public object SettingSourceObject { get; internal set; } private IHasCurrentValue controlWithCurrent => Control as IHasCurrentValue; diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index 87490b4397..c6cc4c1bdd 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -87,7 +87,7 @@ namespace osu.Game.Skinning // Round-about way of getting the user's skin to find available resources. // In the future we'll probably want to allow access to resources from the fallbacks, or potentially other skins // but that requires further thought. - var highestPrioritySkin = ((SkinnableSprite)Source).source.AllSources.First() as Skin; + var highestPrioritySkin = ((SkinnableSprite)SettingSourceObject).source.AllSources.First() as Skin; string[] availableFiles = highestPrioritySkin?.SkinInfo.PerformRead(s => s.Files .Where(f => f.Filename.EndsWith(".png", StringComparison.Ordinal) From f0821ce1fc9ca72c3e1c1534e50ebd913e768a77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 16:16:49 +0900 Subject: [PATCH 0115/1276] Import new skin editor sprites to the cursor location --- osu.Game/Skinning/Editor/SkinEditor.cs | 25 +++++++++++++------ .../Skinning/Editor/SkinSelectionHandler.cs | 8 +++--- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index df0bb7a70c..0208f109da 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -254,7 +254,7 @@ namespace osu.Game.Skinning.Editor placeComponent(component); } - private void placeComponent(ISkinnableDrawable component) + private void placeComponent(ISkinnableDrawable component, bool applyDefaults = true) { var targetContainer = getFirstTarget(); @@ -263,10 +263,13 @@ namespace osu.Game.Skinning.Editor var drawableComponent = (Drawable)component; - // give newly added components a sane starting location. - drawableComponent.Origin = Anchor.TopCentre; - drawableComponent.Anchor = Anchor.TopCentre; - drawableComponent.Y = targetContainer.DrawSize.Y / 2; + if (applyDefaults) + { + // give newly added components a sane starting location. + drawableComponent.Origin = Anchor.TopCentre; + drawableComponent.Anchor = Anchor.TopCentre; + drawableComponent.Y = targetContainer.DrawSize.Y / 2; + } targetContainer.Add(component); @@ -356,10 +359,16 @@ namespace osu.Game.Skinning.Editor realm.Run(r => r.Refresh()); // place component - placeComponent(new SkinnableSprite + var sprite = new SkinnableSprite { - SpriteName = { Value = file.Name } - }); + SpriteName = { Value = file.Name }, + Origin = Anchor.Centre, + Position = getFirstTarget().ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position), + }; + + placeComponent(sprite, false); + + SkinSelectionHandler.ApplyClosestAnchor(sprite); }); return Task.CompletedTask; diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index d7fb5c0498..943425e099 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -157,13 +157,13 @@ namespace osu.Game.Skinning.Editor if (item.UsesFixedAnchor) continue; - applyClosestAnchor(drawable); + ApplyClosestAnchor(drawable); } return true; } - private static void applyClosestAnchor(Drawable drawable) => applyAnchor(drawable, getClosestAnchor(drawable)); + public static void ApplyClosestAnchor(Drawable drawable) => applyAnchor(drawable, getClosestAnchor(drawable)); protected override void OnSelectionChanged() { @@ -252,7 +252,7 @@ namespace osu.Game.Skinning.Editor if (item.UsesFixedAnchor) continue; - applyClosestAnchor(drawable); + ApplyClosestAnchor(drawable); } } @@ -279,7 +279,7 @@ namespace osu.Game.Skinning.Editor foreach (var item in SelectedItems) { item.UsesFixedAnchor = false; - applyClosestAnchor((Drawable)item); + ApplyClosestAnchor((Drawable)item); } } From 01681ee8749c3562672e20fe603597c39a467410 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 16:19:00 +0900 Subject: [PATCH 0116/1276] Add missing `ToArray` call Not sure where this went, was there in my original commit. --- osu.Game/Skinning/SkinManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 44b9c69794..832cb01d22 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -145,10 +145,10 @@ namespace osu.Game.Skinning if (!s.Protected) return; - var existingSkinNames = realm.Run(r => r.All() - .Where(skin => !skin.DeletePending) - .AsEnumerable() - .Select(skin => skin.Name)); + string[] existingSkinNames = realm.Run(r => r.All() + .Where(skin => !skin.DeletePending) + .AsEnumerable() + .Select(skin => skin.Name)).ToArray(); // if the user is attempting to save one of the default skin implementations, create a copy first. var skinInfo = new SkinInfo From fc3ebe9b510d34b5d75aae931076328b09851d9d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Apr 2022 10:40:16 +0300 Subject: [PATCH 0117/1276] Reword log retrieval steps on mobile platforms Co-authored-by: Dean Herbert --- .github/ISSUE_TEMPLATE/bug-issue.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index ea5ee298fb..91ca622f55 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -65,8 +65,8 @@ body: ### Mobile platforms The places to find the logs on mobile platforms are as follows: - - `Android/data/sh.ppy.osulazer/files/logs` *on Android* - - *On iOS*, they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer) + - *On Android*, navigate to `Android/data/sh.ppy.osulazer/files/logs` using a file browser app. + - *On iOS*, connect your device to a PC and copy the `logs` directory from the app's document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer) --- From 37dea0ff211db68d591a98a008cc3e4c76879ebb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Apr 2022 17:05:11 +0900 Subject: [PATCH 0118/1276] Add failing test case --- .../TestSceneMultiplayerPlaylist.cs | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index cbd8b472b8..f8c0939ea9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -13,6 +14,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Rulesets; @@ -183,14 +185,41 @@ namespace osu.Game.Tests.Visual.Multiplayer assertItemInHistoryListStep(2, 0); } + [Test] + public void TestInsertedItemDoesNotRefreshAllOthers() + { + AddStep("change to round robin queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayersRoundRobin }).WaitSafely()); + + // Add a few items for the local user. + addItemStep(); + addItemStep(); + addItemStep(); + addItemStep(); + addItemStep(); + + DrawableRoomPlaylistItem[] drawableItems = null; + AddStep("get drawable items", () => drawableItems = this.ChildrenOfType().ToArray()); + + // Add 1 item for another user. + AddStep("join second user", () => MultiplayerClient.AddUser(new APIUser { Id = 10 })); + addItemStep(userId: 10); + + // New item inserted towards the top of the list. + assertItemInQueueListStep(7, 1); + AddAssert("all previous playlist items remained", () => drawableItems.All(this.ChildrenOfType().Contains)); + } + /// /// Adds a step to create a new playlist item. /// - private void addItemStep(bool expired = false) => AddStep("add item", () => MultiplayerClient.AddPlaylistItem(new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap) + private void addItemStep(bool expired = false, int? userId = null) => AddStep("add item", () => { - Expired = expired, - PlayedAt = DateTimeOffset.Now - }))); + MultiplayerClient.AddUserPlaylistItem(userId ?? API.LocalUser.Value.OnlineID, new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap) + { + Expired = expired, + PlayedAt = DateTimeOffset.Now + })).WaitSafely(); + }); /// /// Asserts the position of a given playlist item in the queue list. From 16d4544ff9b4ca72fcd0899c9cd56a1a92f91246 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Apr 2022 17:06:37 +0900 Subject: [PATCH 0119/1276] Prevent reloads when playlist item order changes --- osu.Game/Online/Rooms/PlaylistItem.cs | 6 ++++-- .../Match/Playlist/MultiplayerPlaylist.cs | 20 +++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs index f696362cbb..a56851cfe6 100644 --- a/osu.Game/Online/Rooms/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Utils; namespace osu.Game.Online.Rooms { @@ -101,13 +102,13 @@ namespace osu.Game.Online.Rooms #endregion - public PlaylistItem With(IBeatmapInfo beatmap) => new PlaylistItem(beatmap) + public PlaylistItem With(Optional beatmap = default, Optional playlistOrder = default) => new PlaylistItem(beatmap.GetOr(Beatmap)) { ID = ID, OwnerID = OwnerID, RulesetID = RulesetID, Expired = Expired, - PlaylistOrder = PlaylistOrder, + PlaylistOrder = playlistOrder.GetOr(PlaylistOrder), PlayedAt = PlayedAt, AllowedMods = AllowedMods, RequiredMods = RequiredMods, @@ -119,6 +120,7 @@ namespace osu.Game.Online.Rooms && Beatmap.OnlineID == other.Beatmap.OnlineID && RulesetID == other.RulesetID && Expired == other.Expired + && PlaylistOrder == other.PlaylistOrder && AllowedMods.SequenceEqual(other.AllowedMods) && RequiredMods.SequenceEqual(other.RequiredMods); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs index 879a21e7c1..41f548a630 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs @@ -117,8 +117,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist { base.PlaylistItemChanged(item); - removeItemFromLists(item.ID); - addItemToLists(item); + var newApiItem = Playlist.SingleOrDefault(i => i.ID == item.ID); + var existingApiItemInQueue = queueList.Items.SingleOrDefault(i => i.ID == item.ID); + + // Test if the only change between the two playlist items is the order. + if (newApiItem != null && existingApiItemInQueue != null && existingApiItemInQueue.With(playlistOrder: newApiItem.PlaylistOrder).Equals(newApiItem)) + { + // Set the new playlist order directly without refreshing the DrawablePlaylistItem. + existingApiItemInQueue.PlaylistOrder = newApiItem.PlaylistOrder; + + // The following isn't really required, but is here for safety and explicitness. + // MultiplayerQueueList internally binds to changes in Playlist to invalidate its own layout, which is mutated on every playlist operation. + queueList.Invalidate(); + } + else + { + removeItemFromLists(item.ID); + addItemToLists(item); + } } private void addItemToLists(MultiplayerPlaylistItem item) From 6e6271d0c0ac4e00b0b2fd18b124e3cd4a0a6ca2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Apr 2022 18:31:17 +0900 Subject: [PATCH 0120/1276] Fix "server-side" room playlist not updated Remove unused using --- .../TestSceneMultiplayerPlaylist.cs | 1 - osu.Game/Online/Rooms/PlaylistItem.cs | 13 ++++++++ .../Multiplayer/TestMultiplayerClient.cs | 33 +++++++++++++++---- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index f8c0939ea9..1231866b36 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs index a56851cfe6..6ec884d79c 100644 --- a/osu.Game/Online/Rooms/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -85,6 +85,19 @@ namespace osu.Game.Online.Rooms Beatmap = beatmap; } + public PlaylistItem(MultiplayerPlaylistItem item) + : this(new APIBeatmap { OnlineID = item.BeatmapID }) + { + ID = item.ID; + OwnerID = item.OwnerID; + RulesetID = item.RulesetID; + Expired = item.Expired; + PlaylistOrder = item.PlaylistOrder; + PlayedAt = item.PlayedAt; + RequiredMods = item.RequiredMods.ToArray(); + AllowedMods = item.AllowedMods.ToArray(); + } + public void MarkInvalid() => valid.Value = false; #region Newtonsoft.Json implicit ShouldSerialize() methods diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index b9304f713d..0efaf16f99 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -31,7 +31,11 @@ namespace osu.Game.Tests.Visual.Multiplayer public override IBindable IsConnected => isConnected; private readonly Bindable isConnected = new Bindable(true); + /// + /// The local client's . This is not always equivalent to the server-side room. + /// public new Room? APIRoom => base.APIRoom; + public Action? RoomSetupAction; public bool RoomJoined { get; private set; } @@ -46,6 +50,11 @@ namespace osu.Game.Tests.Visual.Multiplayer /// private readonly List serverSidePlaylist = new List(); + /// + /// Guaranteed up-to-date API room. + /// + private Room? serverSideAPIRoom; + private MultiplayerPlaylistItem? currentItem => Room?.Playlist[currentIndex]; private int currentIndex; private long lastPlaylistItemId; @@ -192,13 +201,13 @@ namespace osu.Game.Tests.Visual.Multiplayer protected override async Task JoinRoom(long roomId, string? password = null) { - var apiRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId); + serverSideAPIRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId); - if (password != apiRoom.Password.Value) + if (password != serverSideAPIRoom.Password.Value) throw new InvalidOperationException("Invalid password."); serverSidePlaylist.Clear(); - serverSidePlaylist.AddRange(apiRoom.Playlist.Select(item => new MultiplayerPlaylistItem(item))); + serverSidePlaylist.AddRange(serverSideAPIRoom.Playlist.Select(item => new MultiplayerPlaylistItem(item))); lastPlaylistItemId = serverSidePlaylist.Max(item => item.ID); var localUser = new MultiplayerRoomUser(api.LocalUser.Value.Id) @@ -210,11 +219,11 @@ namespace osu.Game.Tests.Visual.Multiplayer { Settings = { - Name = apiRoom.Name.Value, - MatchType = apiRoom.Type.Value, + Name = serverSideAPIRoom.Name.Value, + MatchType = serverSideAPIRoom.Type.Value, Password = password, - QueueMode = apiRoom.QueueMode.Value, - AutoStartDuration = apiRoom.AutoStartDuration.Value + QueueMode = serverSideAPIRoom.QueueMode.Value, + AutoStartDuration = serverSideAPIRoom.AutoStartDuration.Value }, Playlist = serverSidePlaylist.ToList(), Users = { localUser }, @@ -479,6 +488,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Debug.Assert(Room != null); Debug.Assert(APIRoom != null); + Debug.Assert(serverSideAPIRoom != null); var item = serverSidePlaylist.Find(i => i.ID == playlistItemId); @@ -495,6 +505,7 @@ namespace osu.Game.Tests.Visual.Multiplayer throw new InvalidOperationException("Attempted to remove an item which has already been played."); serverSidePlaylist.Remove(item); + serverSideAPIRoom.Playlist.RemoveAll(i => i.ID == item.ID); await ((IMultiplayerClient)this).PlaylistItemRemoved(playlistItemId).ConfigureAwait(false); await updateCurrentItem(Room).ConfigureAwait(false); @@ -576,10 +587,12 @@ namespace osu.Game.Tests.Visual.Multiplayer private async Task addItem(MultiplayerPlaylistItem item) { Debug.Assert(Room != null); + Debug.Assert(serverSideAPIRoom != null); item.ID = ++lastPlaylistItemId; serverSidePlaylist.Add(item); + serverSideAPIRoom.Playlist.Add(new PlaylistItem(item)); await ((IMultiplayerClient)this).PlaylistItemAdded(item).ConfigureAwait(false); await updatePlaylistOrder(Room).ConfigureAwait(false); @@ -603,6 +616,8 @@ namespace osu.Game.Tests.Visual.Multiplayer private async Task updatePlaylistOrder(MultiplayerRoom room) { + Debug.Assert(serverSideAPIRoom != null); + List orderedActiveItems; switch (room.Settings.QueueMode) @@ -648,6 +663,10 @@ namespace osu.Game.Tests.Visual.Multiplayer await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false); } + + // Also ensure that the API room's playlist is correct. + foreach (var item in serverSideAPIRoom.Playlist) + item.PlaylistOrder = serverSidePlaylist.Single(i => i.ID == item.ID).PlaylistOrder; } } } From 43d03f28255858659aac9c2f5078593ba0ecfd24 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 19:30:16 +0900 Subject: [PATCH 0121/1276] Put `ToArray` call in correct place in brackets --- osu.Game/Skinning/SkinManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 832cb01d22..71920fb166 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -148,7 +148,7 @@ namespace osu.Game.Skinning string[] existingSkinNames = realm.Run(r => r.All() .Where(skin => !skin.DeletePending) .AsEnumerable() - .Select(skin => skin.Name)).ToArray(); + .Select(skin => skin.Name).ToArray()); // if the user is attempting to save one of the default skin implementations, create a copy first. var skinInfo = new SkinInfo From 0f4b75ab15241c71ac89389a19586cd04ee821b7 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 1 Apr 2022 21:33:57 +0900 Subject: [PATCH 0122/1276] Add multiplayer lobby countdown SFX --- .../Match/MultiplayerReadyButton.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 62be9ad3bd..8068e80534 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -5,6 +5,8 @@ using System; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Game.Graphics; @@ -27,6 +29,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [CanBeNull] private MultiplayerRoom room => multiplayerClient.Room; + private Sample countdownTickSample; + private Sample countdownTickFinalSample; + private int? lastTickPlayed; + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + countdownTickSample = audio.Samples.Get(@"Multiplayer/countdown-tick"); + countdownTickFinalSample = audio.Samples.Get(@"Multiplayer/countdown-tick-final"); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -83,6 +96,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match else countdownRemaining = countdown.TimeRemaining - timeElapsed; + if (countdownRemaining.Seconds <= 10 && (lastTickPlayed == null || lastTickPlayed != countdownRemaining.Seconds)) + { + countdownTickSample?.Play(); + + if (countdownRemaining.Seconds <= 3) + countdownTickFinalSample?.Play(); + + lastTickPlayed = countdownRemaining.Seconds; + } + string countdownText = $"Starting in {countdownRemaining:mm\\:ss}"; switch (localUser?.State) From b07152a119d3df101600ff18356c476ffdce9326 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 02:35:37 +0200 Subject: [PATCH 0123/1276] Initial attempt at writing a test for the toolbarClock I'll add more once I know if this code passes review or not --- .../Visual/Menus/TestSceneToolbarClock.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index 064d6f82fd..0b5904aa2b 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -16,6 +16,7 @@ namespace osu.Game.Tests.Visual.Menus public class TestSceneToolbarClock : OsuManualInputManagerTestScene { private readonly Container mainContainer; + private readonly ToolbarClock toolbarClock; public TestSceneToolbarClock() { @@ -49,7 +50,7 @@ namespace osu.Game.Tests.Visual.Menus RelativeSizeAxes = Axes.Y, Width = 2, }, - new ToolbarClock(), + toolbarClock = new ToolbarClock(), new Box { Colour = Color4.DarkRed, @@ -76,5 +77,22 @@ namespace osu.Game.Tests.Visual.Menus { AddStep("Set game time long", () => mainContainer.Clock = new FramedOffsetClock(Clock, false) { Offset = 3600.0 * 24 * 1000 * 98 }); } + + [Test] + public void TestHoverBackground() + { + Box hoverBackground = null; + + AddStep("Retrieve hover background", () => hoverBackground = (Box)toolbarClock.Children[0]); + + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(mainContainer, new Vector2(0,200))); + AddAssert("Hover background is not visible", () => hoverBackground.Alpha == 0); + + AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(mainContainer)); + AddAssert("Hover background is visible", () => hoverBackground.Alpha != 0); + + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(mainContainer, new Vector2(0,200))); + AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); + } } } From 6685c97147a92e71fa27b8632a69ae592ad535f4 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 02:43:44 +0200 Subject: [PATCH 0124/1276] mainContainer -> toolbarClock --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index 0b5904aa2b..a17e4030ff 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -85,13 +85,13 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Retrieve hover background", () => hoverBackground = (Box)toolbarClock.Children[0]); - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(mainContainer, new Vector2(0,200))); + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); AddAssert("Hover background is not visible", () => hoverBackground.Alpha == 0); - AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(mainContainer)); + AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(toolbarClock)); AddAssert("Hover background is visible", () => hoverBackground.Alpha != 0); - - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(mainContainer, new Vector2(0,200))); + + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); } } From dc744f18ff7700d1cadd4ac9ad7cfa883a3ecabc Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 02:43:51 +0200 Subject: [PATCH 0125/1276] Trim whitespace --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index a17e4030ff..b20ec7cbe5 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -87,10 +87,8 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); AddAssert("Hover background is not visible", () => hoverBackground.Alpha == 0); - AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(toolbarClock)); AddAssert("Hover background is visible", () => hoverBackground.Alpha != 0); - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); } From 9350f6f5f841684c70864ea64b059e29b0001f95 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 02:59:07 +0200 Subject: [PATCH 0126/1276] Add spaces around commas in Vector2 construction --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index b20ec7cbe5..df61e56011 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -85,11 +85,11 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Retrieve hover background", () => hoverBackground = (Box)toolbarClock.Children[0]); - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0, 200))); AddAssert("Hover background is not visible", () => hoverBackground.Alpha == 0); AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(toolbarClock)); AddAssert("Hover background is visible", () => hoverBackground.Alpha != 0); - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0, 200))); AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); } } From 95ccac50d4c1f2dfbeefbea492f69f3b06979830 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 04:26:16 +0200 Subject: [PATCH 0127/1276] Add display mode changing test Yup this is gonna fail horribly --- .../Visual/Menus/TestSceneToolbarClock.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index df61e56011..7643e5032b 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -2,10 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Timing; +using osu.Game.Configuration; using osu.Game.Overlays.Toolbar; using osuTK; using osuTK.Graphics; @@ -15,6 +18,8 @@ namespace osu.Game.Tests.Visual.Menus [TestFixture] public class TestSceneToolbarClock : OsuManualInputManagerTestScene { + private Bindable clockDisplayMode; + private readonly Container mainContainer; private readonly ToolbarClock toolbarClock; @@ -66,6 +71,12 @@ namespace osu.Game.Tests.Visual.Menus AddSliderStep("scale", 0.5, 4, 1, scale => mainContainer.Scale = new Vector2((float)scale)); } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + clockDisplayMode = config.GetBindable(OsuSetting.ToolbarClockDisplayMode); + } + [Test] public void TestRealGameTime() { @@ -92,5 +103,22 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0, 200))); AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); } + + [Test] + public void TestDisplayModeChange() + { + ToolbarClockDisplayMode initialDisplayMode = 0; + + AddStep("Retrieve current state", () => initialDisplayMode = (ToolbarClockDisplayMode)clockDisplayMode.Value); + + AddStep("Trigger click", () => toolbarClock.TriggerClick()); + AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddStep("Trigger click", () => toolbarClock.TriggerClick()); + AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddStep("Trigger click", () => toolbarClock.TriggerClick()); + AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddStep("Trigger click", () => toolbarClock.TriggerClick()); + AddAssert("State is equal to initial", () => clockDisplayMode.Value == initialDisplayMode); + } } } From 245e452d41fcc2af69007f36f1f33122f2805bd5 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 04:31:43 +0200 Subject: [PATCH 0128/1276] Remove redundant typecast I accidentally left in (Thanks InspectCode) --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index 7643e5032b..c1b8cd919d 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.Menus { ToolbarClockDisplayMode initialDisplayMode = 0; - AddStep("Retrieve current state", () => initialDisplayMode = (ToolbarClockDisplayMode)clockDisplayMode.Value); + AddStep("Retrieve current state", () => initialDisplayMode = clockDisplayMode.Value); AddStep("Trigger click", () => toolbarClock.TriggerClick()); AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); From ee9696855ba9e1098d33353179f903283a2cb69a Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 04:41:05 +0200 Subject: [PATCH 0129/1276] Remove hover test --- .../Visual/Menus/TestSceneToolbarClock.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index c1b8cd919d..269b3cbad0 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -89,21 +89,6 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Set game time long", () => mainContainer.Clock = new FramedOffsetClock(Clock, false) { Offset = 3600.0 * 24 * 1000 * 98 }); } - [Test] - public void TestHoverBackground() - { - Box hoverBackground = null; - - AddStep("Retrieve hover background", () => hoverBackground = (Box)toolbarClock.Children[0]); - - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0, 200))); - AddAssert("Hover background is not visible", () => hoverBackground.Alpha == 0); - AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(toolbarClock)); - AddAssert("Hover background is visible", () => hoverBackground.Alpha != 0); - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0, 200))); - AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); - } - [Test] public void TestDisplayModeChange() { From a1baced7774603a400e74c191ec6f3811a66f728 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 04:45:21 +0200 Subject: [PATCH 0130/1276] Change behavior of the display mode test I remembered to run InspectCode this time, all good --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index 269b3cbad0..2cf73f5442 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -92,12 +92,10 @@ namespace osu.Game.Tests.Visual.Menus [Test] public void TestDisplayModeChange() { - ToolbarClockDisplayMode initialDisplayMode = 0; - - AddStep("Retrieve current state", () => initialDisplayMode = clockDisplayMode.Value); + AddStep("Set clock display mode", () => clockDisplayMode.Value = ToolbarClockDisplayMode.Full); AddStep("Trigger click", () => toolbarClock.TriggerClick()); - AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddAssert("State is digital with runtime", () => clockDisplayMode.Value == ToolbarClockDisplayMode.DigitalWithRuntime); AddStep("Trigger click", () => toolbarClock.TriggerClick()); AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); AddStep("Trigger click", () => toolbarClock.TriggerClick()); From c103cee1d5dce8e5b157f594ebc9c9af0825363e Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 04:53:47 +0200 Subject: [PATCH 0131/1276] Need to commit the second half again because my Git UI messed up --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index 2cf73f5442..87d836687f 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -97,11 +97,11 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Trigger click", () => toolbarClock.TriggerClick()); AddAssert("State is digital with runtime", () => clockDisplayMode.Value == ToolbarClockDisplayMode.DigitalWithRuntime); AddStep("Trigger click", () => toolbarClock.TriggerClick()); - AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddAssert("State is digital", () => clockDisplayMode.Value == ToolbarClockDisplayMode.Digital); AddStep("Trigger click", () => toolbarClock.TriggerClick()); - AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddAssert("State is analog", () => clockDisplayMode.Value == ToolbarClockDisplayMode.Analog); AddStep("Trigger click", () => toolbarClock.TriggerClick()); - AddAssert("State is equal to initial", () => clockDisplayMode.Value == initialDisplayMode); + AddAssert("State is full", () => clockDisplayMode.Value == ToolbarClockDisplayMode.Full); } } } From a252c4cad57cae5472adb69101e288e46ab2c18c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 18:08:23 +0300 Subject: [PATCH 0132/1276] Add random pass/play count data in test scene --- .../Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs index be3fc7aff9..82b34c50c2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs @@ -71,7 +71,9 @@ namespace osu.Game.Tests.Visual.Online { Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), - } + }, + PassCount = RNG.Next(0, 999), + PlayCount = RNG.Next(1000, 1999), }; } From ced5be33e6ac062126ba7352c31eddf00683f303 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 18:08:57 +0300 Subject: [PATCH 0133/1276] Display pass/play count in success rate percentage tooltip --- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index e08f099226..fed3d7ddaa 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -5,6 +5,8 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -19,7 +21,7 @@ namespace osu.Game.Overlays.BeatmapSet protected readonly FailRetryGraph Graph; private readonly FillFlowContainer header; - private readonly OsuSpriteText successPercent; + private readonly SuccessRatePercentage successPercent; private readonly Bar successRate; private readonly Container percentContainer; @@ -45,6 +47,7 @@ namespace osu.Game.Overlays.BeatmapSet float rate = playCount != 0 ? (float)passCount / playCount : 0; successPercent.Text = rate.ToLocalisableString(@"0.#%"); + successPercent.TooltipText = $"{passCount} / {playCount}"; successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); @@ -80,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapSet RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Width = 0f, - Child = successPercent = new OsuSpriteText + Child = successPercent = new SuccessRatePercentage { Anchor = Anchor.TopRight, Origin = Anchor.TopCentre, @@ -121,5 +124,10 @@ namespace osu.Game.Overlays.BeatmapSet Graph.Padding = new MarginPadding { Top = header.DrawHeight }; } + + private class SuccessRatePercentage : OsuSpriteText, IHasTooltip + { + public LocalisableString TooltipText { get; set; } + } } } From c4635f3c03f1ef7aac5a153459d0b55d8e068259 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 18:36:32 +0300 Subject: [PATCH 0134/1276] Add failing test case --- .../Online/TestSceneBeatmapListingOverlay.cs | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index a056e0cd2c..159c49ebea 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -5,9 +5,11 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Beatmaps.Drawables.Cards; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; @@ -29,6 +31,14 @@ namespace osu.Game.Tests.Visual.Online private BeatmapListingSearchControl searchControl => overlay.ChildrenOfType().Single(); + private OsuConfigManager localConfig; + + [BackgroundDependencyLoader] + private void load() + { + Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage)); + } + [SetUpSteps] public void SetUpSteps() { @@ -61,6 +71,8 @@ namespace osu.Game.Tests.Visual.Online Id = API.LocalUser.Value.Id + 1, }; }); + + AddStep("reset size", () => localConfig.SetValue(OsuSetting.BeatmapListingCardSize, BeatmapCardSize.Normal)); } [Test] @@ -120,24 +132,25 @@ namespace osu.Game.Tests.Visual.Online assertAllCardsOfType(30); } - [Test] - public void TestCardSizeSwitching() + [TestCase(false)] + [TestCase(true)] + public void TestCardSizeSwitching(bool viaConfig) { AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 100).ToArray())); assertAllCardsOfType(100); - setCardSize(BeatmapCardSize.Extra); + setCardSize(BeatmapCardSize.Extra, viaConfig); assertAllCardsOfType(100); - setCardSize(BeatmapCardSize.Normal); + setCardSize(BeatmapCardSize.Normal, viaConfig); assertAllCardsOfType(100); AddStep("fetch for 0 beatmaps", () => fetchFor()); AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); - setCardSize(BeatmapCardSize.Extra); + setCardSize(BeatmapCardSize.Extra, viaConfig); AddAssert("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); } @@ -361,7 +374,13 @@ namespace osu.Game.Tests.Visual.Online AddUntilStep("\"no maps found\" placeholder not shown", () => !overlay.ChildrenOfType().Any(d => d.IsPresent)); } - private void setCardSize(BeatmapCardSize cardSize) => AddStep($"set card size to {cardSize}", () => overlay.ChildrenOfType().Single().Current.Value = cardSize); + private void setCardSize(BeatmapCardSize cardSize, bool viaConfig) => AddStep($"set card size to {cardSize}", () => + { + if (!viaConfig) + overlay.ChildrenOfType().Single().Current.Value = cardSize; + else + localConfig.SetValue(OsuSetting.BeatmapListingCardSize, cardSize); + }); private void assertAllCardsOfType(int expectedCount) where T : BeatmapCard => @@ -370,5 +389,11 @@ namespace osu.Game.Tests.Visual.Online int loadedCorrectCount = this.ChildrenOfType().Count(card => card.IsLoaded && card.GetType() == typeof(T)); return loadedCorrectCount > 0 && loadedCorrectCount == expectedCount; }); + + protected override void Dispose(bool isDisposing) + { + localConfig?.Dispose(); + base.Dispose(isDisposing); + } } } From beb8426d3b866743fdaebf84e82d2e518bc3769c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 18:36:49 +0300 Subject: [PATCH 0135/1276] Save beatmap listing card size to game config --- osu.Game/Configuration/OsuConfigManager.cs | 4 ++++ .../BeatmapListing/BeatmapListingFilterControl.cs | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index e8f13ba902..2f966ac0a9 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -10,6 +10,7 @@ using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Game.Beatmaps.Drawables.Cards; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.Localisation; @@ -44,6 +45,8 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f); + SetDefault(OsuSetting.BeatmapListingCardSize, BeatmapCardSize.Normal); + SetDefault(OsuSetting.ToolbarClockDisplayMode, ToolbarClockDisplayMode.Full); // Online settings @@ -297,6 +300,7 @@ namespace osu.Game.Configuration RandomSelectAlgorithm, ShowFpsDisplay, ChatDisplayHeight, + BeatmapListingCardSize, ToolbarClockDisplayMode, Version, ShowConvertedBeatmaps, diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 0f87f04270..e4628e3723 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Game.Beatmaps.Drawables.Cards; +using osu.Game.Configuration; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -53,7 +54,9 @@ namespace osu.Game.Overlays.BeatmapListing /// /// The currently selected . /// - public IBindable CardSize { get; } = new Bindable(); + public IBindable CardSize => cardSize; + + private readonly Bindable cardSize = new Bindable(); private readonly BeatmapListingSearchControl searchControl; private readonly BeatmapListingSortTabControl sortControl; @@ -128,6 +131,9 @@ namespace osu.Game.Overlays.BeatmapListing }; } + [Resolved] + private OsuConfigManager config { get; set; } + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, IAPIProvider api) { @@ -141,6 +147,8 @@ namespace osu.Game.Overlays.BeatmapListing { base.LoadComplete(); + config.BindWith(OsuSetting.BeatmapListingCardSize, cardSize); + var sortCriteria = sortControl.Current; var sortDirection = sortControl.SortDirection; From b9421d1415af7e15c07b3f7237603170ea4a7a92 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 2 Apr 2022 17:14:27 +0100 Subject: [PATCH 0136/1276] Simplify text clear and placeholder change in `ChatTextBox` --- osu.Game/Overlays/Chat/ChatTextBox.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBox.cs b/osu.Game/Overlays/Chat/ChatTextBox.cs index 35ed26cda3..e0f949caba 100644 --- a/osu.Game/Overlays/Chat/ChatTextBox.cs +++ b/osu.Game/Overlays/Chat/ChatTextBox.cs @@ -20,8 +20,10 @@ namespace osu.Game.Overlays.Chat ShowSearch.BindValueChanged(change => { - PlaceholderText = change.NewValue ? "type here to search" : "type here"; - Schedule(() => Text = string.Empty); + bool showSearch = change.NewValue; + + PlaceholderText = showSearch ? "type here to search" : "type here"; + Text = string.Empty; }, true); } From 2297073b7eec019a1553735e7fd846f9488aad34 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 2 Apr 2022 17:15:19 +0100 Subject: [PATCH 0137/1276] Use `OnChatMessageCommit` & `OnSearchTermsChanged` events in `ChatTextBar` --- .../Visual/Online/TestSceneChatTextBox.cs | 39 ++++++++++++++++--- osu.Game/Overlays/Chat/ChatTextBar.cs | 31 +++++++++++++-- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs index e72a1d6652..982fbc397d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -25,6 +25,7 @@ namespace osu.Game.Tests.Visual.Online private readonly Bindable currentChannel = new Bindable(); private OsuSpriteText commitText; + private OsuSpriteText searchText; private ChatTextBar bar; [SetUp] @@ -47,11 +48,32 @@ namespace osu.Game.Tests.Visual.Online { new Drawable[] { - commitText = new OsuSpriteText + new GridContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Font = OsuFont.Default.With(size: 20), + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + commitText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = OsuFont.Default.With(size: 20), + }, + searchText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = OsuFont.Default.With(size: 20), + }, + }, + }, }, }, new Drawable[] @@ -66,12 +88,17 @@ namespace osu.Game.Tests.Visual.Online }, }; - bar.TextBox.OnCommit += (sender, newText) => + bar.OnChatMessageCommit += (sender, newText) => { - commitText.Text = $"Commit: {sender.Text}"; + commitText.Text = $"OnChatMessageCommit: {sender.Text}"; commitText.FadeOutFromOne(1000, Easing.InQuint); sender.Text = string.Empty; }; + + bar.OnSearchTermsChanged += (text) => + { + searchText.Text = $"OnSearchTermsChanged: {text}"; + }; }); } diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index d7edbb83b6..51c74ee0a5 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -3,15 +3,15 @@ #nullable enable +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; +using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; using osuTK; @@ -21,13 +21,16 @@ namespace osu.Game.Overlays.Chat { public readonly BindableBool ShowSearch = new BindableBool(); - public ChatTextBox TextBox { get; private set; } = null!; + public event TextBox.OnCommitHandler? OnChatMessageCommit; + + public event Action? OnSearchTermsChanged; [Resolved] private Bindable currentChannel { get; set; } = null!; private OsuTextFlowContainer chattingTextContainer = null!; private Container searchIconContainer = null!; + private ChatTextBox chatTextBox = null!; private const float chatting_text_width = 180; private const float search_icon_width = 40; @@ -86,7 +89,7 @@ namespace osu.Game.Overlays.Chat { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 5 }, - Child = TextBox = new ChatTextBox + Child = chatTextBox = new ChatTextBox { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -106,12 +109,20 @@ namespace osu.Game.Overlays.Chat { base.LoadComplete(); + chatTextBox.Current.ValueChanged += chatTextBoxChange; + chatTextBox.OnCommit += chatTextBoxCommit; + ShowSearch.BindValueChanged(change => { bool showSearch = change.NewValue; chattingTextContainer.FadeTo(showSearch ? 0 : 1); searchIconContainer.FadeTo(showSearch ? 1 : 0); + + // Clear search terms if any exist when switching back to chat mode + if (!showSearch) + OnSearchTermsChanged?.Invoke(string.Empty); + }, true); currentChannel.BindValueChanged(change => @@ -134,5 +145,17 @@ namespace osu.Game.Overlays.Chat } }, true); } + + private void chatTextBoxChange(ValueChangedEvent change) + { + if (ShowSearch.Value) + OnSearchTermsChanged?.Invoke(change.NewValue); + } + + private void chatTextBoxCommit(TextBox sender, bool newText) + { + if (!ShowSearch.Value) + OnChatMessageCommit?.Invoke(sender, newText); + } } } From 8534dd34632508c05edb0fd6132aca68023c845f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 19:24:16 +0300 Subject: [PATCH 0138/1276] Simplify `TestCase` attributes to one `Values` attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Visual/Online/TestSceneBeatmapListingOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 159c49ebea..9a7bd17902 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -132,9 +132,8 @@ namespace osu.Game.Tests.Visual.Online assertAllCardsOfType(30); } - [TestCase(false)] - [TestCase(true)] - public void TestCardSizeSwitching(bool viaConfig) + [Test] + public void TestCardSizeSwitching([Values] bool viaConfig) { AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); From 9e152cd3fd8aa48354e8bc34bfe3589d2ba77ad4 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 2 Apr 2022 17:27:44 +0100 Subject: [PATCH 0139/1276] Fix code quality issues --- osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs | 2 +- osu.Game/Overlays/Chat/ChatTextBar.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs index 982fbc397d..42d4a8efbd 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Online sender.Text = string.Empty; }; - bar.OnSearchTermsChanged += (text) => + bar.OnSearchTermsChanged += text => { searchText.Text = $"OnSearchTermsChanged: {text}"; }; diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 51c74ee0a5..0eb8056d76 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -122,7 +122,6 @@ namespace osu.Game.Overlays.Chat // Clear search terms if any exist when switching back to chat mode if (!showSearch) OnSearchTermsChanged?.Invoke(string.Empty); - }, true); currentChannel.BindValueChanged(change => From b815f685fc67d869fa99ebd65a8ae49972122a22 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 19:28:33 +0300 Subject: [PATCH 0140/1276] Flip `viaConfig` conditional branch --- .../Visual/Online/TestSceneBeatmapListingOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 9a7bd17902..5999125013 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -375,10 +375,10 @@ namespace osu.Game.Tests.Visual.Online private void setCardSize(BeatmapCardSize cardSize, bool viaConfig) => AddStep($"set card size to {cardSize}", () => { - if (!viaConfig) - overlay.ChildrenOfType().Single().Current.Value = cardSize; - else + if (viaConfig) localConfig.SetValue(OsuSetting.BeatmapListingCardSize, cardSize); + else + overlay.ChildrenOfType().Single().Current.Value = cardSize; }); private void assertAllCardsOfType(int expectedCount) From 0f9461689085cb790de5a3850d051a344d3f0fc2 Mon Sep 17 00:00:00 2001 From: Ame Date: Sat, 2 Apr 2022 19:41:15 +0200 Subject: [PATCH 0141/1276] Make overlay shortcuts able to be toggled instead of repeatable --- osu.Game/OsuGame.cs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 73121f6e7d..3703318e81 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -95,6 +95,8 @@ namespace osu.Game private SkinEditorOverlay skinEditor; + private NowPlayingOverlay nowPlayingOverlay; + private Container overlayContent; private Container rightFloatingOverlayContent; @@ -818,7 +820,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(new NowPlayingOverlay + loadComponentSingleFile(nowPlayingOverlay = new NowPlayingOverlay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -1069,6 +1071,26 @@ namespace osu.Game SkinManager.SelectRandomSkin(); return true; + + case GlobalAction.ToggleChat: + chatOverlay.ToggleVisibility(); + return true; + + case GlobalAction.ToggleSocial: + dashboard.ToggleVisibility(); + return true; + + case GlobalAction.ToggleNowPlaying: + nowPlayingOverlay.ToggleVisibility(); + return true; + + case GlobalAction.ToggleBeatmapListing: + beatmapListing.ToggleVisibility(); + return true; + + case GlobalAction.ToggleSettings: + Settings.ToggleVisibility(); + return true; } return false; From 35629e9be815c41788633363c38ec9f6450669ea Mon Sep 17 00:00:00 2001 From: Ame Date: Sat, 2 Apr 2022 20:45:00 +0200 Subject: [PATCH 0142/1276] General fix for ToolbarButton toggle repetition --- osu.Game/OsuGame.cs | 24 +--------------------- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3703318e81..73121f6e7d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -95,8 +95,6 @@ namespace osu.Game private SkinEditorOverlay skinEditor; - private NowPlayingOverlay nowPlayingOverlay; - private Container overlayContent; private Container rightFloatingOverlayContent; @@ -820,7 +818,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(nowPlayingOverlay = new NowPlayingOverlay + loadComponentSingleFile(new NowPlayingOverlay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -1071,26 +1069,6 @@ namespace osu.Game SkinManager.SelectRandomSkin(); return true; - - case GlobalAction.ToggleChat: - chatOverlay.ToggleVisibility(); - return true; - - case GlobalAction.ToggleSocial: - dashboard.ToggleVisibility(); - return true; - - case GlobalAction.ToggleNowPlaying: - nowPlayingOverlay.ToggleVisibility(); - return true; - - case GlobalAction.ToggleBeatmapListing: - beatmapListing.ToggleVisibility(); - return true; - - case GlobalAction.ToggleSettings: - Settings.ToggleVisibility(); - return true; } return false; diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index c855b76680..670ce65c6b 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -190,7 +190,7 @@ namespace osu.Game.Overlays.Toolbar public bool OnPressed(KeyBindingPressEvent e) { - if (e.Action == Hotkey) + if (!e.Repeat && e.Action == Hotkey) { TriggerClick(); return true; From 01b10e68d2fc8de98912f715bb5e02599d4ba9a9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 23:47:20 +0300 Subject: [PATCH 0143/1276] Adjust taiko hit objects sizes to match osu!(stable) --- osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index f047c03f4b..1a1fde1990 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Objects /// /// Default size of a drawable taiko hit object. /// - public const float DEFAULT_SIZE = 0.45f; + public const float DEFAULT_SIZE = 0.475f; public override Judgement CreateJudgement() => new TaikoJudgement(); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index 6c17573b50..43a099b900 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Objects /// /// Scale multiplier for a strong drawable taiko hit object. /// - public const float STRONG_SCALE = 1.4f; + public const float STRONG_SCALE = 1.525f; /// /// Default size of a strong drawable taiko hit object. From 94fa5e2ef20ab519484638d34be6bbb5238a256c Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 2 Apr 2022 21:58:54 +0100 Subject: [PATCH 0144/1276] Use `Action` for event `OnChatMessageCommitted` & clear textbox internally --- osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs | 5 ++--- osu.Game/Overlays/Chat/ChatTextBar.cs | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs index 42d4a8efbd..e5dd492183 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -88,11 +88,10 @@ namespace osu.Game.Tests.Visual.Online }, }; - bar.OnChatMessageCommit += (sender, newText) => + bar.OnChatMessageCommitted += text => { - commitText.Text = $"OnChatMessageCommit: {sender.Text}"; + commitText.Text = $"OnChatMessageCommitted: {text}"; commitText.FadeOutFromOne(1000, Easing.InQuint); - sender.Text = string.Empty; }; bar.OnSearchTermsChanged += text => diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 0eb8056d76..531c417abc 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Chat { public readonly BindableBool ShowSearch = new BindableBool(); - public event TextBox.OnCommitHandler? OnChatMessageCommit; + public event Action? OnChatMessageCommitted; public event Action? OnSearchTermsChanged; @@ -154,7 +154,9 @@ namespace osu.Game.Overlays.Chat private void chatTextBoxCommit(TextBox sender, bool newText) { if (!ShowSearch.Value) - OnChatMessageCommit?.Invoke(sender, newText); + OnChatMessageCommitted?.Invoke(sender.Text); + + sender.Text = string.Empty; } } } From 4ce69890d4fbad0f7990fe6ceb645cf05ae5252a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Apr 2022 00:21:23 +0300 Subject: [PATCH 0145/1276] Use `TaikoHitObject.DEFAULT_SIZE` for default circle piece symbol size --- osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs index a106c4f629..f2452ad88c 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Taiko.Objects; using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.Skinning.Default @@ -24,8 +25,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default /// public abstract class CirclePiece : BeatSyncedContainer, IHasAccentColour { - public const float SYMBOL_SIZE = 0.45f; + public const float SYMBOL_SIZE = TaikoHitObject.DEFAULT_SIZE; public const float SYMBOL_BORDER = 8; + private const double pre_beat_transition_time = 80; private Color4 accentColour; From 534cc18ff9ff6d1a8f5726bef122a3ff4c79c006 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Apr 2022 01:21:48 +0300 Subject: [PATCH 0146/1276] Adjust osu!taiko legacy hit target size to match osu!(stable) --- .../Skinning/Legacy/TaikoLegacyHitTarget.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs index 9feb2054da..c4657fcc49 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy new Sprite { Texture = skin.GetTexture("approachcircle"), - Scale = new Vector2(0.73f), + Scale = new Vector2(0.83f), Alpha = 0.47f, // eyeballed to match stable Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy new Sprite { Texture = skin.GetTexture("taikobigcircle"), - Scale = new Vector2(0.7f), + Scale = new Vector2(0.8f), Alpha = 0.22f, // eyeballed to match stable Anchor = Anchor.Centre, Origin = Anchor.Centre, From 970b1951ace07653526d5633f7975019ba006b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 14:23:45 +0200 Subject: [PATCH 0147/1276] Rewrite logic slightly to better convey meaning of textbox clear --- osu.Game/Overlays/Chat/ChatTextBar.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 531c417abc..ef20149dac 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -153,9 +153,10 @@ namespace osu.Game.Overlays.Chat private void chatTextBoxCommit(TextBox sender, bool newText) { - if (!ShowSearch.Value) - OnChatMessageCommitted?.Invoke(sender.Text); + if (ShowSearch.Value) + return; + OnChatMessageCommitted?.Invoke(sender.Text); sender.Text = string.Empty; } } From 6d1844adc3a9b01b8d33a07bdf56fcdc6d671c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 14:27:37 +0200 Subject: [PATCH 0148/1276] Use `nameof()` in test to reference event names --- osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs index e5dd492183..a241aa0517 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -90,13 +90,13 @@ namespace osu.Game.Tests.Visual.Online bar.OnChatMessageCommitted += text => { - commitText.Text = $"OnChatMessageCommitted: {text}"; + commitText.Text = $"{nameof(bar.OnChatMessageCommitted)}: {text}"; commitText.FadeOutFromOne(1000, Easing.InQuint); }; bar.OnSearchTermsChanged += text => { - searchText.Text = $"OnSearchTermsChanged: {text}"; + searchText.Text = $"{nameof(bar.OnSearchTermsChanged)}: {text}"; }; }); } From 1393e3628be595cc9f2ab67cc7ee92d453e2acf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 15:24:00 +0200 Subject: [PATCH 0149/1276] Invert operands for better readability --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 670ce65c6b..4a839b048c 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -190,7 +190,7 @@ namespace osu.Game.Overlays.Toolbar public bool OnPressed(KeyBindingPressEvent e) { - if (!e.Repeat && e.Action == Hotkey) + if (e.Action == Hotkey && !e.Repeat) { TriggerClick(); return true; From 293ef4483609737f34bf5796d0f8fa09b84b177c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 26 Mar 2022 22:43:17 +0100 Subject: [PATCH 0150/1276] Implement new mod select screen --- .../UserInterface/TestSceneModSelectScreen.cs | 28 ++ osu.Game/Overlays/Mods/ModColumn.cs | 11 +- osu.Game/Overlays/Mods/ModSelectScreen.cs | 315 ++++++++++++++++++ osu.Game/Overlays/Mods/ModSettingsArea.cs | 4 +- 4 files changed, 356 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs create mode 100644 osu.Game/Overlays/Mods/ModSelectScreen.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs new file mode 100644 index 0000000000..e6a5784536 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Mods; + +namespace osu.Game.Tests.Visual.UserInterface +{ + [TestFixture] + public class TestSceneModSelectScreen : OsuTestScene + { + [Test] + public void TestModSelectScreen() + { + ModSelectScreen modSelectScreen = null; + + AddStep("create screen", () => Child = modSelectScreen = new ModSelectScreen + { + RelativeSizeAxes = Axes.Both, + State = { Value = Visibility.Visible } + }); + + AddToggleStep("toggle state", visible => modSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden); + } + } +} diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 736a0205e2..9cf9e6a268 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -48,6 +48,8 @@ namespace osu.Game.Overlays.Mods } } + public Action? ModStateChanged { get; set; } + private readonly ModType modType; private readonly Key[]? toggleKeys; @@ -251,7 +253,14 @@ namespace osu.Game.Overlays.Mods panelFlow.ChildrenEnumerable = loaded; foreach (var panel in panelFlow) - panel.Active.BindValueChanged(_ => updateToggleState()); + { + panel.Active.BindValueChanged(_ => + { + updateToggleState(); + ModStateChanged?.Invoke(panel.Mod, panel.Active.Value); + }); + } + updateToggleState(); updateFilter(); diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs new file mode 100644 index 0000000000..bd27407965 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -0,0 +1,315 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +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.Framework.Input.Events; +using osu.Framework.Layout; +using osu.Game.Configuration; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Mods; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Overlays.Mods +{ + public class ModSelectScreen : OsuFocusedOverlayContainer + { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + + [Cached] + private Bindable> selectedMods = new Bindable>(Array.Empty()); + + private readonly BindableBool customisationVisible = new BindableBool(); + + private DifficultyMultiplierDisplay multiplierDisplay; + private ModSettingsArea modSettingsArea; + private Container columnContainer; + private FillFlowContainer columnFlow; + private GridContainer grid; + private Container mainContent; + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + RelativePositionAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + mainContent = new Container + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + grid = new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.Absolute, 75), + }, + Content = new[] + { + new Drawable[] + { + new PopupScreenTitle + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Title = "Mod Select", + Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.", + Close = Hide + } + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 100, Vertical = 10 }, + Child = multiplierDisplay = new DifficultyMultiplierDisplay + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight + } + } + }, + new Drawable[] + { + columnContainer = new Container + { + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, + X = 1, + Padding = new MarginPadding { Horizontal = 70 }, + Children = new Drawable[] + { + new OsuScrollContainer(Direction.Horizontal) + { + RelativeSizeAxes = Axes.Both, + Masking = false, + ScrollbarOverlapsContent = false, + Child = columnFlow = new ModColumnContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Spacing = new Vector2(10, 0), + Children = new[] + { + new ModColumn(ModType.DifficultyReduction, false, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), + new ModColumn(ModType.DifficultyIncrease, false, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), + new ModColumn(ModType.Automation, false, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), + new ModColumn(ModType.Conversion, false), + new ModColumn(ModType.Fun, false) + } + } + } + } + } + }, + new[] { Empty() } + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.X, + Height = 50, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Colour = colourProvider.Background5 + }, + new ShearedToggleButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Vertical = 14, Left = 70 }, + Text = "Mod Customisation", + Active = { BindTarget = customisationVisible } + } + } + }, + new ClickToReturnContainer + { + RelativeSizeAxes = Axes.Both, + HandleMouse = { BindTarget = customisationVisible }, + OnClicked = () => customisationVisible.Value = false + } + } + }, + modSettingsArea = new ModSettingsArea + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre + } + }; + + foreach (var column in columnFlow) + { + column.ModStateChanged = (mod, active) => selectedMods.Value = active + ? selectedMods.Value.Append(mod).ToArray() + : selectedMods.Value.Except(new[] { mod }).ToArray(); + } + + columnFlow.Shear = new Vector2(ModPanel.SHEAR_X, 0); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + ((IBindable>)modSettingsArea.SelectedMods).BindTo(selectedMods); + selectedMods.BindValueChanged(val => + { + updateMultiplier(); + updateCustomisation(val); + }, true); + customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); + + FinishTransforms(true); + } + + private void updateMultiplier() + { + double multiplier = 1.0; + + foreach (var mod in selectedMods.Value) + multiplier *= mod.ScoreMultiplier; + + multiplierDisplay.Current.Value = multiplier; + } + + private void updateCustomisation(ValueChangedEvent> valueChangedEvent) + { + bool anyCustomisableMod = false; + bool anyModWithRequiredCustomisationAdded = false; + + foreach (var mod in selectedMods.Value) + { + anyCustomisableMod |= mod.GetSettingsSourceProperties().Any(); + anyModWithRequiredCustomisationAdded |= !valueChangedEvent.OldValue.Contains(mod) && mod.RequiresConfiguration; + } + + if (anyCustomisableMod) + { + customisationVisible.Disabled = false; + + if (anyModWithRequiredCustomisationAdded && !customisationVisible.Value) + customisationVisible.Value = true; + } + else + { + if (customisationVisible.Value) + customisationVisible.Value = false; + + customisationVisible.Disabled = true; + } + } + + private void updateCustomisationVisualState() + { + grid.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, 800, Easing.OutQuint); + + float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0; + + modSettingsArea.ResizeHeightTo(modAreaHeight, 800, Easing.InOutCubic); + mainContent.TransformTo(nameof(Margin), new MarginPadding { Bottom = modAreaHeight }, 800, Easing.InOutCubic); + } + + protected override void PopIn() + { + base.PopIn(); + this.MoveToY(0, 800, Easing.OutQuint); + columnContainer.MoveToX(0, 800, Easing.OutQuint); + } + + protected override void PopOut() + { + base.PopOut(); + this.MoveToY(1.2f, 800, Easing.OutQuint); + columnContainer.MoveToX(1, 800, Easing.OutQuint); + } + + private class ModColumnContainer : FillFlowContainer + { + private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize); + + public ModColumnContainer() + { + AddLayout(drawSizeLayout); + } + + public override void Add(ModColumn column) + { + base.Add(column); + + Debug.Assert(column != null); + column.Shear = Vector2.Zero; + } + + protected override void Update() + { + base.Update(); + + if (!drawSizeLayout.IsValid) + { + Padding = new MarginPadding + { + Left = DrawHeight * ModPanel.SHEAR_X, + Bottom = 10 + }; + + drawSizeLayout.Validate(); + } + } + } + + private class ClickToReturnContainer : Container + { + public BindableBool HandleMouse { get; } = new BindableBool(); + + public Action OnClicked { get; set; } + + protected override bool Handle(UIEvent e) + { + if (!HandleMouse.Value) + return base.Handle(e); + + switch (e) + { + case ClickEvent _: + OnClicked?.Invoke(); + return true; + + case MouseEvent _: + return true; + } + + return base.Handle(e); + } + } + } +} diff --git a/osu.Game/Overlays/Mods/ModSettingsArea.cs b/osu.Game/Overlays/Mods/ModSettingsArea.cs index e0a30f60c2..9b0382c6a4 100644 --- a/osu.Game/Overlays/Mods/ModSettingsArea.cs +++ b/osu.Game/Overlays/Mods/ModSettingsArea.cs @@ -23,6 +23,8 @@ namespace osu.Game.Overlays.Mods { public Bindable> SelectedMods { get; } = new Bindable>(); + public const float HEIGHT = 250; + private readonly Box background; private readonly FillFlowContainer modSettingsFlow; @@ -32,7 +34,7 @@ namespace osu.Game.Overlays.Mods public ModSettingsArea() { RelativeSizeAxes = Axes.X; - Height = 250; + Height = HEIGHT; Anchor = Anchor.BottomRight; Origin = Anchor.BottomRight; From e46c2df409afe3bcf50e3a8bb40bb8f1bf90d0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 26 Mar 2022 23:21:17 +0100 Subject: [PATCH 0151/1276] Add testing for customisation panel show/hide logic --- .../UserInterface/TestSceneModSelectScreen.cs | 62 ++++++++++++++++--- osu.Game/Overlays/Mods/ModSelectScreen.cs | 16 ++--- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index e6a5784536..25b2e92c0c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -1,28 +1,76 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneModSelectScreen : OsuTestScene + public class TestSceneModSelectScreen : OsuManualInputManagerTestScene { - [Test] - public void TestModSelectScreen() - { - ModSelectScreen modSelectScreen = null; + private ModSelectScreen modSelectScreen; + [SetUpSteps] + public void SetUpSteps() + { AddStep("create screen", () => Child = modSelectScreen = new ModSelectScreen { RelativeSizeAxes = Axes.Both, - State = { Value = Visibility.Visible } + State = { Value = Visibility.Visible }, + SelectedMods = { BindTarget = SelectedMods } + }); + } + + [Test] + public void TestStateChange() + { + AddToggleStep("toggle state", visible => modSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden); + } + + [Test] + public void TestCustomisationToggleState() + { + assertCustomisationToggleState(disabled: true, active: false); + + AddStep("select customisable mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime() }); + assertCustomisationToggleState(disabled: false, active: false); + + AddStep("select mod requiring configuration", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); + assertCustomisationToggleState(disabled: false, active: true); + + AddStep("dismiss mod customisation", () => + { + InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); }); - AddToggleStep("toggle state", visible => modSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden); + AddStep("append another mod not requiring config", () => SelectedMods.Value = SelectedMods.Value.Append(new OsuModFlashlight()).ToArray()); + assertCustomisationToggleState(disabled: false, active: false); + + AddStep("select mod without configuration", () => SelectedMods.Value = new[] { new OsuModAutoplay() }); + assertCustomisationToggleState(disabled: true, active: false); + + AddStep("select mod requiring configuration", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); + assertCustomisationToggleState(disabled: false, active: true); + + AddStep("select mod without configuration", () => SelectedMods.Value = new[] { new OsuModAutoplay() }); + assertCustomisationToggleState(disabled: true, active: false); // config was dismissed without explicit user action. + } + + private void assertCustomisationToggleState(bool disabled, bool active) + { + ShearedToggleButton getToggle() => modSelectScreen.ChildrenOfType().Single(); + + AddAssert($"customisation toggle is {(disabled ? "" : "not ")}disabled", () => getToggle().Active.Disabled == disabled); + AddAssert($"customisation toggle is {(active ? "" : "not ")}active", () => getToggle().Active.Value == active); } } } diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index bd27407965..0a9b1ddbcb 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Mods private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); [Cached] - private Bindable> selectedMods = new Bindable>(Array.Empty()); + public Bindable> SelectedMods { get; private set; } = new Bindable>(Array.Empty()); private readonly BindableBool customisationVisible = new BindableBool(); @@ -169,9 +169,9 @@ namespace osu.Game.Overlays.Mods foreach (var column in columnFlow) { - column.ModStateChanged = (mod, active) => selectedMods.Value = active - ? selectedMods.Value.Append(mod).ToArray() - : selectedMods.Value.Except(new[] { mod }).ToArray(); + column.ModStateChanged = (mod, active) => SelectedMods.Value = active + ? SelectedMods.Value.Append(mod).ToArray() + : SelectedMods.Value.Except(new[] { mod }).ToArray(); } columnFlow.Shear = new Vector2(ModPanel.SHEAR_X, 0); @@ -181,8 +181,8 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - ((IBindable>)modSettingsArea.SelectedMods).BindTo(selectedMods); - selectedMods.BindValueChanged(val => + ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); + SelectedMods.BindValueChanged(val => { updateMultiplier(); updateCustomisation(val); @@ -196,7 +196,7 @@ namespace osu.Game.Overlays.Mods { double multiplier = 1.0; - foreach (var mod in selectedMods.Value) + foreach (var mod in SelectedMods.Value) multiplier *= mod.ScoreMultiplier; multiplierDisplay.Current.Value = multiplier; @@ -207,7 +207,7 @@ namespace osu.Game.Overlays.Mods bool anyCustomisableMod = false; bool anyModWithRequiredCustomisationAdded = false; - foreach (var mod in selectedMods.Value) + foreach (var mod in SelectedMods.Value) { anyCustomisableMod |= mod.GetSettingsSourceProperties().Any(); anyModWithRequiredCustomisationAdded |= !valueChangedEvent.OldValue.Contains(mod) && mod.RequiresConfiguration; From 2921a1360917b915852e678bed4b453c9e777235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 17:43:13 +0200 Subject: [PATCH 0152/1276] Add testing for mod bindable state propagation --- .../UserInterface/TestSceneModSelectScreen.cs | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 25b2e92c0c..a5f5048961 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -6,8 +6,10 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osuTK.Input; @@ -21,23 +23,55 @@ namespace osu.Game.Tests.Visual.UserInterface [SetUpSteps] public void SetUpSteps() { - AddStep("create screen", () => Child = modSelectScreen = new ModSelectScreen - { - RelativeSizeAxes = Axes.Both, - State = { Value = Visibility.Visible }, - SelectedMods = { BindTarget = SelectedMods } - }); + AddStep("reset mods", () => SelectedMods.SetDefault()); } + private void createScreen() => AddStep("create screen", () => Child = modSelectScreen = new ModSelectScreen + { + RelativeSizeAxes = Axes.Both, + State = { Value = Visibility.Visible }, + SelectedMods = { BindTarget = SelectedMods } + }); + [Test] public void TestStateChange() { + createScreen(); AddToggleStep("toggle state", visible => modSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden); } + [Test] + public void TestPreexistingSelection() + { + AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModAlternate(), new OsuModDaycore() }); + createScreen(); + AddUntilStep("two panels active", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddAssert("mod multiplier correct", () => + { + double multiplier = SelectedMods.Value.Aggregate(1d, (multiplier, mod) => multiplier * mod.ScoreMultiplier); + return Precision.AlmostEquals(multiplier, modSelectScreen.ChildrenOfType().Single().Current.Value); + }); + assertCustomisationToggleState(disabled: false, active: false); + } + + [Test] + public void TestExternalSelection() + { + createScreen(); + AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModAlternate(), new OsuModDaycore() }); + AddUntilStep("two panels active", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddAssert("mod multiplier correct", () => + { + double multiplier = SelectedMods.Value.Aggregate(1d, (multiplier, mod) => multiplier * mod.ScoreMultiplier); + return Precision.AlmostEquals(multiplier, modSelectScreen.ChildrenOfType().Single().Current.Value); + }); + assertCustomisationToggleState(disabled: false, active: false); + } + [Test] public void TestCustomisationToggleState() { + createScreen(); assertCustomisationToggleState(disabled: true, active: false); AddStep("select customisable mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime() }); From cd776d21a6c5268277644bebd1fe3336cb75dbdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Mar 2022 00:16:10 +0200 Subject: [PATCH 0153/1276] Fix propagation of selected mods to columns --- osu.Game/Overlays/Mods/ModColumn.cs | 43 +++++++++++++++-------- osu.Game/Overlays/Mods/ModSelectScreen.cs | 42 ++++++++++++++++++---- 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 9cf9e6a268..46a562ac98 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -31,6 +31,8 @@ namespace osu.Game.Overlays.Mods { public class ModColumn : CompositeDrawable { + public readonly ModType ModType; + private Func? filter; /// @@ -48,9 +50,8 @@ namespace osu.Game.Overlays.Mods } } - public Action? ModStateChanged { get; set; } + public Bindable> SelectedMods = new Bindable>(Array.Empty()); - private readonly ModType modType; private readonly Key[]? toggleKeys; private readonly Bindable>> availableMods = new Bindable>>(); @@ -71,7 +72,7 @@ namespace osu.Game.Overlays.Mods public ModColumn(ModType modType, bool allowBulkSelection, Key[]? toggleKeys = null) { - this.modType = modType; + ModType = modType; this.toggleKeys = toggleKeys; Width = 320; @@ -195,7 +196,7 @@ namespace osu.Game.Overlays.Mods private void createHeaderText() { - IEnumerable headerTextWords = modType.Humanize(LetterCasing.Title).Split(' '); + IEnumerable headerTextWords = ModType.Humanize(LetterCasing.Title).Split(' '); if (headerTextWords.Count() > 1) { @@ -211,7 +212,7 @@ namespace osu.Game.Overlays.Mods { availableMods.BindTo(game.AvailableMods); - headerBackground.Colour = accentColour = colours.ForModType(modType); + headerBackground.Colour = accentColour = colours.ForModType(ModType); if (toggleAllCheckbox != null) { @@ -227,6 +228,12 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); availableMods.BindValueChanged(_ => Scheduler.AddOnce(updateMods)); + SelectedMods.BindValueChanged(_ => + { + // if a load is in progress, don't try to update the selection - the load flow will do so. + if (latestLoadTask == null) + updateActiveState(); + }); updateMods(); } @@ -234,7 +241,7 @@ namespace osu.Game.Overlays.Mods private void updateMods() { - var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(modType) ?? Array.Empty()).ToList(); + var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty()).ToList(); if (newMods.SequenceEqual(panelFlow.Children.Select(p => p.Mod))) return; @@ -252,18 +259,20 @@ namespace osu.Game.Overlays.Mods { panelFlow.ChildrenEnumerable = loaded; + updateActiveState(); + updateToggleAllState(); + updateFilter(); + foreach (var panel in panelFlow) { panel.Active.BindValueChanged(_ => { - updateToggleState(); - ModStateChanged?.Invoke(panel.Mod, panel.Active.Value); + updateToggleAllState(); + SelectedMods.Value = panel.Active.Value + ? SelectedMods.Value.Append(panel.Mod).ToArray() + : SelectedMods.Value.Except(new[] { panel.Mod }).ToArray(); }); } - - updateToggleState(); - - updateFilter(); }, (cancellationTokenSource = new CancellationTokenSource()).Token); loadTask.ContinueWith(_ => { @@ -272,6 +281,12 @@ namespace osu.Game.Overlays.Mods }); } + private void updateActiveState() + { + foreach (var panel in panelFlow) + panel.Active.Value = SelectedMods.Value.Contains(panel.Mod, EqualityComparer.Default); + } + #region Bulk select / deselect private const double initial_multiple_selection_delay = 120; @@ -306,7 +321,7 @@ namespace osu.Game.Overlays.Mods } } - private void updateToggleState() + private void updateToggleAllState() { if (toggleAllCheckbox != null && !SelectionAnimationRunning) { @@ -408,7 +423,7 @@ namespace osu.Game.Overlays.Mods foreach (var modPanel in panelFlow) modPanel.ApplyFilter(Filter); - updateToggleState(); + updateToggleAllState(); } #endregion diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 0a9b1ddbcb..659c88e070 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -167,13 +167,6 @@ namespace osu.Game.Overlays.Mods } }; - foreach (var column in columnFlow) - { - column.ModStateChanged = (mod, active) => SelectedMods.Value = active - ? SelectedMods.Value.Append(mod).ToArray() - : SelectedMods.Value.Except(new[] { mod }).ToArray(); - } - columnFlow.Shear = new Vector2(ModPanel.SHEAR_X, 0); } @@ -182,11 +175,19 @@ namespace osu.Game.Overlays.Mods base.LoadComplete(); ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); + SelectedMods.BindValueChanged(val => { updateMultiplier(); updateCustomisation(val); + updateSelectionFromBindable(); }, true); + + foreach (var column in columnFlow) + { + column.SelectedMods.BindValueChanged(_ => updateBindableFromSelection()); + } + customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); FinishTransforms(true); @@ -239,6 +240,33 @@ namespace osu.Game.Overlays.Mods mainContent.TransformTo(nameof(Margin), new MarginPadding { Bottom = modAreaHeight }, 800, Easing.InOutCubic); } + private bool selectionBindableSyncInProgress; + + private void updateSelectionFromBindable() + { + if (selectionBindableSyncInProgress) + return; + + selectionBindableSyncInProgress = true; + + foreach (var column in columnFlow) + column.SelectedMods.Value = SelectedMods.Value.Where(mod => mod.Type == column.ModType).ToArray(); + + selectionBindableSyncInProgress = false; + } + + private void updateBindableFromSelection() + { + if (selectionBindableSyncInProgress) + return; + + selectionBindableSyncInProgress = true; + + SelectedMods.Value = columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray(); + + selectionBindableSyncInProgress = false; + } + protected override void PopIn() { base.PopIn(); From a172b194f0a97d546e7af99ee34f93512cb8c594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 18:38:23 +0200 Subject: [PATCH 0154/1276] Add testing for different rulesets --- .../UserInterface/TestSceneModSelectScreen.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index a5f5048961..0d568cd030 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -9,8 +9,12 @@ using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Taiko; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface @@ -68,6 +72,16 @@ namespace osu.Game.Tests.Visual.UserInterface assertCustomisationToggleState(disabled: false, active: false); } + [Test] + public void TestRulesetChange() + { + createScreen(); + AddStep("change to osu!", () => Ruleset.Value = new OsuRuleset().RulesetInfo); + AddStep("change to osu!taiko", () => Ruleset.Value = new TaikoRuleset().RulesetInfo); + AddStep("change to osu!catch", () => Ruleset.Value = new CatchRuleset().RulesetInfo); + AddStep("change to osu!mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo); + } + [Test] public void TestCustomisationToggleState() { From 90e44b67ea0c883128e7e2fe11da91e2e18101e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 20:24:32 +0200 Subject: [PATCH 0155/1276] Improve robustness of test code --- .../UserInterface/TestSceneModSelectScreen.cs | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 0d568cd030..47cff40873 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -3,18 +3,16 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; -using osu.Game.Rulesets.Catch; -using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Taiko; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface @@ -22,20 +20,29 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneModSelectScreen : OsuManualInputManagerTestScene { + [Resolved] + private RulesetStore rulesetStore { get; set; } + private ModSelectScreen modSelectScreen; [SetUpSteps] public void SetUpSteps() { + AddStep("clear contents", Clear); + AddStep("reset ruleset", () => Ruleset.Value = rulesetStore.GetRuleset(0)); AddStep("reset mods", () => SelectedMods.SetDefault()); } - private void createScreen() => AddStep("create screen", () => Child = modSelectScreen = new ModSelectScreen + private void createScreen() { - RelativeSizeAxes = Axes.Both, - State = { Value = Visibility.Visible }, - SelectedMods = { BindTarget = SelectedMods } - }); + AddStep("create screen", () => Child = modSelectScreen = new ModSelectScreen + { + RelativeSizeAxes = Axes.Both, + State = { Value = Visibility.Visible }, + SelectedMods = { BindTarget = SelectedMods } + }); + waitForColumnLoad(); + } [Test] public void TestStateChange() @@ -76,10 +83,10 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestRulesetChange() { createScreen(); - AddStep("change to osu!", () => Ruleset.Value = new OsuRuleset().RulesetInfo); - AddStep("change to osu!taiko", () => Ruleset.Value = new TaikoRuleset().RulesetInfo); - AddStep("change to osu!catch", () => Ruleset.Value = new CatchRuleset().RulesetInfo); - AddStep("change to osu!mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo); + changeRuleset(0); + changeRuleset(1); + changeRuleset(2); + changeRuleset(3); } [Test] @@ -113,6 +120,15 @@ namespace osu.Game.Tests.Visual.UserInterface assertCustomisationToggleState(disabled: true, active: false); // config was dismissed without explicit user action. } + private void waitForColumnLoad() => AddUntilStep("all column content loaded", + () => modSelectScreen.ChildrenOfType().Any() && modSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); + + private void changeRuleset(int id) + { + AddStep($"set ruleset to {id}", () => Ruleset.Value = rulesetStore.GetRuleset(id)); + waitForColumnLoad(); + } + private void assertCustomisationToggleState(bool disabled, bool active) { ShearedToggleButton getToggle() => modSelectScreen.ChildrenOfType().Single(); From e1f147a207a42f47370ac42527226212e23d3f9c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 4 Apr 2022 13:46:41 +0900 Subject: [PATCH 0156/1276] Mutate playlist in EditUserPlaylistItem --- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 0efaf16f99..4a974cf61d 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -458,8 +458,8 @@ namespace osu.Game.Tests.Visual.Multiplayer public async Task EditUserPlaylistItem(int userId, MultiplayerPlaylistItem item) { Debug.Assert(Room != null); - Debug.Assert(APIRoom != null); Debug.Assert(currentItem != null); + Debug.Assert(serverSideAPIRoom != null); item.OwnerID = userId; @@ -478,6 +478,7 @@ namespace osu.Game.Tests.Visual.Multiplayer item.PlaylistOrder = existingItem.PlaylistOrder; serverSidePlaylist[serverSidePlaylist.IndexOf(existingItem)] = item; + serverSideAPIRoom.Playlist[serverSideAPIRoom.Playlist.IndexOf(serverSideAPIRoom.Playlist.Single(i => i.ID == item.ID))] = new PlaylistItem(item); await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false); } From 39c6eed81969fe883264c04610c4b05ea0968816 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 14:10:57 +0900 Subject: [PATCH 0157/1276] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 6a3b113fa2..200008017f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3c01f29671..18faf318a6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c8f170497d..b0c382c695 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From 0abebe4d23a17cfd772fe1b700ddefb2a2427a57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 14:36:03 +0900 Subject: [PATCH 0158/1276] Stabilise countdown updates to be based on when whole seconds change --- .../Match/MultiplayerReadyButton.cs | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 62be9ad3bd..a20d250ea8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -47,17 +47,33 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match countdownChangeTime = DateTimeOffset.Now; } + scheduleNextCountdownUpdate(); + + updateButtonText(); + updateButtonColour(); + }); + + private void scheduleNextCountdownUpdate() + { if (countdown != null) - countdownUpdateDelegate ??= Scheduler.AddDelayed(updateButtonText, 100, true); + { + // If a countdown is active, schedule relevant components to update on the next whole second. + double timeToNextSecond = countdownTimeRemaining.TotalMilliseconds % 1000; + + countdownUpdateDelegate = Scheduler.AddDelayed(onCountdownTick, timeToNextSecond); + } else { countdownUpdateDelegate?.Cancel(); countdownUpdateDelegate = null; } - updateButtonText(); - updateButtonColour(); - }); + void onCountdownTick() + { + updateButtonText(); + scheduleNextCountdownUpdate(); + } + } private void updateButtonText() { @@ -75,15 +91,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match if (countdown != null) { - TimeSpan timeElapsed = DateTimeOffset.Now - countdownChangeTime; - TimeSpan countdownRemaining; - - if (timeElapsed > countdown.TimeRemaining) - countdownRemaining = TimeSpan.Zero; - else - countdownRemaining = countdown.TimeRemaining - timeElapsed; - - string countdownText = $"Starting in {countdownRemaining:mm\\:ss}"; + string countdownText = $"Starting in {countdownTimeRemaining:mm\\:ss}"; switch (localUser?.State) { @@ -116,6 +124,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } } + private TimeSpan countdownTimeRemaining + { + get + { + TimeSpan timeElapsed = DateTimeOffset.Now - countdownChangeTime; + TimeSpan remaining; + + if (timeElapsed > countdown.TimeRemaining) + remaining = TimeSpan.Zero; + else + remaining = countdown.TimeRemaining - timeElapsed; + + return remaining; + } + } + private void updateButtonColour() { if (room == null) From 3ba81f3fdce3791d50f526849a320919a1e9d9fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 15:29:03 +0900 Subject: [PATCH 0159/1276] Fix overlapping variable usage --- .../Visual/UserInterface/TestSceneModSelectScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 47cff40873..1e9357b6c4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("two panels active", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); AddAssert("mod multiplier correct", () => { - double multiplier = SelectedMods.Value.Aggregate(1d, (multiplier, mod) => multiplier * mod.ScoreMultiplier); + double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); return Precision.AlmostEquals(multiplier, modSelectScreen.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("two panels active", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); AddAssert("mod multiplier correct", () => { - double multiplier = SelectedMods.Value.Aggregate(1d, (multiplier, mod) => multiplier * mod.ScoreMultiplier); + double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); return Precision.AlmostEquals(multiplier, modSelectScreen.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); From b5df3500079489c969ba95666a7c94c0cf977d41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 15:45:44 +0900 Subject: [PATCH 0160/1276] Adjust pop in/out transitions --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 26 +++++++++++++++++------ 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 659c88e070..fb991f60ba 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -38,6 +38,9 @@ namespace osu.Game.Overlays.Mods private GridContainer grid; private Container mainContent; + private PopupScreenTitle header; + private Container footer; + [BackgroundDependencyLoader] private void load() { @@ -67,7 +70,7 @@ namespace osu.Game.Overlays.Mods { new Drawable[] { - new PopupScreenTitle + header = new PopupScreenTitle { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -96,7 +99,6 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, - X = 1, Padding = new MarginPadding { Horizontal = 70 }, Children = new Drawable[] { @@ -126,7 +128,7 @@ namespace osu.Game.Overlays.Mods new[] { Empty() } } }, - new Container + footer = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -269,16 +271,26 @@ namespace osu.Game.Overlays.Mods protected override void PopIn() { + const double fade_in_duration = 500; + base.PopIn(); - this.MoveToY(0, 800, Easing.OutQuint); - columnContainer.MoveToX(0, 800, Easing.OutQuint); + + header.MoveToY(0, fade_in_duration, Easing.OutQuint); + footer.MoveToY(0, fade_in_duration, Easing.OutQuint); + + this.FadeIn(fade_in_duration, Easing.OutQuint); } protected override void PopOut() { + const double fade_out_duration = 500; + base.PopOut(); - this.MoveToY(1.2f, 800, Easing.OutQuint); - columnContainer.MoveToX(1, 800, Easing.OutQuint); + + header.MoveToY(-header.DrawHeight, fade_out_duration, Easing.OutQuint); + footer.MoveToY(footer.DrawHeight, fade_out_duration, Easing.OutQuint); + + this.FadeOut(fade_out_duration, Easing.OutQuint); } private class ModColumnContainer : FillFlowContainer From bc53adb72a14e5bbc16d320243a76388d2cc063e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 15:50:40 +0900 Subject: [PATCH 0161/1276] Adjust customisation transition slightly --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index fb991f60ba..b3435affff 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -234,12 +234,14 @@ namespace osu.Game.Overlays.Mods private void updateCustomisationVisualState() { - grid.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, 800, Easing.OutQuint); + const double transition_duration = 700; + + grid.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic); float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0; - modSettingsArea.ResizeHeightTo(modAreaHeight, 800, Easing.InOutCubic); - mainContent.TransformTo(nameof(Margin), new MarginPadding { Bottom = modAreaHeight }, 800, Easing.InOutCubic); + modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic); + mainContent.TransformTo(nameof(Margin), new MarginPadding { Bottom = modAreaHeight }, transition_duration, Easing.InOutCubic); } private bool selectionBindableSyncInProgress; From 09e15f5496a8ecee0f0c77f3743a3b664a150da9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:27:03 +0900 Subject: [PATCH 0162/1276] Remove nullable on `RealmBackedResourceStore` realm parameter --- osu.Game/Skinning/RealmBackedResourceStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs index e727a7e59a..c81e976a67 100644 --- a/osu.Game/Skinning/RealmBackedResourceStore.cs +++ b/osu.Game/Skinning/RealmBackedResourceStore.cs @@ -24,7 +24,7 @@ namespace osu.Game.Skinning private readonly Live liveSource; private readonly IDisposable? realmSubscription; - public RealmBackedResourceStore(Live source, IResourceStore underlyingStore, RealmAccess? realm) + public RealmBackedResourceStore(Live source, IResourceStore underlyingStore, RealmAccess realm) : base(underlyingStore) { liveSource = source; @@ -32,7 +32,7 @@ namespace osu.Game.Skinning invalidateCache(); Debug.Assert(fileToStoragePathMapping != null); - realmSubscription = realm?.RegisterForNotifications(r => r.All().Where(s => s.ID == source.ID), skinChanged); + realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s => s.ID == source.ID), skinChanged); } protected override void Dispose(bool disposing) From 300feadf6a9adba8120db8dc3e50fcef5c33c99d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:27:46 +0900 Subject: [PATCH 0163/1276] Update `SkinnableSprite` to match more broad usage --- osu.Game/Skinning/SkinnableSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index c6cc4c1bdd..e7c62302b1 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -15,7 +15,7 @@ using osuTK; namespace osu.Game.Skinning { /// - /// A skinnable element which uses a stable sprite and can therefore share implementation logic. + /// A skinnable element which uses a single texture backing. /// public class SkinnableSprite : SkinnableDrawable, ISkinnableDrawable { From dac5dfde8f8cee98be41887a5b4c94444efe3953 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:28:43 +0900 Subject: [PATCH 0164/1276] Remove unnecessary `LazyThreadSafetyMode` specification --- osu.Game/Skinning/RealmBackedResourceStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs index c81e976a67..0353b8a64d 100644 --- a/osu.Game/Skinning/RealmBackedResourceStore.cs +++ b/osu.Game/Skinning/RealmBackedResourceStore.cs @@ -61,7 +61,7 @@ namespace osu.Game.Skinning return null; } - private void invalidateCache() => fileToStoragePathMapping = new Lazy>(initialiseFileCache, LazyThreadSafetyMode.ExecutionAndPublication); + private void invalidateCache() => fileToStoragePathMapping = new Lazy>(initialiseFileCache); private Dictionary initialiseFileCache() => liveSource.PerformRead(source => { From de30a42558b7ab5d0e03cae2ed80d574cb694732 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:30:14 +0900 Subject: [PATCH 0165/1276] Add `region` for import methods and move `Dispose` to end of time --- osu.Game/Skinning/Editor/SkinEditor.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index df0bb7a70c..607a881d28 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -197,13 +197,6 @@ namespace osu.Game.Skinning.Editor SelectedComponents.BindCollectionChanged((_, __) => Scheduler.AddOnce(populateSettings), true); } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - game?.UnregisterImportHandler(this); - } - public void UpdateTargetScreen(Drawable targetScreen) { this.targetScreen = targetScreen; @@ -337,6 +330,8 @@ namespace osu.Game.Skinning.Editor availableTargets.FirstOrDefault(t => t.Components.Contains(item))?.Remove(item); } + #region Drag & drop import handling + public Task Import(params string[] paths) { Schedule(() => @@ -368,5 +363,14 @@ namespace osu.Game.Skinning.Editor public Task Import(params ImportTask[] tasks) => throw new NotImplementedException(); public IEnumerable HandledExtensions => new[] { ".jpg", ".jpeg", ".png" }; + + #endregion + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + game?.UnregisterImportHandler(this); + } } } From 8185020f128dcda5d5a1feb236b02e7a3fbd51d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:35:48 +0900 Subject: [PATCH 0166/1276] Improve the visual of the missing sprite sprite --- osu.Game/Skinning/RealmBackedResourceStore.cs | 1 - osu.Game/Skinning/SkinnableSprite.cs | 33 +++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs index 0353b8a64d..0057132044 100644 --- a/osu.Game/Skinning/RealmBackedResourceStore.cs +++ b/osu.Game/Skinning/RealmBackedResourceStore.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Threading; using osu.Framework.Extensions; using osu.Framework.IO.Stores; using osu.Game.Database; diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index e7c62302b1..c5f110f908 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -6,9 +6,11 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Configuration; +using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Settings; using osuTK; @@ -55,13 +57,7 @@ namespace osu.Game.Skinning var texture = textures.Get(component.LookupName); if (texture == null) - { - return new SpriteIcon - { - Size = new Vector2(100), - Icon = FontAwesome.Solid.QuestionCircle - }; - } + return new SpriteNotFound(component.LookupName); return new Sprite { Texture = texture }; } @@ -98,5 +94,28 @@ namespace osu.Game.Skinning Items = availableFiles; } } + + public class SpriteNotFound : CompositeDrawable + { + public SpriteNotFound(string lookup) + { + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new SpriteIcon + { + Size = new Vector2(50), + Icon = FontAwesome.Solid.QuestionCircle + }, + new OsuSpriteText + { + Position = new Vector2(25, 50), + Text = $"missing: {lookup}", + Origin = Anchor.TopCentre, + } + }; + } + } } } From 5f358a04e98ba580ce6fa7b7c32ee23a35d8cba3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:40:19 +0900 Subject: [PATCH 0167/1276] Return a valid "lighting" response from `DefaultSkin` This is temporary to allow the new sprite lookup flow to potentially be merged before hit lighting skinnability is addressed. --- osu.Game/Skinning/DefaultSkin.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index c645b0fae4..119b0ec9ad 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -158,6 +158,13 @@ namespace osu.Game.Skinning break; } + switch (component.LookupName) + { + // Temporary until default skin has a valid hit lighting. + case @"lighting": + return Drawable.Empty(); + } + if (GetTexture(component.LookupName) is Texture t) return new Sprite { Texture = t }; From 57b8c32f253f94df284fe1a9c9b4b739f9b4ac54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:42:12 +0900 Subject: [PATCH 0168/1276] Remove unused fields --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index b3435affff..8baf141c61 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -33,7 +33,6 @@ namespace osu.Game.Overlays.Mods private DifficultyMultiplierDisplay multiplierDisplay; private ModSettingsArea modSettingsArea; - private Container columnContainer; private FillFlowContainer columnFlow; private GridContainer grid; private Container mainContent; @@ -95,7 +94,7 @@ namespace osu.Game.Overlays.Mods }, new Drawable[] { - columnContainer = new Container + new Container { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, From 6b5ee6d89dbf64de2003f0b8cb9e77da75def66b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:44:05 +0900 Subject: [PATCH 0169/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 6a3b113fa2..ff14c97cd9 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3c01f29671..7b0f8c72c5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index c8f170497d..88daf2eda7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 3708f2b744b46251c3d680b27ce7583109f3f2b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 01:05:04 +0900 Subject: [PATCH 0170/1276] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3203ffeac3..fbe13b11ee 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7e193a37f9..1bebf78d97 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index a68bebeabe..efd5bac38e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From 9a07a95d39817ce408487253e410d845ee11c27d Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Mon, 4 Apr 2022 19:22:53 +0200 Subject: [PATCH 0171/1276] Make several delete confirmation buttons dangerous buttons Includes: - Mass deletion - Beatmap deletion - Local score deletion --- .../Sections/Maintenance/MassDeleteConfirmationDialog.cs | 2 +- osu.Game/Screens/Select/BeatmapDeleteDialog.cs | 2 +- osu.Game/Screens/Select/LocalScoreDeleteDialog.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs index 6380232bbb..c481c80d82 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance HeaderText = @"Confirm deletion of"; Buttons = new PopupDialogButton[] { - new PopupDialogOkButton + new PopupDialogDangerousButton { Text = @"Yes. Go for it.", Action = deleteAction diff --git a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs index 1ac278d045..b156c2485b 100644 --- a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs +++ b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Select HeaderText = @"Confirm deletion of"; Buttons = new PopupDialogButton[] { - new PopupDialogOkButton + new PopupDialogDangerousButton { Text = @"Yes. Totally. Delete it.", Action = () => manager?.Delete(beatmap), diff --git a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs index 1ae244281b..cb96e3f23e 100644 --- a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs +++ b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Select HeaderText = "Confirm deletion of local score"; Buttons = new PopupDialogButton[] { - new PopupDialogOkButton + new PopupDialogDangerousButton { Text = "Yes. Please.", Action = () => scoreManager?.Delete(score) From 9b367d645d464bfcdeec61742de7768b928a8f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Apr 2022 20:02:47 +0200 Subject: [PATCH 0172/1276] Always play pop in sequence --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 8baf141c61..7915fb755c 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -29,6 +29,8 @@ namespace osu.Game.Overlays.Mods [Cached] public Bindable> SelectedMods { get; private set; } = new Bindable>(Array.Empty()); + protected override bool StartHidden => true; + private readonly BindableBool customisationVisible = new BindableBool(); private DifficultyMultiplierDisplay multiplierDisplay; @@ -164,7 +166,8 @@ namespace osu.Game.Overlays.Mods modSettingsArea = new ModSettingsArea { Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre + Origin = Anchor.BottomCentre, + Height = 0 } }; @@ -190,8 +193,6 @@ namespace osu.Game.Overlays.Mods } customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); - - FinishTransforms(true); } private void updateMultiplier() From 29b7460cc7454a8b4371c72f2f30858a04b146c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Apr 2022 20:05:54 +0200 Subject: [PATCH 0173/1276] Fix weird test step --- osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 1e9357b6c4..34d9ddcc4e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestStateChange() { createScreen(); - AddToggleStep("toggle state", visible => modSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden); + AddStep("toggle state", () => modSelectScreen.ToggleVisibility()); } [Test] From a1ded66fd85bcc09e2cfe32cf2ca3d938726b2c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Apr 2022 21:59:09 +0200 Subject: [PATCH 0174/1276] Fix various breakage in delete local score test scene --- .../TestSceneDeleteLocalScore.cs | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index a0a1feff36..c8a8fd43fb 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -44,9 +44,6 @@ namespace osu.Game.Tests.Visual.UserInterface private BeatmapInfo beatmapInfo; - [Resolved] - private RealmAccess realm { get; set; } - [Cached] private readonly DialogOverlay dialogOverlay; @@ -92,6 +89,12 @@ namespace osu.Game.Tests.Visual.UserInterface dependencies.Cache(scoreManager = new ScoreManager(dependencies.Get(), () => beatmapManager, LocalStorage, Realm, Scheduler)); Dependencies.Cache(Realm); + return dependencies; + } + + [BackgroundDependencyLoader] + private void load() => Schedule(() => + { var imported = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).GetResultSafely(); imported?.PerformRead(s => @@ -115,26 +118,26 @@ namespace osu.Game.Tests.Visual.UserInterface importedScores.Add(scoreManager.Import(score).Value); } }); - - return dependencies; - } - - [SetUp] - public void Setup() => Schedule(() => - { - realm.Run(r => - { - // Due to soft deletions, we can re-use deleted scores between test runs - scoreManager.Undelete(r.All().Where(s => s.DeletePending).ToList()); - }); - - leaderboard.BeatmapInfo = beatmapInfo; - leaderboard.RefetchScores(); // Required in the case that the beatmap hasn't changed }); [SetUpSteps] public void SetupSteps() { + AddUntilStep("ensure scores imported", () => importedScores.Count == 50); + AddStep("undelete scores", () => + { + Realm.Run(r => + { + // Due to soft deletions, we can re-use deleted scores between test runs + scoreManager.Undelete(r.All().Where(s => s.DeletePending).ToList()); + }); + }); + AddStep("set up leaderboard", () => + { + leaderboard.BeatmapInfo = beatmapInfo; + leaderboard.RefetchScores(); // Required in the case that the beatmap hasn't changed + }); + // Ensure the leaderboard items have finished showing up AddStep("finish transforms", () => leaderboard.FinishTransforms(true)); AddUntilStep("wait for drawables", () => leaderboard.ChildrenOfType().Any()); From f73062a0d6c141290c93f227db7dd63173175324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Apr 2022 22:22:55 +0200 Subject: [PATCH 0175/1276] Revert "Remove nullable on `RealmBackedResourceStore` realm parameter" This reverts commit 09e15f5496a8ecee0f0c77f3743a3b664a150da9. --- osu.Game/Skinning/RealmBackedResourceStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs index 0057132044..7fa24284ee 100644 --- a/osu.Game/Skinning/RealmBackedResourceStore.cs +++ b/osu.Game/Skinning/RealmBackedResourceStore.cs @@ -23,7 +23,7 @@ namespace osu.Game.Skinning private readonly Live liveSource; private readonly IDisposable? realmSubscription; - public RealmBackedResourceStore(Live source, IResourceStore underlyingStore, RealmAccess realm) + public RealmBackedResourceStore(Live source, IResourceStore underlyingStore, RealmAccess? realm) : base(underlyingStore) { liveSource = source; @@ -31,7 +31,7 @@ namespace osu.Game.Skinning invalidateCache(); Debug.Assert(fileToStoragePathMapping != null); - realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s => s.ID == source.ID), skinChanged); + realmSubscription = realm?.RegisterForNotifications(r => r.All().Where(s => s.ID == source.ID), skinChanged); } protected override void Dispose(bool disposing) From da315f8a61b8e6d876dc47bfe709c17b8dc1241e Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Mon, 4 Apr 2022 22:44:35 +0200 Subject: [PATCH 0176/1276] Make the test hold the button instead of pressing it --- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index a0a1feff36..02a37627d7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -169,7 +169,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("click delete button", () => { InputManager.MoveMouseTo(dialogOverlay.ChildrenOfType().First()); - InputManager.Click(MouseButton.Left); + InputManager.PressButton(MouseButton.Left); }); AddUntilStep("wait for fetch", () => leaderboard.Scores != null); From b2c822a3b1b1498513829c9d469062a52a4a12ec Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Mon, 4 Apr 2022 23:02:07 +0200 Subject: [PATCH 0177/1276] Release mouse button --- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 02a37627d7..26e7b5bdda 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -174,6 +174,9 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for fetch", () => leaderboard.Scores != null); AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineID != scoreBeingDeleted.OnlineID)); + + // "Clean up" + AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left)); } [Test] From 32c89f8643633fa7880292aa7d6828d7e6acaefa Mon Sep 17 00:00:00 2001 From: Ame Date: Tue, 5 Apr 2022 00:33:41 +0200 Subject: [PATCH 0178/1276] Handle repeated `OnPressed()` on `FooterButton` (without `FooterButtonRandom`) --- osu.Game/Screens/Select/FooterButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index 8d2ea47757..9cb178ca8b 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -174,7 +174,7 @@ namespace osu.Game.Screens.Select public virtual bool OnPressed(KeyBindingPressEvent e) { - if (e.Action == Hotkey) + if (e.Action == Hotkey && !e.Repeat) { TriggerClick(); return true; From 117d81d84f13c40fb7a782cfda8d1df6c5fa0c92 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Apr 2022 03:04:53 +0300 Subject: [PATCH 0179/1276] Use perfect osu!(stable) strong scale value Co-authored-by: Dean Herbert --- osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index 43a099b900..6e0f6a3109 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Objects /// /// Scale multiplier for a strong drawable taiko hit object. /// - public const float STRONG_SCALE = 1.525f; + public const float STRONG_SCALE = 1 / 0.65f; /// /// Default size of a strong drawable taiko hit object. From 174dc1641c3fcc03cf72d8f3d35105a07c732d3e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 11:49:57 +0900 Subject: [PATCH 0180/1276] Fix multiple issues with timekeeping - Using realtime (`DateTimeOffset.Now`) meant that values would be changing in the same frame, causing misfirings or incorrect displays - No debounce on sample playback meant that scheduling edge cases could potentially cause samples to be played more than once. --- .../Match/MultiplayerReadyButton.cs | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index a9092bc25a..9b7e9a925e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } private MultiplayerCountdown countdown; - private DateTimeOffset countdownChangeTime; + private double countdownChangeTime; private ScheduledDelegate countdownUpdateDelegate; private void onRoomUpdated() => Scheduler.AddOnce(() => @@ -56,7 +56,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match if (countdown != room?.Countdown) { countdown = room?.Countdown; - countdownChangeTime = DateTimeOffset.Now; + countdownChangeTime = Time.Current; } scheduleNextCountdownUpdate(); @@ -86,13 +86,27 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match int secondsRemaining = countdownTimeRemaining.Seconds; - if (secondsRemaining < 10) countdownTickSample?.Play(); - if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); + playTickSound(secondsRemaining); - scheduleNextCountdownUpdate(); + if (secondsRemaining > 0) + scheduleNextCountdownUpdate(); } } + private double? lastTickSampleTime; + + private void playTickSound(int secondsRemaining) + { + // Simplified debounce. Ticks should only be played roughly once per second regardless of how often this function is called. + if (Time.Current - lastTickSampleTime < 500) + return; + + lastTickSampleTime = Time.Current; + + if (secondsRemaining < 10) countdownTickSample?.Play(); + if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); + } + private void updateButtonText() { if (room == null) @@ -146,13 +160,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { get { - TimeSpan timeElapsed = DateTimeOffset.Now - countdownChangeTime; + double timeElapsed = Time.Current - countdownChangeTime; TimeSpan remaining; - if (timeElapsed > countdown.TimeRemaining) + if (timeElapsed > countdown.TimeRemaining.TotalMilliseconds) remaining = TimeSpan.Zero; else - remaining = countdown.TimeRemaining - timeElapsed; + remaining = countdown.TimeRemaining - TimeSpan.FromMilliseconds(timeElapsed); return remaining; } From 31bf0c4a9b78bb40f5175ea0cb0388243d95c0c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 13:16:06 +0900 Subject: [PATCH 0181/1276] Disable "final" sample in countdown for the time being --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 9b7e9a925e..6eed900963 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -104,7 +104,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match lastTickSampleTime = Time.Current; if (secondsRemaining < 10) countdownTickSample?.Play(); - if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); + // disabled for now pending further work on sound effect + // if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); } private void updateButtonText() From d0f83885cea57adb37334b3646f5eb2e75da784f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 13:20:34 +0900 Subject: [PATCH 0182/1276] Appease the CI --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 6eed900963..7e092bd353 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -30,13 +30,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private MultiplayerRoom room => multiplayerClient.Room; private Sample countdownTickSample; - private Sample countdownTickFinalSample; [BackgroundDependencyLoader] private void load(AudioManager audio) { countdownTickSample = audio.Samples.Get(@"Multiplayer/countdown-tick"); - countdownTickFinalSample = audio.Samples.Get(@"Multiplayer/countdown-tick-final"); + // disabled for now pending further work on sound effect + // countdownTickFinalSample = audio.Samples.Get(@"Multiplayer/countdown-tick-final"); } protected override void LoadComplete() From 5f415cbe53b1d406aa83abf402ceb7249305ea61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 15:48:18 +0900 Subject: [PATCH 0183/1276] Full potential null reference and add better commentary on countdown scheduling --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 7e092bd353..4cde7e71c3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -67,9 +67,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void scheduleNextCountdownUpdate() { + countdownUpdateDelegate?.Cancel(); + if (countdown != null) { - // If a countdown is active, schedule relevant components to update on the next whole second. + // The remaining time on a countdown may be at a fractional portion between two seconds. + // We want to align certain audio/visual cues to the point at which integer seconds change. + // To do so, we schedule to the next whole second. + // Note that scheduler invocation isn't guaranteed to be accurate, so this may still occur slightly late. double timeToNextSecond = countdownTimeRemaining.TotalMilliseconds % 1000; countdownUpdateDelegate = Scheduler.AddDelayed(onCountdownTick, timeToNextSecond); From 8e543204cdca8305cf224f338848876d31120982 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 15:49:47 +0900 Subject: [PATCH 0184/1276] Remove debounce logic (not required after switching to `Update` clock time) --- .../Multiplayer/Match/MultiplayerReadyButton.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 4cde7e71c3..4860078a79 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -98,16 +98,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } } - private double? lastTickSampleTime; - private void playTickSound(int secondsRemaining) { - // Simplified debounce. Ticks should only be played roughly once per second regardless of how often this function is called. - if (Time.Current - lastTickSampleTime < 500) - return; - - lastTickSampleTime = Time.Current; - if (secondsRemaining < 10) countdownTickSample?.Play(); // disabled for now pending further work on sound effect // if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); From 3d8ae0465f1313981960f10a949aadbd21bd6651 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 15:51:04 +0900 Subject: [PATCH 0185/1276] Reword comment slightly --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 4860078a79..d275f309cb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -73,8 +73,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { // The remaining time on a countdown may be at a fractional portion between two seconds. // We want to align certain audio/visual cues to the point at which integer seconds change. - // To do so, we schedule to the next whole second. - // Note that scheduler invocation isn't guaranteed to be accurate, so this may still occur slightly late. + // To do so, we schedule to the next whole second. Note that scheduler invocation isn't + // guaranteed to be accurate, so this may still occur slightly late, but even in such a case + // the next invocation will be roughly correct. double timeToNextSecond = countdownTimeRemaining.TotalMilliseconds % 1000; countdownUpdateDelegate = Scheduler.AddDelayed(onCountdownTick, timeToNextSecond); From 23dad7bdc4cab9bbbac3f8c707c17f2d23eb2c53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 16:39:02 +0900 Subject: [PATCH 0186/1276] Move scroll view padding to content level Without doing this, there is a non-masked but also non-interactive area to the left or right of the view. --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 7915fb755c..5197cb6c3e 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -100,7 +100,6 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 70 }, Children = new Drawable[] { new OsuScrollContainer(Direction.Horizontal) @@ -113,6 +112,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Spacing = new Vector2(10, 0), + Margin = new MarginPadding { Horizontal = 70 }, Children = new[] { new ModColumn(ModType.DifficultyReduction, false, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), From f156cb797df13c31e43856e58037cec9b490bf7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 16:40:09 +0900 Subject: [PATCH 0187/1276] Improve nested scroll behaviour --- osu.Game/Overlays/Mods/ModColumn.cs | 60 ++++++++++++++++++++++- osu.Game/Overlays/Mods/ModSelectScreen.cs | 1 + 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 46a562ac98..513dd2ca3f 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -151,9 +151,10 @@ namespace osu.Game.Overlays.Mods }, new Drawable[] { - new OsuScrollContainer + new NestedVerticalScrollContainer { RelativeSizeAxes = Axes.Both, + ClampExtension = 100, ScrollbarOverlapsContent = false, Child = panelFlow = new FillFlowContainer { @@ -194,6 +195,63 @@ namespace osu.Game.Overlays.Mods } } + /// + /// A scroll container that handles the case of vertically scrolling content inside a larger horizontally scrolling parent container. + /// + private class NestedVerticalScrollContainer : OsuScrollContainer + { + private OsuScrollContainer? parentScrollContainer; + + protected override void LoadComplete() + { + base.LoadComplete(); + + parentScrollContainer = findClosestParent(); + } + + protected override bool OnScroll(ScrollEvent e) + { + if (parentScrollContainer == null) + return base.OnScroll(e); + + // If not on screen, handle scroll but also allow parent to scroll at the same time. + bool topRightOutsideOfView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.TopRight) == false; + bool bottomLeftOutsideOfView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.BottomLeft) == false; + + if (topRightOutsideOfView || bottomLeftOutsideOfView) + { + base.OnScroll(e); + return false; + } + + bool scrollingPastEnd = e.ScrollDelta.Y < 0 && IsScrolledToEnd(); + bool scrollingPastStart = e.ScrollDelta.Y > 0 && Target <= 0; + + // Same deal if at one of the two extents of the view. + if (scrollingPastStart || scrollingPastEnd) + { + base.OnScroll(e); + return false; + } + + return base.OnScroll(e); + } + + // TODO: remove when https://github.com/ppy/osu-framework/pull/5092 is available. + private T? findClosestParent() where T : class, IDrawable + { + Drawable cursor = this; + + while ((cursor = cursor.Parent) != null) + { + if (cursor is T match) + return match; + } + + return default; + } + } + private void createHeaderText() { IEnumerable headerTextWords = ModType.Humanize(LetterCasing.Title).Split(' '); diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 5197cb6c3e..1d6f784133 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -106,6 +106,7 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.Both, Masking = false, + ClampExtension = 100, ScrollbarOverlapsContent = false, Child = columnFlow = new ModColumnContainer { From 2ec15a1ebe83c3e18bbde06f9f41c29db7780797 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 5 Apr 2022 16:47:15 +0900 Subject: [PATCH 0188/1276] Fix lookup through transformers --- osu.Game/Skinning/LegacySkinTransformer.cs | 2 +- osu.Game/Skinning/SkinnableSprite.cs | 23 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinTransformer.cs b/osu.Game/Skinning/LegacySkinTransformer.cs index 97084f34e0..9481fc7182 100644 --- a/osu.Game/Skinning/LegacySkinTransformer.cs +++ b/osu.Game/Skinning/LegacySkinTransformer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Skinning /// The which is being transformed. /// [NotNull] - protected ISkin Skin { get; } + protected internal ISkin Skin { get; } protected LegacySkinTransformer([NotNull] ISkin skin) { diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index c5f110f908..4b4d7fe2c6 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -83,7 +84,7 @@ namespace osu.Game.Skinning // Round-about way of getting the user's skin to find available resources. // In the future we'll probably want to allow access to resources from the fallbacks, or potentially other skins // but that requires further thought. - var highestPrioritySkin = ((SkinnableSprite)SettingSourceObject).source.AllSources.First() as Skin; + var highestPrioritySkin = getHighestPriorityUserSkin(((SkinnableSprite)SettingSourceObject).source.AllSources) as Skin; string[] availableFiles = highestPrioritySkin?.SkinInfo.PerformRead(s => s.Files .Where(f => f.Filename.EndsWith(".png", StringComparison.Ordinal) @@ -92,6 +93,26 @@ namespace osu.Game.Skinning if (availableFiles?.Length > 0) Items = availableFiles; + + static ISkin getHighestPriorityUserSkin(IEnumerable skins) + { + foreach (var skin in skins) + { + if (skin is LegacySkinTransformer transformer && isUserSkin(transformer.Skin)) + return transformer.Skin; + + if (isUserSkin(skin)) + return skin; + } + + return null; + } + + // Temporarily used to exclude undesirable ISkin implementations + static bool isUserSkin(ISkin skin) + => skin.GetType() == typeof(DefaultSkin) + || skin.GetType() == typeof(DefaultLegacySkin) + || skin.GetType() == typeof(LegacySkin); } } From 58e9147b12a613e6ae2dc590d82f5672304963b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 16:48:25 +0900 Subject: [PATCH 0189/1276] Simplify and better comment nested scroll conditionals --- osu.Game/Overlays/Mods/ModColumn.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 513dd2ca3f..e915e88e99 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -214,11 +214,11 @@ namespace osu.Game.Overlays.Mods if (parentScrollContainer == null) return base.OnScroll(e); - // If not on screen, handle scroll but also allow parent to scroll at the same time. - bool topRightOutsideOfView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.TopRight) == false; - bool bottomLeftOutsideOfView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.BottomLeft) == false; + bool topRightInView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.TopRight); + bool bottomLeftInView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.BottomLeft); - if (topRightOutsideOfView || bottomLeftOutsideOfView) + // If not completely on-screen, handle scroll but also allow parent to scroll at the same time (to hopefully bring our content into full view). + if (!topRightInView || !bottomLeftInView) { base.OnScroll(e); return false; @@ -227,7 +227,7 @@ namespace osu.Game.Overlays.Mods bool scrollingPastEnd = e.ScrollDelta.Y < 0 && IsScrolledToEnd(); bool scrollingPastStart = e.ScrollDelta.Y > 0 && Target <= 0; - // Same deal if at one of the two extents of the view. + // If at either of our extents, allow the parent scroll to handle as a horizontal scroll to view more content. if (scrollingPastStart || scrollingPastEnd) { base.OnScroll(e); From 388322cd61eeec61aa7bc53597f49b4c0f463619 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 16:56:24 +0900 Subject: [PATCH 0190/1276] Speed up customisation panel toggle a bit --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 1d6f784133..1f0dcea6c2 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -235,7 +235,7 @@ namespace osu.Game.Overlays.Mods private void updateCustomisationVisualState() { - const double transition_duration = 700; + const double transition_duration = 300; grid.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic); From a408776734b8d7285d8f1af98560dae8405aeb09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 17:14:59 +0900 Subject: [PATCH 0191/1276] Limit `FillFlow` of columns to applicable direction --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 1f0dcea6c2..8468448bbb 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -110,6 +110,7 @@ namespace osu.Game.Overlays.Mods ScrollbarOverlapsContent = false, Child = columnFlow = new ModColumnContainer { + Direction = FillDirection.Horizontal, RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Spacing = new Vector2(10, 0), From 497e5e3a36731490881e8afe3da2cd087446b197 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 17:15:24 +0900 Subject: [PATCH 0192/1276] Slightly adjust scroll handling and also apply to `ModSettingsContainer` --- osu.Game/Overlays/Mods/ModColumn.cs | 57 ----------------- osu.Game/Overlays/Mods/ModSettingsArea.cs | 4 +- .../Mods/NestedVerticalScrollContainer.cs | 61 +++++++++++++++++++ 3 files changed, 64 insertions(+), 58 deletions(-) create mode 100644 osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index e915e88e99..8531ae1e3f 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -195,63 +195,6 @@ namespace osu.Game.Overlays.Mods } } - /// - /// A scroll container that handles the case of vertically scrolling content inside a larger horizontally scrolling parent container. - /// - private class NestedVerticalScrollContainer : OsuScrollContainer - { - private OsuScrollContainer? parentScrollContainer; - - protected override void LoadComplete() - { - base.LoadComplete(); - - parentScrollContainer = findClosestParent(); - } - - protected override bool OnScroll(ScrollEvent e) - { - if (parentScrollContainer == null) - return base.OnScroll(e); - - bool topRightInView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.TopRight); - bool bottomLeftInView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.BottomLeft); - - // If not completely on-screen, handle scroll but also allow parent to scroll at the same time (to hopefully bring our content into full view). - if (!topRightInView || !bottomLeftInView) - { - base.OnScroll(e); - return false; - } - - bool scrollingPastEnd = e.ScrollDelta.Y < 0 && IsScrolledToEnd(); - bool scrollingPastStart = e.ScrollDelta.Y > 0 && Target <= 0; - - // If at either of our extents, allow the parent scroll to handle as a horizontal scroll to view more content. - if (scrollingPastStart || scrollingPastEnd) - { - base.OnScroll(e); - return false; - } - - return base.OnScroll(e); - } - - // TODO: remove when https://github.com/ppy/osu-framework/pull/5092 is available. - private T? findClosestParent() where T : class, IDrawable - { - Drawable cursor = this; - - while ((cursor = cursor.Parent) != null) - { - if (cursor is T match) - return match; - } - - return default; - } - } - private void createHeaderText() { IEnumerable headerTextWords = ModType.Humanize(LetterCasing.Title).Split(' '); diff --git a/osu.Game/Overlays/Mods/ModSettingsArea.cs b/osu.Game/Overlays/Mods/ModSettingsArea.cs index 9b0382c6a4..be72c1e3e3 100644 --- a/osu.Game/Overlays/Mods/ModSettingsArea.cs +++ b/osu.Game/Overlays/Mods/ModSettingsArea.cs @@ -54,6 +54,7 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.Both, ScrollbarOverlapsContent = false, + ClampExtension = 100, Child = modSettingsFlow = new FillFlowContainer { AutoSizeAxes = Axes.X, @@ -157,9 +158,10 @@ namespace osu.Game.Overlays.Mods new[] { Empty() }, new Drawable[] { - new OsuScrollContainer(Direction.Vertical) + new NestedVerticalScrollContainer { RelativeSizeAxes = Axes.Both, + ClampExtension = 100, Child = new FillFlowContainer { RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs new file mode 100644 index 0000000000..c62b52ef27 --- /dev/null +++ b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs @@ -0,0 +1,61 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Mods +{ + /// + /// A scroll container that handles the case of vertically scrolling content inside a larger horizontally scrolling parent container. + /// + public class NestedVerticalScrollContainer : OsuScrollContainer + { + private OsuScrollContainer? parentScrollContainer; + + protected override void LoadComplete() + { + base.LoadComplete(); + + parentScrollContainer = findClosestParent(); + } + + protected override bool OnScroll(ScrollEvent e) + { + if (parentScrollContainer == null) + return base.OnScroll(e); + + bool topRightInView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.TopRight); + bool bottomLeftInView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.BottomLeft); + + // If not completely on-screen, handle scroll but also allow parent to scroll at the same time (to hopefully bring our content into full view). + if (!topRightInView || !bottomLeftInView) + return false; + + bool scrollingPastEnd = e.ScrollDelta.Y < 0 && IsScrolledToEnd(); + bool scrollingPastStart = e.ScrollDelta.Y > 0 && Target <= 0; + + // If at either of our extents, delegate scroll to the horizontal parent container. + if (scrollingPastStart || scrollingPastEnd) + return false; + + return base.OnScroll(e); + } + + // TODO: remove when https://github.com/ppy/osu-framework/pull/5092 is available. + private T? findClosestParent() where T : class, IDrawable + { + Drawable cursor = this; + + while ((cursor = cursor.Parent) != null) + { + if (cursor is T match) + return match; + } + + return default; + } + } +} From 901032bfa21e0916efa9ce1b5b184f1978f53e8f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 18:25:27 +0900 Subject: [PATCH 0193/1276] Animate multiplier display --- .../Mods/DifficultyMultiplierDisplay.cs | 5 ++-- osu.Game/Overlays/Mods/ModSelectScreen.cs | 27 +++++++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 4fc3a904fa..4d3f630a55 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -20,6 +20,8 @@ namespace osu.Game.Overlays.Mods { public class DifficultyMultiplierDisplay : CompositeDrawable, IHasCurrentValue { + public const float HEIGHT = 42; + public Bindable Current { get => current.Current; @@ -42,13 +44,12 @@ namespace osu.Game.Overlays.Mods [Resolved] private OverlayColourProvider colourProvider { get; set; } - private const float height = 42; private const float multiplier_value_area_width = 56; private const float transition_duration = 200; public DifficultyMultiplierDisplay() { - Height = height; + Height = HEIGHT; AutoSizeAxes = Axes.X; InternalChild = new Container diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 8468448bbb..3437922cc8 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -84,13 +84,21 @@ namespace osu.Game.Overlays.Mods { new Container { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 100, Vertical = 10 }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.X, + RelativePositionAxes = Axes.X, + X = 0.3f, + Height = DifficultyMultiplierDisplay.HEIGHT, + Margin = new MarginPadding + { + Horizontal = 100, + Vertical = 10 + }, Child = multiplierDisplay = new DifficultyMultiplierDisplay { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight + Anchor = Anchor.Centre, + Origin = Anchor.Centre } } }, @@ -283,6 +291,11 @@ namespace osu.Game.Overlays.Mods footer.MoveToY(0, fade_in_duration, Easing.OutQuint); this.FadeIn(fade_in_duration, Easing.OutQuint); + + multiplierDisplay + .Delay(300) + .FadeIn(200, Easing.OutQuint) + .ScaleTo(1, fade_in_duration, Easing.OutElastic); } protected override void PopOut() @@ -291,6 +304,10 @@ namespace osu.Game.Overlays.Mods base.PopOut(); + multiplierDisplay + .FadeOut(200, Easing.OutQuint) + .ScaleTo(0.75f, fade_out_duration, Easing.OutQuint); + header.MoveToY(-header.DrawHeight, fade_out_duration, Easing.OutQuint); footer.MoveToY(footer.DrawHeight, fade_out_duration, Easing.OutQuint); From 9fdeb2053746f6e0b35366cbedb4026034640ef9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 18:27:34 +0900 Subject: [PATCH 0194/1276] Animate individual `ModColumn`s during togle of oerlay --- osu.Game/Overlays/Mods/ModColumn.cs | 143 ++++++++++++---------- osu.Game/Overlays/Mods/ModSelectScreen.cs | 15 +++ 2 files changed, 91 insertions(+), 67 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 8531ae1e3f..f84ae4ac8a 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -31,6 +31,8 @@ namespace osu.Game.Overlays.Mods { public class ModColumn : CompositeDrawable { + public readonly Container TopLevelContent; + public readonly ModType ModType; private Func? filter; @@ -78,90 +80,97 @@ namespace osu.Game.Overlays.Mods Width = 320; RelativeSizeAxes = Axes.Y; Shear = new Vector2(ModPanel.SHEAR_X, 0); - CornerRadius = ModPanel.CORNER_RADIUS; - Masking = true; Container controlContainer; InternalChildren = new Drawable[] { - new Container - { - RelativeSizeAxes = Axes.X, - Height = header_height + ModPanel.CORNER_RADIUS, - Children = new Drawable[] - { - headerBackground = new Box - { - RelativeSizeAxes = Axes.X, - Height = header_height + ModPanel.CORNER_RADIUS - }, - headerText = new OsuTextFlowContainer(t => - { - t.Font = OsuFont.TorusAlternate.With(size: 17); - t.Shadow = false; - t.Colour = Colour4.Black; - }) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Shear = new Vector2(-ModPanel.SHEAR_X, 0), - Padding = new MarginPadding - { - Horizontal = 17, - Bottom = ModPanel.CORNER_RADIUS - } - } - } - }, - new Container + TopLevelContent = new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = header_height }, - Child = contentContainer = new Container + CornerRadius = ModPanel.CORNER_RADIUS, + Masking = true, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = ModPanel.CORNER_RADIUS, - BorderThickness = 3, - Children = new Drawable[] + new Container { - contentBackground = new Box + RelativeSizeAxes = Axes.X, + Height = header_height + ModPanel.CORNER_RADIUS, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both - }, - new GridContainer + headerBackground = new Box + { + RelativeSizeAxes = Axes.X, + Height = header_height + ModPanel.CORNER_RADIUS + }, + headerText = new OsuTextFlowContainer(t => + { + t.Font = OsuFont.TorusAlternate.With(size: 17); + t.Shadow = false; + t.Colour = Colour4.Black; + }) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Shear = new Vector2(-ModPanel.SHEAR_X, 0), + Padding = new MarginPadding + { + Horizontal = 17, + Bottom = ModPanel.CORNER_RADIUS + } + } + } + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = header_height }, + Child = contentContainer = new Container { RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + Masking = true, + CornerRadius = ModPanel.CORNER_RADIUS, + BorderThickness = 3, + Children = new Drawable[] { - new Dimension(GridSizeMode.AutoSize), - new Dimension() - }, - Content = new[] - { - new Drawable[] + contentBackground = new Box { - controlContainer = new Container - { - RelativeSizeAxes = Axes.X, - Padding = new MarginPadding { Horizontal = 14 } - } + RelativeSizeAxes = Axes.Both }, - new Drawable[] + new GridContainer { - new NestedVerticalScrollContainer + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - RelativeSizeAxes = Axes.Both, - ClampExtension = 100, - ScrollbarOverlapsContent = false, - Child = panelFlow = new FillFlowContainer + new Dimension(GridSizeMode.AutoSize), + new Dimension() + }, + Content = new[] + { + new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0, 7), - Padding = new MarginPadding(7) + controlContainer = new Container + { + RelativeSizeAxes = Axes.X, + Padding = new MarginPadding { Horizontal = 14 } + } + }, + new Drawable[] + { + new NestedVerticalScrollContainer + { + RelativeSizeAxes = Axes.Both, + ClampExtension = 100, + ScrollbarOverlapsContent = false, + Child = panelFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0, 7), + Padding = new MarginPadding(7) + } + } } } } diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 3437922cc8..1009c2edf6 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -106,6 +106,7 @@ namespace osu.Game.Overlays.Mods { new Container { + Depth = float.MaxValue, RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, Children = new Drawable[] @@ -296,6 +297,13 @@ namespace osu.Game.Overlays.Mods .Delay(300) .FadeIn(200, Easing.OutQuint) .ScaleTo(1, fade_in_duration, Easing.OutElastic); + + for (int i = 0; i < columnFlow.Count; i++) + { + columnFlow[i].TopLevelContent + .Delay(i * 30) + .MoveToY(0, fade_in_duration, Easing.OutQuint); + } } protected override void PopOut() @@ -312,6 +320,13 @@ namespace osu.Game.Overlays.Mods footer.MoveToY(footer.DrawHeight, fade_out_duration, Easing.OutQuint); this.FadeOut(fade_out_duration, Easing.OutQuint); + + for (int i = 0; i < columnFlow.Count; i++) + { + const float distance = 700; + + columnFlow[i].TopLevelContent.MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint); + } } private class ModColumnContainer : FillFlowContainer From 7a1820e6bb99a48101033da2abf8a9a6cd4670fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 18:25:27 +0900 Subject: [PATCH 0195/1276] Fix multiplier display resetting transformations --- osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 4d3f630a55..248d4f288e 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -146,8 +146,9 @@ namespace osu.Game.Overlays.Mods protected override void LoadComplete() { base.LoadComplete(); + current.BindValueChanged(_ => updateState(), true); - FinishTransforms(true); + // required to prevent the counter initially rolling up from 0 to 1 // due to `Current.Value` having a nonstandard default value of 1. multiplierCounter.SetCountWithoutRolling(Current.Value); From 54715885af16463759bb1aa99ed70f298232bb6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 18:38:31 +0900 Subject: [PATCH 0196/1276] Adjust animation metrics slightly --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 1009c2edf6..c43987ea60 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -284,25 +284,25 @@ namespace osu.Game.Overlays.Mods protected override void PopIn() { - const double fade_in_duration = 500; + const double fade_in_duration = 400; base.PopIn(); + this.FadeIn(fade_in_duration, Easing.OutQuint); header.MoveToY(0, fade_in_duration, Easing.OutQuint); footer.MoveToY(0, fade_in_duration, Easing.OutQuint); - this.FadeIn(fade_in_duration, Easing.OutQuint); - multiplierDisplay - .Delay(300) - .FadeIn(200, Easing.OutQuint) + .Delay(fade_in_duration * 0.65f) + .FadeIn(fade_in_duration / 2, Easing.OutQuint) .ScaleTo(1, fade_in_duration, Easing.OutElastic); for (int i = 0; i < columnFlow.Count; i++) { columnFlow[i].TopLevelContent .Delay(i * 30) - .MoveToY(0, fade_in_duration, Easing.OutQuint); + .MoveToY(0, fade_in_duration, Easing.OutQuint) + .FadeIn(fade_in_duration, Easing.OutQuint); } } @@ -311,21 +311,22 @@ namespace osu.Game.Overlays.Mods const double fade_out_duration = 500; base.PopOut(); + this.FadeOut(fade_out_duration, Easing.OutQuint); multiplierDisplay - .FadeOut(200, Easing.OutQuint) + .FadeOut(fade_out_duration / 2, Easing.OutQuint) .ScaleTo(0.75f, fade_out_duration, Easing.OutQuint); header.MoveToY(-header.DrawHeight, fade_out_duration, Easing.OutQuint); footer.MoveToY(footer.DrawHeight, fade_out_duration, Easing.OutQuint); - this.FadeOut(fade_out_duration, Easing.OutQuint); - for (int i = 0; i < columnFlow.Count; i++) { const float distance = 700; - columnFlow[i].TopLevelContent.MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint); + columnFlow[i].TopLevelContent + .MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint) + .FadeOut(fade_out_duration, Easing.OutQuint); } } From 631aa2a6ec8e35be514953c4c4b40e35a51cdb4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 18:44:25 +0900 Subject: [PATCH 0197/1276] Remove left padding to allow left-most column to exist further to the.. left --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index c43987ea60..62080ec1b5 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -123,7 +123,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Spacing = new Vector2(10, 0), - Margin = new MarginPadding { Horizontal = 70 }, + Margin = new MarginPadding { Right = 70 }, Children = new[] { new ModColumn(ModType.DifficultyReduction, false, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), From 32daf64a31a09cc459461957d0aa709c52f015f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 19:23:35 +0900 Subject: [PATCH 0198/1276] Use newly exposed framework helper function to find closest parent --- osu.Android.props | 2 +- .../Mods/NestedVerticalScrollContainer.cs | 16 +--------------- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 4 files changed, 5 insertions(+), 19 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 6a3b113fa2..a168d351fc 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs index c62b52ef27..1bf965beeb 100644 --- a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs +++ b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - parentScrollContainer = findClosestParent(); + parentScrollContainer = this.FindClosestParent(); } protected override bool OnScroll(ScrollEvent e) @@ -43,19 +43,5 @@ namespace osu.Game.Overlays.Mods return base.OnScroll(e); } - - // TODO: remove when https://github.com/ppy/osu-framework/pull/5092 is available. - private T? findClosestParent() where T : class, IDrawable - { - Drawable cursor = this; - - while ((cursor = cursor.Parent) != null) - { - if (cursor is T match) - return match; - } - - return default; - } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3c01f29671..e968c6602d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index c8f170497d..e441ed81ab 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From ac799aaf7a412db957203074b9110776ad61dfbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 19:25:47 +0900 Subject: [PATCH 0199/1276] Add missing newline --- osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs index 1bf965beeb..aba47d5423 100644 --- a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs +++ b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. #nullable enable + using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; From 74706f72e6450befc842511bdc0077b100180ac9 Mon Sep 17 00:00:00 2001 From: Perry MacMurray Date: Tue, 5 Apr 2022 15:10:55 -0400 Subject: [PATCH 0200/1276] Add 12-hour display setting (TODO add toggle) --- osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs index 81a362450c..b535c5c6a2 100644 --- a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs +++ b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs @@ -29,6 +29,8 @@ namespace osu.Game.Overlays.Toolbar } } + private bool format12H = true; + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -50,13 +52,17 @@ namespace osu.Game.Overlays.Toolbar protected override void UpdateDisplay(DateTimeOffset now) { - realTime.Text = $"{now:HH:mm:ss}"; + realTime.Text = format12H ? $"{now:hh:mm:ss tt}" : $"{now:HH:mm:ss}"; gameTime.Text = $"running {new TimeSpan(TimeSpan.TicksPerSecond * (int)(Clock.CurrentTime / 1000)):c}"; } private void updateMetrics() { - Width = showRuntime ? 66 : 45; // Allows for space for game time up to 99 days (in the padding area since this is quite rare). + if (format12H) + Width = 74; + else + Width = showRuntime ? 66 : 45; // Allows for space for game time up to 99 days (in the padding area since this is quite rare). + gameTime.FadeTo(showRuntime ? 1 : 0); } } From 9d475f7b336b196a06f99801a64c7cd280641d9a Mon Sep 17 00:00:00 2001 From: Perry MacMurray Date: Tue, 5 Apr 2022 16:21:28 -0400 Subject: [PATCH 0201/1276] Add config in settings for forcing 24H time, determines default off CurrentCulture --- osu.Game/Configuration/OsuConfigManager.cs | 3 +++ osu.Game/Localisation/UserInterfaceStrings.cs | 5 +++++ .../Sections/UserInterface/GeneralSettings.cs | 5 +++++ .../Overlays/Toolbar/DigitalClockDisplay.cs | 20 ++++++++++++++++--- osu.Game/Overlays/Toolbar/ToolbarClock.cs | 6 ++++++ 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 2f966ac0a9..99372c92d9 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -102,6 +102,8 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.MenuParallax, true); + SetDefault(OsuSetting.Prefer24HourTime, false); + // Gameplay SetDefault(OsuSetting.PositionalHitsounds, true); // replaced by level setting below, can be removed 20220703. SetDefault(OsuSetting.PositionalHitsoundsLevel, 0.2f, 0, 1); @@ -287,6 +289,7 @@ namespace osu.Game.Configuration MenuVoice, CursorRotation, MenuParallax, + Prefer24HourTime, BeatmapDetailTab, BeatmapDetailModsFilter, Username, diff --git a/osu.Game/Localisation/UserInterfaceStrings.cs b/osu.Game/Localisation/UserInterfaceStrings.cs index 4be403edb4..ac79f9f45d 100644 --- a/osu.Game/Localisation/UserInterfaceStrings.cs +++ b/osu.Game/Localisation/UserInterfaceStrings.cs @@ -24,6 +24,11 @@ namespace osu.Game.Localisation /// public static LocalisableString CursorRotation => new TranslatableString(getKey(@"cursor_rotation"), @"Rotate cursor when dragging"); + /// + /// "Prefer 24-hour time" + /// + public static LocalisableString Prefer24HourTime => new TranslatableString(getKey(@"prefer_24_hour_time"), @"Prefer 24-hour time"); + /// /// "Menu cursor size" /// diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 59894cbcae..d6d38c8e12 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -41,6 +41,11 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface Current = config.GetBindable(OsuSetting.UIHoldActivationDelay), KeyboardStep = 50 }, + new SettingsCheckbox + { + LabelText = UserInterfaceStrings.Prefer24HourTime, + Current = config.GetBindable(OsuSetting.Prefer24HourTime) + }, }; } } diff --git a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs index b535c5c6a2..66f40532d0 100644 --- a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs +++ b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs @@ -29,7 +29,21 @@ namespace osu.Game.Overlays.Toolbar } } - private bool format12H = true; + private bool format12H = false; + + public bool Format12H + { + get => format12H; + set + { + if (format12H == value) + return; + + format12H = value; + updateMetrics(); + UpdateDisplay(DateTimeOffset.Now); //Update realTime.Text immediately instead of waiting until next second + } + } [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -52,14 +66,14 @@ namespace osu.Game.Overlays.Toolbar protected override void UpdateDisplay(DateTimeOffset now) { - realTime.Text = format12H ? $"{now:hh:mm:ss tt}" : $"{now:HH:mm:ss}"; + realTime.Text = format12H ? $"{now:h:mm:ss tt}" : $"{now:HH:mm:ss}"; gameTime.Text = $"running {new TimeSpan(TimeSpan.TicksPerSecond * (int)(Clock.CurrentTime / 1000)):c}"; } private void updateMetrics() { if (format12H) - Width = 74; + Width = 70; else Width = showRuntime ? 66 : 45; // Allows for space for game time up to 99 days (in the padding area since this is quite rare). diff --git a/osu.Game/Overlays/Toolbar/ToolbarClock.cs b/osu.Game/Overlays/Toolbar/ToolbarClock.cs index 22a96603dc..b256d6fbc4 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarClock.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarClock.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Globalization; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -20,6 +21,7 @@ namespace osu.Game.Overlays.Toolbar public class ToolbarClock : OsuClickableContainer { private Bindable clockDisplayMode; + private Bindable digitalPrefer24Hour; private Box hoverBackground; private Box flashBackground; @@ -38,6 +40,7 @@ namespace osu.Game.Overlays.Toolbar private void load(OsuConfigManager config) { clockDisplayMode = config.GetBindable(OsuSetting.ToolbarClockDisplayMode); + digitalPrefer24Hour = config.GetBindable(OsuSetting.Prefer24HourTime); Children = new Drawable[] { @@ -94,6 +97,9 @@ namespace osu.Game.Overlays.Toolbar analog.FadeTo(showAnalog ? 1 : 0); }, true); + + digitalPrefer24Hour.BindValueChanged(prefer24H => + digital.Format12H = prefer24H.NewValue ? false : CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern.Contains("tt"), true); } protected override bool OnClick(ClickEvent e) From 2525ec0d18db76a64e50be272dbf457204e1579a Mon Sep 17 00:00:00 2001 From: Perry MacMurray Date: Tue, 5 Apr 2022 17:07:58 -0400 Subject: [PATCH 0202/1276] Switch 24H toggle from UI settings to language settings --- osu.Game/Localisation/GeneralSettingsStrings.cs | 5 +++++ osu.Game/Localisation/UserInterfaceStrings.cs | 5 ----- .../Settings/Sections/General/LanguageSettings.cs | 8 +++++++- .../Settings/Sections/UserInterface/GeneralSettings.cs | 5 ----- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs index a60e4891f4..cca8c8efd1 100644 --- a/osu.Game/Localisation/GeneralSettingsStrings.cs +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -29,6 +29,11 @@ namespace osu.Game.Localisation /// public static LocalisableString PreferOriginalMetadataLanguage => new TranslatableString(getKey(@"prefer_original"), @"Prefer metadata in original language"); + /// + /// "Prefer 24-hour time" + /// + public static LocalisableString Prefer24HourTime => new TranslatableString(getKey(@"prefer_24_hour_time"), @"Prefer 24-hour time"); + /// /// "Updates" /// diff --git a/osu.Game/Localisation/UserInterfaceStrings.cs b/osu.Game/Localisation/UserInterfaceStrings.cs index ac79f9f45d..4be403edb4 100644 --- a/osu.Game/Localisation/UserInterfaceStrings.cs +++ b/osu.Game/Localisation/UserInterfaceStrings.cs @@ -24,11 +24,6 @@ namespace osu.Game.Localisation /// public static LocalisableString CursorRotation => new TranslatableString(getKey(@"cursor_rotation"), @"Rotate cursor when dragging"); - /// - /// "Prefer 24-hour time" - /// - public static LocalisableString Prefer24HourTime => new TranslatableString(getKey(@"prefer_24_hour_time"), @"Prefer 24-hour time"); - /// /// "Menu cursor size" /// diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index 200618c469..e7cc1bce10 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Game.Configuration; using osu.Game.Extensions; using osu.Game.Localisation; @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections.General protected override LocalisableString Header => GeneralSettingsStrings.LanguageHeader; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig) + private void load(FrameworkConfigManager frameworkConfig, OsuConfigManager config) { frameworkLocale = frameworkConfig.GetBindable(FrameworkSetting.Locale); @@ -34,6 +35,11 @@ namespace osu.Game.Overlays.Settings.Sections.General LabelText = GeneralSettingsStrings.PreferOriginalMetadataLanguage, Current = frameworkConfig.GetBindable(FrameworkSetting.ShowUnicode) }, + new SettingsCheckbox + { + LabelText = GeneralSettingsStrings.Prefer24HourTime, + Current = config.GetBindable(OsuSetting.Prefer24HourTime) + }, }; if (!LanguageExtensions.TryParseCultureCode(frameworkLocale.Value, out var locale)) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index d6d38c8e12..59894cbcae 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -41,11 +41,6 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface Current = config.GetBindable(OsuSetting.UIHoldActivationDelay), KeyboardStep = 50 }, - new SettingsCheckbox - { - LabelText = UserInterfaceStrings.Prefer24HourTime, - Current = config.GetBindable(OsuSetting.Prefer24HourTime) - }, }; } } From 01da3924ccf7ccf91431ffb22db3fd15cf468b50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 11:32:35 +0900 Subject: [PATCH 0203/1276] Simplify `IsCurrentScreen` check to only apply to relevant call --- .../Multiplayer/MultiplayerMatchSongSelect.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 2e8f625eba..289b8730ce 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -74,18 +74,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer task.FireAndForget(onSuccess: () => Schedule(() => { + loadingLayer.Hide(); + // If an error or server side trigger occurred this screen may have already exited by external means. if (!this.IsCurrentScreen()) - return; - - loadingLayer.Hide(); - this.Exit(); + this.Exit(); }), onError: _ => Schedule(() => { - // If an error or server side trigger occurred this screen may have already exited by external means. - if (!this.IsCurrentScreen()) - return; - loadingLayer.Hide(); Carousel.AllowSelection = true; })); From f795f77cf99d5dbd902860f7e685b1d0c2130393 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 14:00:54 +0900 Subject: [PATCH 0204/1276] Add missing newline --- osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs index 98c593236a..4729765084 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. #nullable enable + using System; using System.Diagnostics; using System.Threading.Tasks; From 220d7bc6db4a2fbfb5c84a58c7c7058c3b7218f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 14:01:25 +0900 Subject: [PATCH 0205/1276] Fix dangerous realm operation in `TestSceneMultiplayerMatchSongSelect` The import process was running on the async load thread, but then accessed from the access thread later on. This seemed to somehow pass fine in headless runs, but would fail on visual test execution (specifically on `TestBeatmapConfirmed()`). --- .../Multiplayer/TestSceneMultiplayerMatchSongSelect.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 381b9b58bd..84c53ce22e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -15,6 +15,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Online.Rooms; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; @@ -35,10 +36,12 @@ namespace osu.Game.Tests.Visual.Multiplayer private BeatmapManager manager; private RulesetStore rulesets; - private List beatmaps; + private IList beatmaps => importedBeatmapSet?.PerformRead(s => s.Beatmaps) ?? new List(); private TestMultiplayerMatchSongSelect songSelect; + private Live importedBeatmapSet; + [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -46,8 +49,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); - beatmaps = new List(); - var metadata = new BeatmapMetadata { Artist = "Some Artist", @@ -79,11 +80,10 @@ namespace osu.Game.Tests.Visual.Multiplayer Difficulty = new BeatmapDifficulty() }; - beatmaps.Add(beatmap); beatmapSetInfo.Beatmaps.Add(beatmap); } - manager.Import(beatmapSetInfo); + importedBeatmapSet = manager.Import(beatmapSetInfo); } public override void SetUpSteps() From 552ec5282f1d7fcb9a11cac2859f0ed570d3abde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 14:44:00 +0900 Subject: [PATCH 0206/1276] Change `WorkingBeatmap.GetVirtualTrack` to use length provided by `BeatmapInfo` A lot of tests are using test resources that populate the length field, but do not populate hitobjects. The general expectation is that components should be using the cached length in cases where hitobjects are not relevant, but `GetVirtualTrack` was doing its own local calculation. This could cause tests to fail due to `MusicController` changing track in the background. --- osu.Game/Beatmaps/WorkingBeatmap.cs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 397d47c389..bb64ec796c 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -17,7 +17,6 @@ using osu.Framework.Logging; using osu.Framework.Testing; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osu.Game.Storyboards; @@ -152,24 +151,7 @@ namespace osu.Game.Beatmaps { const double excess_length = 1000; - var lastObject = Beatmap?.HitObjects.LastOrDefault(); - - double length; - - switch (lastObject) - { - case null: - length = emptyLength; - break; - - case IHasDuration endTime: - length = endTime.EndTime + excess_length; - break; - - default: - length = lastObject.StartTime + excess_length; - break; - } + double length = (BeatmapInfo?.Length + excess_length) ?? emptyLength; return audioManager.Tracks.GetVirtual(length); } From d17890ca9acd9d30ce2269dab25712cce966cbe1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 14:51:08 +0900 Subject: [PATCH 0207/1276] Replace a couple more local test beatmap cases which can instead use `TestResources` methods --- .../TestSceneMultiplayerMatchSongSelect.cs | 39 +------------------ .../TestSceneDeleteLocalScore.cs | 17 +------- 2 files changed, 3 insertions(+), 53 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 84c53ce22e..714951cc42 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -8,12 +8,10 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.Rooms; @@ -28,6 +26,7 @@ using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.Select; +using osu.Game.Tests.Resources; namespace osu.Game.Tests.Visual.Multiplayer { @@ -49,41 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(Realm); - var metadata = new BeatmapMetadata - { - Artist = "Some Artist", - Title = "Some Beatmap", - Author = { Username = "Some Author" }, - }; - - var beatmapSetInfo = new BeatmapSetInfo - { - OnlineID = 10, - Hash = Guid.NewGuid().ToString().ComputeMD5Hash(), - DateAdded = DateTimeOffset.UtcNow - }; - - for (int i = 0; i < 8; ++i) - { - int beatmapId = 10 * 10 + i; - - int length = RNG.Next(30000, 200000); - double bpm = RNG.NextSingle(80, 200); - - var beatmap = new BeatmapInfo - { - Ruleset = rulesets.GetRuleset(i % 4) ?? throw new InvalidOperationException(), - OnlineID = beatmapId, - Length = length, - BPM = bpm, - Metadata = metadata, - Difficulty = new BeatmapDifficulty() - }; - - beatmapSetInfo.Beatmaps.Add(beatmap); - } - - importedBeatmapSet = manager.Import(beatmapSetInfo); + importedBeatmapSet = manager.Import(TestResources.CreateTestBeatmapSetInfo(8, rulesets.AvailableRulesets.ToArray())); } public override void SetUpSteps() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 13404a9810..72dfc53c01 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -17,7 +16,6 @@ using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; -using osu.Game.Models; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; using osu.Game.Overlays; @@ -60,20 +58,7 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.Centre, Size = new Vector2(550f, 450f), Scope = BeatmapLeaderboardScope.Local, - BeatmapInfo = new BeatmapInfo - { - ID = Guid.NewGuid(), - Metadata = new BeatmapMetadata - { - Title = "TestSong", - Artist = "TestArtist", - Author = new RealmUser - { - Username = "TestAuthor" - }, - }, - DifficultyName = "Insane" - }, + BeatmapInfo = TestResources.CreateTestBeatmapSetInfo().Beatmaps.First() } }, dialogOverlay = new DialogOverlay() From 2b8a5833ddd4794c89fbb452d6a7d2d52bb107be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 15:13:02 +0900 Subject: [PATCH 0208/1276] Fix back-to-front conditional check --- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 289b8730ce..848424bc76 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer loadingLayer.Hide(); // If an error or server side trigger occurred this screen may have already exited by external means. - if (!this.IsCurrentScreen()) + if (this.IsCurrentScreen()) this.Exit(); }), onError: _ => Schedule(() => { From c2b2d443ed070da9b8fafa5b871e235f40ca13f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 15:31:12 +0900 Subject: [PATCH 0209/1276] Add more comprehensive assert output to try and discern CI issues --- .../Visual/Gameplay/TestSceneLeadIn.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index 5a1fc1b1e5..58d2e4a1da 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -36,10 +36,10 @@ namespace osu.Game.Tests.Visual.Gameplay BeatmapInfo = { AudioLeadIn = leadIn } }); - AddAssert($"first frame is {expectedStartTime}", () => + AddStep($"check first frame time", () => { - Debug.Assert(player.FirstFrameClockTime != null); - return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms); + Assert.That(player.FirstFrameClockTime, Is.Not.Null); + Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); }); } @@ -59,10 +59,10 @@ namespace osu.Game.Tests.Visual.Gameplay loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard); - AddAssert($"first frame is {expectedStartTime}", () => + AddStep($"check first frame time", () => { - Debug.Assert(player.FirstFrameClockTime != null); - return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms); + Assert.That(player.FirstFrameClockTime, Is.Not.Null); + Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); }); } @@ -97,10 +97,10 @@ namespace osu.Game.Tests.Visual.Gameplay loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard); - AddAssert($"first frame is {expectedStartTime}", () => + AddStep($"check first frame time", () => { - Debug.Assert(player.FirstFrameClockTime != null); - return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms); + Assert.That(player.FirstFrameClockTime, Is.Not.Null); + Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); }); } From 065bb60324c6f62393aee92799b4a6d916a9fa77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 16:05:11 +0900 Subject: [PATCH 0210/1276] Remove unused using statements --- osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index 58d2e4a1da..5ec9c6c474 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -1,12 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Diagnostics; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Timing; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Osu; From c42ef43faaeedd1ff18ccd6c1ceb16bd0e44a790 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 16:46:52 +0900 Subject: [PATCH 0211/1276] Ensure intro beatmap has protected flag set In cases this isn't set, the beatmap has likely entered a bad state. Closes https://github.com/ppy/osu/issues/17659. --- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index a6b54dd1f2..e569b0e517 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -147,7 +147,7 @@ namespace osu.Game.Screens.Menu bool loadThemedIntro() { - var setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == BeatmapHash); + var setInfo = beatmaps.QueryBeatmapSet(b => b.Protected && b.Hash == BeatmapHash); if (setInfo == null) return false; From 43724f477a1cb7870a64d0878159ad4606678d87 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 6 Apr 2022 19:14:00 +0800 Subject: [PATCH 0212/1276] Remove outdated NU1701 warning. --- Directory.Build.props | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5bdf12218c..3ab3dc3d13 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -26,14 +26,6 @@ true $(NoWarn);CS1591 - - - $(NoWarn);NU1701 - false ppy Pty Ltd From ec4f1bcbc8209dce61b5cc8ce6862a15aaa2d50c Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 6 Apr 2022 19:18:18 +0800 Subject: [PATCH 0213/1276] Remove outdated NETCore packages. --- osu.Desktop/osu.Desktop.csproj | 2 -- osu.Game/osu.Game.csproj | 1 - 2 files changed, 3 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index a06484214b..3ca6411812 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -25,7 +25,6 @@ - @@ -33,7 +32,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7b0f8c72c5..db1a945765 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,6 @@ - all From edb556643edf509ed77eb3da3e926658d08e45b6 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 5 Apr 2022 01:00:33 -0700 Subject: [PATCH 0214/1276] Add failing replay button enabled test asserts --- osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 8b7e1c4e58..5d25287e45 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -150,10 +150,12 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("import score", () => imported = scoreManager.Import(getScoreInfo(true))); AddUntilStep("state is available", () => downloadButton.State.Value == DownloadState.LocallyAvailable); + AddAssert("button is enabled", () => downloadButton.ChildrenOfType().First().Enabled.Value); AddStep("delete score", () => scoreManager.Delete(imported.Value)); AddUntilStep("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded); + AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType().First().Enabled.Value); } [Test] From 4432a93d096ed81f1a94d11914c48e19c1e346dd Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 5 Apr 2022 01:04:19 -0700 Subject: [PATCH 0215/1276] Fix replay button being disabled when available locally but not online --- osu.Game/Screens/Ranking/ReplayDownloadButton.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index 6a74fdaf75..222115daca 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -87,19 +87,20 @@ namespace osu.Game.Screens.Ranking }); } - button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable; - updateTooltip(); + updateState(); }, true); State.BindValueChanged(state => { button.State.Value = state.NewValue; - updateTooltip(); + updateState(); }, true); } - private void updateTooltip() + private void updateState() { + button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable; + switch (replayAvailability) { case ReplayAvailability.Local: From 8a73831115ab15eff3059e938786c235646862b6 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 7 Apr 2022 13:12:33 +0900 Subject: [PATCH 0216/1276] Add MP lobby 'warning' SFX for the final seconds of countdown --- .../Multiplayer/Match/MultiplayerReadyButton.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index d275f309cb..22a0243f8f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -30,13 +30,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private MultiplayerRoom room => multiplayerClient.Room; private Sample countdownTickSample; + private Sample countdownWarnSample; + private Sample countdownWarnFinalSample; [BackgroundDependencyLoader] private void load(AudioManager audio) { countdownTickSample = audio.Samples.Get(@"Multiplayer/countdown-tick"); - // disabled for now pending further work on sound effect - // countdownTickFinalSample = audio.Samples.Get(@"Multiplayer/countdown-tick-final"); + countdownWarnSample = audio.Samples.Get(@"Multiplayer/countdown-warn"); + countdownWarnFinalSample = audio.Samples.Get(@"Multiplayer/countdown-warn-final"); } protected override void LoadComplete() @@ -102,8 +104,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void playTickSound(int secondsRemaining) { if (secondsRemaining < 10) countdownTickSample?.Play(); - // disabled for now pending further work on sound effect - // if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); + + if (secondsRemaining <= 3) + { + if (secondsRemaining > 0) + countdownWarnSample?.Play(); + else + countdownWarnFinalSample?.Play(); + } } private void updateButtonText() From 358931842fc1936b8141cf58d6fe2c67ca6851ce Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 6 Apr 2022 21:22:56 -0700 Subject: [PATCH 0217/1276] Move enabled setting to each case --- osu.Game/Screens/Ranking/ReplayDownloadButton.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index 222115daca..0c9c909395 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -99,20 +99,21 @@ namespace osu.Game.Screens.Ranking private void updateState() { - button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable; - switch (replayAvailability) { case ReplayAvailability.Local: button.TooltipText = @"watch replay"; + button.Enabled.Value = true; break; case ReplayAvailability.Online: button.TooltipText = @"download replay"; + button.Enabled.Value = true; break; default: button.TooltipText = @"replay unavailable"; + button.Enabled.Value = false; break; } } From 9ed4f310488e9c53223893674d21dea7f2a9617f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 13:35:07 +0900 Subject: [PATCH 0218/1276] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f1cbe1c9eb..10685d5990 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a009307cfe..7fd2647a8a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 6e35fba86f..a5efc40d51 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From b7f8716de9cd1f32c4ea9ad7f6b32b43b842167c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 14:10:08 +0900 Subject: [PATCH 0219/1276] Add test coverage of skin lookups failing when `@2x` and extension are present --- .../Skinning/LegacySkinTextureFallbackTest.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs index 7516e7500b..db7fc0b4a0 100644 --- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs +++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs @@ -69,6 +69,20 @@ namespace osu.Game.Tests.NonVisual.Skinning "Gameplay/osu/followpoint", "followpoint", 1 }, + new object[] + { + // Looking up a filename with extension specified should work. + new[] { "followpoint.png" }, + "followpoint.png", + "followpoint.png", 1 + }, + new object[] + { + // Looking up a filename with extension specified should also work with @2x sprites. + new[] { "followpoint@2x.png" }, + "followpoint.png", + "followpoint@2x.png", 2 + }, }; [TestCaseSource(nameof(fallbackTestCases))] From 205edb65a2d800eb7bf54bdf3d1b8e56f4c81f19 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 14:16:16 +0900 Subject: [PATCH 0220/1276] Fix filename lookups on `LegacySkin`s going awry when extension is specified Due to the logic present to handle `@2x` fallback, the extension was potentially being added at the wrong point in the filename. This change ensures that the lookup filenames are always correct. Closes https://github.com/ppy/osu/issues/17690. --- osu.Game/Skinning/LegacySkin.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 92713023f4..a97b5d44ce 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -443,7 +443,11 @@ namespace osu.Game.Skinning string lookupName = name.Replace(@"@2x", string.Empty); float ratio = 2; - var texture = Textures?.Get(@$"{lookupName}@2x", wrapModeS, wrapModeT); + string twoTimesFilename = Path.HasExtension(lookupName) + ? @$"{Path.GetFileNameWithoutExtension(lookupName)}@2x{Path.GetExtension(lookupName)}" + : @$"{lookupName}@2x"; + + var texture = Textures?.Get(twoTimesFilename, wrapModeS, wrapModeT); if (texture == null) { From a7e262627fe34e05c91497366b67054beb962fd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 15:13:39 +0900 Subject: [PATCH 0221/1276] Fix `DrawableStoryboardAnimation` not specifying sizing correctly Usually this would be handled by `TextureAnimation`, but because we are inheriting from `DrawableAnimation` here for reasons, we needed to implement this ourselves. Follows the implementation in `TextureAnimation`. --- .../Drawables/DrawableStoryboardAnimation.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index 88cb5f40a1..2ef6f28720 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -5,8 +5,10 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Utils; +using osu.Game.Skinning; using osuTK; namespace osu.Game.Storyboards.Drawables @@ -88,6 +90,14 @@ namespace osu.Game.Storyboards.Drawables LifetimeEnd = animation.EndTime; } + protected override Vector2 GetCurrentDisplaySize() + { + Texture texture = (CurrentFrame as Sprite)?.Texture + ?? ((CurrentFrame as SkinnableSprite)?.Drawable as Sprite)?.Texture; + + return new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0); + } + [BackgroundDependencyLoader] private void load(TextureStore textureStore, Storyboard storyboard) { From 93bdca5211c52c37c5e0fc358693affe9366ad5b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 17:26:23 +0900 Subject: [PATCH 0222/1276] Split out `GetTextures` helper function for `LegacySkin`s --- osu.Game/Skinning/LegacySkinExtensions.cs | 86 ++++++++++++++--------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index 479afabb00..514a06a4ee 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -18,39 +20,32 @@ namespace osu.Game.Skinning { public static class LegacySkinExtensions { - [CanBeNull] - public static Drawable GetAnimation(this ISkin source, string componentName, bool animatable, bool looping, bool applyConfigFrameRate = false, string animationSeparator = "-", - bool startAtCurrentTime = true, double? frameLength = null) + public static Drawable? GetAnimation(this ISkin? source, string componentName, bool animatable, bool looping, bool applyConfigFrameRate = false, string animationSeparator = "-", + bool startAtCurrentTime = true, double? frameLength = null) => source.GetAnimation(componentName, default, default, animatable, looping, applyConfigFrameRate, animationSeparator, startAtCurrentTime, frameLength); - [CanBeNull] - public static Drawable GetAnimation(this ISkin source, string componentName, WrapMode wrapModeS, WrapMode wrapModeT, bool animatable, bool looping, bool applyConfigFrameRate = false, - string animationSeparator = "-", - bool startAtCurrentTime = true, double? frameLength = null) + public static Drawable? GetAnimation(this ISkin? source, string componentName, WrapMode wrapModeS, WrapMode wrapModeT, bool animatable, bool looping, bool applyConfigFrameRate = false, + string animationSeparator = "-", bool startAtCurrentTime = true, double? frameLength = null) { - Texture texture; - - // find the first source which provides either the animated or non-animated version. - ISkin skin = (source as ISkinSource)?.FindProvider(s => - { - if (animatable && s.GetTexture(getFrameName(0)) != null) - return true; - - return s.GetTexture(componentName, wrapModeS, wrapModeT) != null; - }) ?? source; - - if (skin == null) + if (source == null) return null; - if (animatable) - { - var textures = getTextures().ToArray(); + var textures = GetTextures(source, componentName, wrapModeS, wrapModeT, animatable, animationSeparator, out var retrievalSource); + + switch (textures.Length) + { + case 0: + return null; + + case 1: + return new Sprite { Texture = textures[0] }; + + default: + Debug.Assert(retrievalSource != null); - if (textures.Length > 0) - { var animation = new SkinnableTextureAnimation(startAtCurrentTime) { - DefaultFrameLength = frameLength ?? getFrameLength(skin, applyConfigFrameRate, textures), + DefaultFrameLength = frameLength ?? getFrameLength(retrievalSource, applyConfigFrameRate, textures), Loop = looping, }; @@ -58,19 +53,46 @@ namespace osu.Game.Skinning animation.AddFrame(t); return animation; - } + } + } + + public static Texture[] GetTextures(this ISkin? source, string componentName, WrapMode wrapModeS, WrapMode wrapModeT, bool animatable, string animationSeparator, out ISkin? retrievalSource) + { + retrievalSource = null; + + if (source == null) + return Array.Empty(); + + // find the first source which provides either the animated or non-animated version. + retrievalSource = (source as ISkinSource)?.FindProvider(s => + { + if (animatable && s.GetTexture(getFrameName(0)) != null) + return true; + + return s.GetTexture(componentName, wrapModeS, wrapModeT) != null; + }) ?? source; + + if (animatable) + { + var textures = getTextures(retrievalSource).ToArray(); + + if (textures.Length > 0) + return textures; } // if an animation was not allowed or not found, fall back to a sprite retrieval. - if ((texture = skin.GetTexture(componentName, wrapModeS, wrapModeT)) != null) - return new Sprite { Texture = texture }; + var singleTexture = retrievalSource.GetTexture(componentName, wrapModeS, wrapModeT); - return null; + return singleTexture != null + ? new[] { singleTexture } + : Array.Empty(); - IEnumerable getTextures() + IEnumerable getTextures(ISkin skin) { for (int i = 0; true; i++) { + Texture? texture; + if ((texture = skin.GetTexture(getFrameName(i), wrapModeS, wrapModeT)) == null) break; @@ -130,7 +152,7 @@ namespace osu.Game.Skinning public class SkinnableTextureAnimation : TextureAnimation { [Resolved(canBeNull: true)] - private IAnimationTimeReference timeReference { get; set; } + private IAnimationTimeReference? timeReference { get; set; } private readonly Bindable animationStartTime = new BindableDouble(); From cbc4e5319d1fa8d7b0f77ca9a5549cc56acaedad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 17:26:31 +0900 Subject: [PATCH 0223/1276] Fix `DrawableStoryboardAnimation` to handle skin fallback frame count similar to stable Reasoning is explained in the inline comment (basically, stable doesn't care what the user specifies as the frame count when falling back to skin resources). This change also removes on to two layers of drawables, which should be a win in heavy storyboards. --- .../Drawables/DrawableStoryboardAnimation.cs | 47 +++++++++++++++++-- .../Drawables/DrawableStoryboardSprite.cs | 27 ++++++++--- osu.Game/Storyboards/Storyboard.cs | 20 ++------ 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index 2ef6f28720..28e65a7910 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.IO; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; @@ -98,17 +99,55 @@ namespace osu.Game.Storyboards.Drawables return new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0); } + [Resolved] + private ISkinSource skin { get; set; } + [BackgroundDependencyLoader] private void load(TextureStore textureStore, Storyboard storyboard) { - for (int frameIndex = 0; frameIndex < Animation.FrameCount; frameIndex++) + int frameIndex = 0; + + Texture frameTexture = storyboard.GetTextureFromPath(getFramePath(frameIndex), textureStore); + + if (frameTexture != null) { - string framePath = Animation.Path.Replace(".", frameIndex + "."); - Drawable frame = storyboard.CreateSpriteFromResourcePath(framePath, textureStore) ?? Empty(); - AddFrame(frame, Animation.FrameDelay); + // sourcing from storyboard. + for (frameIndex = 0; frameIndex < Animation.FrameCount; frameIndex++) + { + frameTexture = storyboard.GetTextureFromPath(getFramePath(frameIndex), textureStore); + AddFrame(new Sprite { Texture = frameTexture }, Animation.FrameDelay); + } + } + else if (storyboard.UseSkinSprites) + { + // fallback to skin if required. + skin.SourceChanged += skinSourceChanged; + skinSourceChanged(); } Animation.ApplyTransforms(this); } + + private void skinSourceChanged() + { + ClearFrames(); + + // ClearFrames doesn't clear the last displayed frame. + // Clear manually for now, in case the skin doesn't provide any frames. + DisplayFrame(Empty()); + + // When reading from a skin, we match stables weird behaviour where `FrameCount` is ignored + // and resources are retrieved until the end of the animation. + foreach (var texture in skin.GetTextures(Path.GetFileNameWithoutExtension(Animation.Path), default, default, true, string.Empty, out _)) + AddFrame(new Sprite { Texture = texture }, Animation.FrameDelay); + } + + private string getFramePath(int i) => Animation.Path.Replace(".", $"{i}."); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + skin.SourceChanged -= skinSourceChanged; + } } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index db10f13896..6622cfb6be 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -4,14 +4,15 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Utils; +using osu.Game.Skinning; using osuTK; namespace osu.Game.Storyboards.Drawables { - public class DrawableStoryboardSprite : CompositeDrawable, IFlippable, IVectorScalable + public class DrawableStoryboardSprite : Sprite, IFlippable, IVectorScalable { public StoryboardSprite Sprite { get; } @@ -85,19 +86,31 @@ namespace osu.Game.Storyboards.Drawables LifetimeStart = sprite.StartTime; LifetimeEnd = sprite.EndTime; - - AutoSizeAxes = Axes.Both; } + [Resolved] + private ISkinSource skin { get; set; } + [BackgroundDependencyLoader] private void load(TextureStore textureStore, Storyboard storyboard) { - var drawable = storyboard.CreateSpriteFromResourcePath(Sprite.Path, textureStore); + Texture = storyboard.GetTextureFromPath(Sprite.Path, textureStore); - if (drawable != null) - InternalChild = drawable; + if (Texture == null && storyboard.UseSkinSprites) + { + skin.SourceChanged += skinSourceChanged; + skinSourceChanged(); + } Sprite.ApplyTransforms(this); } + + private void skinSourceChanged() => Texture = skin.GetTexture(Sprite.Path); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + skin.SourceChanged -= skinSourceChanged; + } } } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index b662b98e4e..1d21b5dce2 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -4,13 +4,10 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Extensions; using osu.Game.Rulesets.Mods; -using osu.Game.Skinning; using osu.Game.Storyboards.Drawables; namespace osu.Game.Storyboards @@ -94,25 +91,14 @@ namespace osu.Game.Storyboards public DrawableStoryboard CreateDrawable(IReadOnlyList mods = null) => new DrawableStoryboard(this, mods); - public Drawable CreateSpriteFromResourcePath(string path, TextureStore textureStore) + public Texture GetTextureFromPath(string path, TextureStore textureStore) { - Drawable drawable = null; - string storyboardPath = BeatmapInfo.BeatmapSet?.Files.FirstOrDefault(f => f.Filename.Equals(path, StringComparison.OrdinalIgnoreCase))?.File.GetStoragePath(); if (!string.IsNullOrEmpty(storyboardPath)) - drawable = new Sprite { Texture = textureStore.Get(storyboardPath) }; - // if the texture isn't available locally in the beatmap, some storyboards choose to source from the underlying skin lookup hierarchy. - else if (UseSkinSprites) - { - drawable = new SkinnableSprite(path) - { - RelativeSizeAxes = Axes.None, - AutoSizeAxes = Axes.Both, - }; - } + return textureStore.Get(storyboardPath); - return drawable; + return null; } } } From 040afff670d37335aaa5b8f5aab4ec21af087517 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 17:34:50 +0900 Subject: [PATCH 0224/1276] Change `DrawableStoryboardAnimation` to derive from `TextureAnimation` --- .../Drawables/DrawableStoryboardAnimation.cs | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index 28e65a7910..5ebca4d860 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -6,7 +6,6 @@ using System.IO; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Utils; using osu.Game.Skinning; @@ -14,7 +13,7 @@ using osuTK; namespace osu.Game.Storyboards.Drawables { - public class DrawableStoryboardAnimation : DrawableAnimation, IFlippable, IVectorScalable + public class DrawableStoryboardAnimation : TextureAnimation, IFlippable, IVectorScalable { public StoryboardAnimation Animation { get; } @@ -91,14 +90,6 @@ namespace osu.Game.Storyboards.Drawables LifetimeEnd = animation.EndTime; } - protected override Vector2 GetCurrentDisplaySize() - { - Texture texture = (CurrentFrame as Sprite)?.Texture - ?? ((CurrentFrame as SkinnableSprite)?.Drawable as Sprite)?.Texture; - - return new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0); - } - [Resolved] private ISkinSource skin { get; set; } @@ -114,8 +105,7 @@ namespace osu.Game.Storyboards.Drawables // sourcing from storyboard. for (frameIndex = 0; frameIndex < Animation.FrameCount; frameIndex++) { - frameTexture = storyboard.GetTextureFromPath(getFramePath(frameIndex), textureStore); - AddFrame(new Sprite { Texture = frameTexture }, Animation.FrameDelay); + AddFrame(storyboard.GetTextureFromPath(getFramePath(frameIndex), textureStore), Animation.FrameDelay); } } else if (storyboard.UseSkinSprites) @@ -132,14 +122,10 @@ namespace osu.Game.Storyboards.Drawables { ClearFrames(); - // ClearFrames doesn't clear the last displayed frame. - // Clear manually for now, in case the skin doesn't provide any frames. - DisplayFrame(Empty()); - // When reading from a skin, we match stables weird behaviour where `FrameCount` is ignored // and resources are retrieved until the end of the animation. foreach (var texture in skin.GetTextures(Path.GetFileNameWithoutExtension(Animation.Path), default, default, true, string.Empty, out _)) - AddFrame(new Sprite { Texture = texture }, Animation.FrameDelay); + AddFrame(texture, Animation.FrameDelay); } private string getFramePath(int i) => Animation.Path.Replace(".", $"{i}."); From 0674862b6c69ab7df325fa9d7506deee79b36aae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 17:51:10 +0900 Subject: [PATCH 0225/1276] Fix failing tests --- .../Gameplay/TestSceneDrawableStoryboardSprite.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs index 34e6d1996d..cfe9e35298 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Rulesets; @@ -36,7 +37,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); - assertSpritesFromSkin(false); + AddAssert("sprite didn't find texture", () => + sprites.All(sprite => sprite.ChildrenOfType().All(s => s.Texture == null))); } [Test] @@ -48,9 +50,12 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); - assertSpritesFromSkin(true); + // Only checking for at least one sprite that succeeded, as not all skins in this test provide the hitcircleoverlay texture. + AddAssert("sprite found texture", () => + sprites.Any(sprite => sprite.ChildrenOfType().All(s => s.Texture != null))); - AddAssert("skinnable sprite has correct size", () => sprites.Any(s => Precision.AlmostEquals(s.ChildrenOfType().Single().Size, new Vector2(128, 128)))); + AddAssert("skinnable sprite has correct size", () => + sprites.Any(sprite => sprite.ChildrenOfType().All(s => s.Size == new Vector2(128)))); } [Test] @@ -104,9 +109,5 @@ namespace osu.Game.Tests.Visual.Gameplay s.LifetimeStart = double.MinValue; s.LifetimeEnd = double.MaxValue; }); - - private void assertSpritesFromSkin(bool fromSkin) => - AddAssert($"sprites are {(fromSkin ? "from skin" : "from storyboard")}", - () => sprites.All(sprite => sprite.ChildrenOfType().Any() == fromSkin)); } } From 37509e04a934ec1e51c080866316589d69a70003 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 18:11:30 +0900 Subject: [PATCH 0226/1276] Rename variable and fix inspections --- osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs | 14 +++++++------- osu.Game/Overlays/Toolbar/ToolbarClock.cs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs index 66f40532d0..4392c5811a 100644 --- a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs +++ b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs @@ -29,17 +29,17 @@ namespace osu.Game.Overlays.Toolbar } } - private bool format12H = false; + private bool use24HourDisplay; - public bool Format12H + public bool Use24HourDisplay { - get => format12H; + get => use24HourDisplay; set { - if (format12H == value) + if (use24HourDisplay == value) return; - format12H = value; + use24HourDisplay = value; updateMetrics(); UpdateDisplay(DateTimeOffset.Now); //Update realTime.Text immediately instead of waiting until next second } @@ -66,13 +66,13 @@ namespace osu.Game.Overlays.Toolbar protected override void UpdateDisplay(DateTimeOffset now) { - realTime.Text = format12H ? $"{now:h:mm:ss tt}" : $"{now:HH:mm:ss}"; + realTime.Text = use24HourDisplay ? $"{now:h:mm:ss tt}" : $"{now:HH:mm:ss}"; gameTime.Text = $"running {new TimeSpan(TimeSpan.TicksPerSecond * (int)(Clock.CurrentTime / 1000)):c}"; } private void updateMetrics() { - if (format12H) + if (use24HourDisplay) Width = 70; else Width = showRuntime ? 66 : 45; // Allows for space for game time up to 99 days (in the padding area since this is quite rare). diff --git a/osu.Game/Overlays/Toolbar/ToolbarClock.cs b/osu.Game/Overlays/Toolbar/ToolbarClock.cs index b256d6fbc4..264e4bffde 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarClock.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarClock.cs @@ -99,7 +99,7 @@ namespace osu.Game.Overlays.Toolbar }, true); digitalPrefer24Hour.BindValueChanged(prefer24H => - digital.Format12H = prefer24H.NewValue ? false : CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern.Contains("tt"), true); + digital.Use24HourDisplay = !prefer24H.NewValue && CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern.Contains("tt"), true); } protected override bool OnClick(ClickEvent e) From 7530fe5adf4ea66d7f5cc02034aefef8eed0c969 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 18:13:23 +0900 Subject: [PATCH 0227/1276] Move default specification to configuration initial value --- osu.Game/Configuration/OsuConfigManager.cs | 4 +++- osu.Game/Overlays/Toolbar/ToolbarClock.cs | 8 +++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 99372c92d9..9aacb50684 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Globalization; using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; using osu.Framework.Extensions; @@ -102,7 +103,8 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.MenuParallax, true); - SetDefault(OsuSetting.Prefer24HourTime, false); + // See https://stackoverflow.com/a/63307411 for default sourcing. + SetDefault(OsuSetting.Prefer24HourTime, CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern.Contains(@"tt")); // Gameplay SetDefault(OsuSetting.PositionalHitsounds, true); // replaced by level setting below, can be removed 20220703. diff --git a/osu.Game/Overlays/Toolbar/ToolbarClock.cs b/osu.Game/Overlays/Toolbar/ToolbarClock.cs index 264e4bffde..d726a95f46 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarClock.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarClock.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Globalization; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -21,7 +20,7 @@ namespace osu.Game.Overlays.Toolbar public class ToolbarClock : OsuClickableContainer { private Bindable clockDisplayMode; - private Bindable digitalPrefer24Hour; + private Bindable prefer24HourTime; private Box hoverBackground; private Box flashBackground; @@ -40,7 +39,7 @@ namespace osu.Game.Overlays.Toolbar private void load(OsuConfigManager config) { clockDisplayMode = config.GetBindable(OsuSetting.ToolbarClockDisplayMode); - digitalPrefer24Hour = config.GetBindable(OsuSetting.Prefer24HourTime); + prefer24HourTime = config.GetBindable(OsuSetting.Prefer24HourTime); Children = new Drawable[] { @@ -98,8 +97,7 @@ namespace osu.Game.Overlays.Toolbar analog.FadeTo(showAnalog ? 1 : 0); }, true); - digitalPrefer24Hour.BindValueChanged(prefer24H => - digital.Use24HourDisplay = !prefer24H.NewValue && CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern.Contains("tt"), true); + prefer24HourTime.BindValueChanged(prefer24H => digital.Use24HourDisplay = !prefer24H.NewValue, true); } protected override bool OnClick(ClickEvent e) From 555aee7b6a98056587d1077a71faff2c44f0b0a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 18:16:14 +0900 Subject: [PATCH 0228/1276] Remove incorrect and pointless complexity to width changes --- osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs index 4392c5811a..68b36c2a04 100644 --- a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs +++ b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs @@ -72,10 +72,7 @@ namespace osu.Game.Overlays.Toolbar private void updateMetrics() { - if (use24HourDisplay) - Width = 70; - else - Width = showRuntime ? 66 : 45; // Allows for space for game time up to 99 days (in the padding area since this is quite rare). + Width = showRuntime || use24HourDisplay ? 66 : 45; // Allows for space for game time up to 99 days (in the padding area since this is quite rare). gameTime.FadeTo(showRuntime ? 1 : 0); } From b2c0eaeab4abf7f05cdec587acda9b1c19a29bbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 18:20:15 +0900 Subject: [PATCH 0229/1276] Make string read better --- osu.Game/Localisation/GeneralSettingsStrings.cs | 4 ++-- .../Overlays/Settings/Sections/General/LanguageSettings.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs index cca8c8efd1..c65e6c77f4 100644 --- a/osu.Game/Localisation/GeneralSettingsStrings.cs +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -30,9 +30,9 @@ namespace osu.Game.Localisation public static LocalisableString PreferOriginalMetadataLanguage => new TranslatableString(getKey(@"prefer_original"), @"Prefer metadata in original language"); /// - /// "Prefer 24-hour time" + /// "Prefer 24-hour time display" /// - public static LocalisableString Prefer24HourTime => new TranslatableString(getKey(@"prefer_24_hour_time"), @"Prefer 24-hour time"); + public static LocalisableString Prefer24HourTimeDisplay => new TranslatableString(getKey(@"prefer_24_hour_time_display"), @"Prefer 24-hour time display"); /// /// "Updates" diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index e7cc1bce10..cdce187a35 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Settings.Sections.General }, new SettingsCheckbox { - LabelText = GeneralSettingsStrings.Prefer24HourTime, + LabelText = GeneralSettingsStrings.Prefer24HourTimeDisplay, Current = config.GetBindable(OsuSetting.Prefer24HourTime) }, }; From a4d3afd06d63059ca2b2cff0b4fb2577dd74818e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 19:12:10 +0900 Subject: [PATCH 0230/1276] Fix multiple issues with bindable safety in `SkinEditor` components --- .../Skinning/Editor/SkinBlueprintContainer.cs | 23 ++++++++++++------- osu.Game/Skinning/Editor/SkinEditor.cs | 3 +++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index d67bfb89ab..ebf3c9c319 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -21,21 +21,20 @@ namespace osu.Game.Skinning.Editor private readonly List> targetComponents = new List>(); + [Resolved] + private SkinEditor editor { get; set; } + public SkinBlueprintContainer(Drawable target) { this.target = target; } - [BackgroundDependencyLoader(true)] - private void load(SkinEditor editor) - { - SelectedItems.BindTo(editor.SelectedComponents); - } - protected override void LoadComplete() { base.LoadComplete(); + SelectedItems.BindTo(editor.SelectedComponents); + // track each target container on the current screen. var targetContainers = target.ChildrenOfType().ToArray(); @@ -56,7 +55,7 @@ namespace osu.Game.Skinning.Editor } } - private void componentsChanged(object sender, NotifyCollectionChangedEventArgs e) + private void componentsChanged(object sender, NotifyCollectionChangedEventArgs e) => Schedule(() => { switch (e.Action) { @@ -79,7 +78,7 @@ namespace osu.Game.Skinning.Editor AddBlueprintFor(item); break; } - } + }); protected override void AddBlueprintFor(ISkinnableDrawable item) { @@ -93,5 +92,13 @@ namespace osu.Game.Skinning.Editor protected override SelectionBlueprint CreateBlueprintFor(ISkinnableDrawable component) => new SkinBlueprint(component); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + foreach (var list in targetComponents) + list.UnbindAll(); + } } } diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index bcff70c008..4602366413 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -203,6 +203,9 @@ namespace osu.Game.Skinning.Editor SelectedComponents.Clear(); + // Immediately clear the previous blueprint container to ensure it doesn't try to interact with the old target. + content.Clear(); + Scheduler.AddOnce(loadBlueprintContainer); Scheduler.AddOnce(populateSettings); From 5b29ddd2ed91fae0d1fbe840d4575bce6d18be7d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 7 Apr 2022 15:05:08 +0300 Subject: [PATCH 0231/1276] Add further test coverage against paths with extensions --- .../Skinning/LegacySkinTextureFallbackTest.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs index db7fc0b4a0..76c49edf78 100644 --- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs +++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs @@ -83,6 +83,20 @@ namespace osu.Game.Tests.NonVisual.Skinning "followpoint.png", "followpoint@2x.png", 2 }, + new object[] + { + // Looking up a path with extension specified should work. + new[] { "Gameplay/osu/followpoint.png" }, + "Gameplay/osu/followpoint.png", + "Gameplay/osu/followpoint.png", 1 + }, + new object[] + { + // Looking up a path with extension specified should also work with @2x sprites. + new[] { "Gameplay/osu/followpoint@2x.png" }, + "Gameplay/osu/followpoint.png", + "Gameplay/osu/followpoint@2x.png", 2 + }, }; [TestCaseSource(nameof(fallbackTestCases))] From fb9fe4213df4b89ddfec65c71765322aeb38742e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 7 Apr 2022 15:07:14 +0300 Subject: [PATCH 0232/1276] Fix skin texture lookups not handling paths with extensions --- osu.Game/Skinning/LegacySkin.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index a97b5d44ce..f7d5581621 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -443,9 +443,7 @@ namespace osu.Game.Skinning string lookupName = name.Replace(@"@2x", string.Empty); float ratio = 2; - string twoTimesFilename = Path.HasExtension(lookupName) - ? @$"{Path.GetFileNameWithoutExtension(lookupName)}@2x{Path.GetExtension(lookupName)}" - : @$"{lookupName}@2x"; + string twoTimesFilename = $"{Path.ChangeExtension(lookupName, null)}@2x{Path.GetExtension(lookupName)}"; var texture = Textures?.Get(twoTimesFilename, wrapModeS, wrapModeT); From c323020fcf362ee4f570fc0f495127a555cd3d5e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 7 Apr 2022 15:56:12 +0300 Subject: [PATCH 0233/1276] Fix `Use24HourDisplay` conditional flipped --- osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs | 3 ++- osu.Game/Overlays/Toolbar/ToolbarClock.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs index 68b36c2a04..10de35ee50 100644 --- a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs +++ b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs @@ -40,6 +40,7 @@ namespace osu.Game.Overlays.Toolbar return; use24HourDisplay = value; + updateMetrics(); UpdateDisplay(DateTimeOffset.Now); //Update realTime.Text immediately instead of waiting until next second } @@ -66,7 +67,7 @@ namespace osu.Game.Overlays.Toolbar protected override void UpdateDisplay(DateTimeOffset now) { - realTime.Text = use24HourDisplay ? $"{now:h:mm:ss tt}" : $"{now:HH:mm:ss}"; + realTime.Text = use24HourDisplay ? $"{now:HH:mm:ss}" : $"{now:h:mm:ss tt}"; gameTime.Text = $"running {new TimeSpan(TimeSpan.TicksPerSecond * (int)(Clock.CurrentTime / 1000)):c}"; } diff --git a/osu.Game/Overlays/Toolbar/ToolbarClock.cs b/osu.Game/Overlays/Toolbar/ToolbarClock.cs index d726a95f46..308359570f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarClock.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarClock.cs @@ -97,7 +97,7 @@ namespace osu.Game.Overlays.Toolbar analog.FadeTo(showAnalog ? 1 : 0); }, true); - prefer24HourTime.BindValueChanged(prefer24H => digital.Use24HourDisplay = !prefer24H.NewValue, true); + prefer24HourTime.BindValueChanged(prefer24H => digital.Use24HourDisplay = prefer24H.NewValue, true); } protected override bool OnClick(ClickEvent e) From 0e9248624076d27261c872a3a763ad6051251dfc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 22:05:04 +0900 Subject: [PATCH 0234/1276] Update various licence years to 2022 --- Directory.Build.props | 2 +- LICENCE | 2 +- Templates/osu.Game.Templates.csproj | 2 +- osu.Desktop/osu.nuspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3ab3dc3d13..709545bf1d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -34,7 +34,7 @@ https://github.com/ppy/osu Automated release. ppy Pty Ltd - Copyright (c) 2021 ppy Pty Ltd + Copyright (c) 2022 ppy Pty Ltd osu game diff --git a/LICENCE b/LICENCE index b5962ad3b2..d3e7537cef 100644 --- a/LICENCE +++ b/LICENCE @@ -1,4 +1,4 @@ -Copyright (c) 2021 ppy Pty Ltd . +Copyright (c) 2022 ppy Pty Ltd . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Templates/osu.Game.Templates.csproj b/Templates/osu.Game.Templates.csproj index 4624d3d771..b8c3ad373a 100644 --- a/Templates/osu.Game.Templates.csproj +++ b/Templates/osu.Game.Templates.csproj @@ -8,7 +8,7 @@ https://github.com/ppy/osu/blob/master/Templates https://github.com/ppy/osu Automated release. - Copyright (c) 2021 ppy Pty Ltd + Copyright (c) 2022 ppy Pty Ltd Templates to use when creating a ruleset for consumption in osu!. dotnet-new;templates;osu netstandard2.1 diff --git a/osu.Desktop/osu.nuspec b/osu.Desktop/osu.nuspec index 1757fd7c73..dc1ec17e2c 100644 --- a/osu.Desktop/osu.nuspec +++ b/osu.Desktop/osu.nuspec @@ -11,7 +11,7 @@ false A free-to-win rhythm game. Rhythm is just a *click* away! testing - Copyright (c) 2021 ppy Pty Ltd + Copyright (c) 2022 ppy Pty Ltd en-AU From 11b4c5ca875b8a4f2a61ba8184f5f1b2865809be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Apr 2022 23:42:42 +0900 Subject: [PATCH 0235/1276] Fix potential null ref if `UpdateTargetScreen` is called too early --- osu.Game/Skinning/Editor/SkinEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 4602366413..e36d5ca3c6 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -204,7 +204,7 @@ namespace osu.Game.Skinning.Editor SelectedComponents.Clear(); // Immediately clear the previous blueprint container to ensure it doesn't try to interact with the old target. - content.Clear(); + content?.Clear(); Scheduler.AddOnce(loadBlueprintContainer); Scheduler.AddOnce(populateSettings); From a616f5bc2a4eb90824ed0d7ae62fcd4a972c7720 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 7 Apr 2022 19:24:15 +0300 Subject: [PATCH 0236/1276] Fix regressed digital clock width conditional --- osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs index 10de35ee50..ac6f563336 100644 --- a/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs +++ b/osu.Game/Overlays/Toolbar/DigitalClockDisplay.cs @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Toolbar private void updateMetrics() { - Width = showRuntime || use24HourDisplay ? 66 : 45; // Allows for space for game time up to 99 days (in the padding area since this is quite rare). + Width = showRuntime || !use24HourDisplay ? 66 : 45; // Allows for space for game time up to 99 days (in the padding area since this is quite rare). gameTime.FadeTo(showRuntime ? 1 : 0); } From 04db80848bade5ee16f46621339cf4c5622bbd99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 7 Apr 2022 21:24:50 +0200 Subject: [PATCH 0237/1276] Remove unused using directives --- .../Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs index cfe9e35298..b2f4fa2738 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs @@ -9,10 +9,8 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; -using osu.Framework.Utils; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; -using osu.Game.Skinning; using osu.Game.Storyboards; using osu.Game.Storyboards.Drawables; using osuTK; From 71c0216c559449c8286a816d0d592ebaad41fdfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 7 Apr 2022 21:37:42 +0200 Subject: [PATCH 0238/1276] Add null check guards to unsubscriptions in `Dispose()` --- osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs | 4 +++- osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index 5ebca4d860..8a14b8b183 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -133,7 +133,9 @@ namespace osu.Game.Storyboards.Drawables protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - skin.SourceChanged -= skinSourceChanged; + + if (skin != null) + skin.SourceChanged -= skinSourceChanged; } } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index 6622cfb6be..a6f2b8fcbd 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -110,7 +110,9 @@ namespace osu.Game.Storyboards.Drawables protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - skin.SourceChanged -= skinSourceChanged; + + if (skin != null) + skin.SourceChanged -= skinSourceChanged; } } } From 112096768b2107645ec0cafcad8bd9a8893c18ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 7 Apr 2022 22:38:00 +0200 Subject: [PATCH 0239/1276] Add test checking incompatibility of multi mods --- .../Mods/MultiModIncompatibilityTest.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 osu.Game.Tests/Mods/MultiModIncompatibilityTest.cs diff --git a/osu.Game.Tests/Mods/MultiModIncompatibilityTest.cs b/osu.Game.Tests/Mods/MultiModIncompatibilityTest.cs new file mode 100644 index 0000000000..312b939315 --- /dev/null +++ b/osu.Game.Tests/Mods/MultiModIncompatibilityTest.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Taiko; +using osu.Game.Utils; + +namespace osu.Game.Tests.Mods +{ + [TestFixture] + public class MultiModIncompatibilityTest + { + /// + /// Ensures that all mods grouped into s, as declared by the default rulesets, are pairwise incompatible with each other. + /// + [TestCase(typeof(OsuRuleset))] + [TestCase(typeof(TaikoRuleset))] + [TestCase(typeof(CatchRuleset))] + [TestCase(typeof(ManiaRuleset))] + public void TestAllMultiModsFromRulesetAreIncompatible(Type rulesetType) + { + var ruleset = (Ruleset)Activator.CreateInstance(rulesetType); + Assert.That(ruleset, Is.Not.Null); + + var allMultiMods = getMultiMods(ruleset); + + Assert.Multiple(() => + { + foreach (var multiMod in allMultiMods) + { + int modCount = multiMod.Mods.Length; + + for (int i = 0; i < modCount; ++i) + { + // indexing from i + 1 ensures that only pairs of different mods are checked, and are checked only once + // (indexing from 0 would check each pair twice, and also check each mod against itself). + for (int j = i + 1; j < modCount; ++j) + { + var firstMod = multiMod.Mods[i]; + var secondMod = multiMod.Mods[j]; + + Assert.That( + ModUtils.CheckCompatibleSet(new[] { firstMod, secondMod }), Is.False, + $"{firstMod.Name} ({firstMod.Acronym}) and {secondMod.Name} ({secondMod.Acronym}) should be incompatible."); + } + } + } + }); + } + + /// + /// This local helper is used rather than , because the aforementioned method flattens multi mods. + /// > + private static IEnumerable getMultiMods(Ruleset ruleset) + => Enum.GetValues(typeof(ModType)).Cast().SelectMany(ruleset.GetModsFor).OfType(); + } +} From 6630b38c08d02c7f975ccd7b9d7f4031fbd08eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 19:08:54 +0200 Subject: [PATCH 0240/1276] Make all `ModRateAdjust` implementations incompatible with each other --- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 4 ---- osu.Game/Rulesets/Mods/ModHalfTime.cs | 4 ---- osu.Game/Rulesets/Mods/ModRateAdjust.cs | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index d4c4dce0f5..1c71f5d055 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; @@ -18,8 +16,6 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Zoooooooooom..."; - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModHalfTime)).ToArray(); - [SettingSource("Speed increase", "The actual increase to apply")] public override BindableNumber SpeedChange { get; } = new BindableDouble { diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index c240cdbe6e..13d89e30d6 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; @@ -18,8 +16,6 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyReduction; public override string Description => "Less zoom..."; - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModDoubleTime)).ToArray(); - [SettingSource("Speed decrease", "The actual decrease to apply")] public override BindableNumber SpeedChange { get; } = new BindableDouble { diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index ebe18f2188..88fb609c07 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mods public double ApplyToRate(double time, double rate) => rate * SpeedChange.Value; - public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp), typeof(ModAdaptiveSpeed) }; + public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp), typeof(ModAdaptiveSpeed), typeof(ModRateAdjust) }; public override string SettingDescription => SpeedChange.IsDefault ? string.Empty : $"{SpeedChange.Value:N2}x"; } From 5e02ba353bd234008cf42dc422fe9bc1c42ae0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 19:12:24 +0200 Subject: [PATCH 0241/1276] Make `OsuMod{Blinds,Flashlight}` incompatible --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 2 ++ osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index ad4c5dfd5d..7567c96b50 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -29,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.DifficultyIncrease; public override double ScoreMultiplier => 1.12; + public override Type[] IncompatibleMods => new[] { typeof(OsuModFlashlight) }; + private DrawableOsuBlinds blinds; public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 38c84be295..44d72fae61 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; @@ -19,6 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModFlashlight : ModFlashlight, IApplicableToDrawableHitObject { public override double ScoreMultiplier => 1.12; + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModBlinds)).ToArray(); private const double default_follow_delay = 120; From e99d0f9faeb115d551ecd6fe26f27141666646ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 19:14:45 +0200 Subject: [PATCH 0242/1276] Make all `OsuModObjectScaleTween` implementations incompatible with each other --- osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs index 778447e444..70c075276f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods protected virtual float EndScale => 1; - public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) }; + public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween) }; protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { From 282b220d9436105b441588d64b3a7b515a1550c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Apr 2022 00:16:25 +0200 Subject: [PATCH 0243/1276] Fix test failure due to checking incompaatibility of flattened multi mods --- osu.Game/Utils/ModUtils.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index ff8e04cc58..8df44216b6 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -115,7 +115,9 @@ namespace osu.Game.Utils { mods = mods.ToArray(); - CheckCompatibleSet(mods, out invalidMods); + // exclude multi mods from compatibility checks. + // the loop below automatically marks all multi mods as not valid for gameplay anyway. + CheckCompatibleSet(mods.Where(m => !(m is MultiMod)), out invalidMods); foreach (var mod in mods) { From 9ba99ed57d0abc9e67ee9bbab633077a7103976c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Apr 2022 14:42:54 +0900 Subject: [PATCH 0244/1276] Ensure all access to `MultiplayerClient.Room` is on the update thread This was an implicit requirement until now, but not well documented everywhere. Adding this makes it much easier to understand the requirement (and probably safer). --- .../Online/Multiplayer/MultiplayerClient.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index d6099e5f72..2084ba048d 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Development; using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Game.Database; @@ -87,7 +88,21 @@ namespace osu.Game.Online.Multiplayer /// /// The joined . /// - public MultiplayerRoom? Room { get; private set; } + public MultiplayerRoom? Room + { + get + { + Debug.Assert(ThreadSafety.IsUpdateThread); + return room; + } + private set + { + Debug.Assert(ThreadSafety.IsUpdateThread); + room = value; + } + } + + private MultiplayerRoom? room; /// /// The users in the joined which are participating in the current gameplay loop. From d50f41225ffe644d1abb31681637c0cebeeacc38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Apr 2022 14:43:53 +0900 Subject: [PATCH 0245/1276] Rename `scheduleAsync` to `runOnUpdateThreadAsync` --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 2084ba048d..5cbf65478c 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -181,7 +181,7 @@ namespace osu.Game.Online.Multiplayer await Task.WhenAll(joinedRoom.Users.Select(PopulateUser)).ConfigureAwait(false); // Update the stored room (must be done on update thread for thread-safety). - await scheduleAsync(() => + await runOnUpdateThreadAsync(() => { Room = joinedRoom; APIRoom = room; @@ -228,7 +228,7 @@ namespace osu.Game.Online.Multiplayer // Leaving rooms is expected to occur instantaneously whilst the operation is finalised in the background. // However a few members need to be reset immediately to prevent other components from entering invalid states whilst the operation hasn't yet completed. // For example, if a room was left and the user immediately pressed the "create room" button, then the user could be taken into the lobby if the value of Room is not reset in time. - var scheduledReset = scheduleAsync(() => + var scheduledReset = runOnUpdateThreadAsync(() => { APIRoom = null; Room = null; @@ -799,7 +799,7 @@ namespace osu.Game.Online.Multiplayer PlayingUserIds.Remove(userId); } - private Task scheduleAsync(Action action, CancellationToken cancellationToken = default) + private Task runOnUpdateThreadAsync(Action action, CancellationToken cancellationToken = default) { var tcs = new TaskCompletionSource(); From 64c63fe93a4ae35a01a86ab5c25a0e65a633a036 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Apr 2022 14:52:56 +0900 Subject: [PATCH 0246/1276] Move null check in `JoinRoom` on to update thread --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 5cbf65478c..996a4e312f 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -163,13 +163,13 @@ namespace osu.Game.Online.Multiplayer /// An optional password to use for the join operation. public async Task JoinRoom(Room room, string? password = null) { + if (Room != null) + throw new InvalidOperationException("Cannot join a multiplayer room while already in one."); + var cancellationSource = joinCancellationSource = new CancellationTokenSource(); await joinOrLeaveTaskChain.Add(async () => { - if (Room != null) - throw new InvalidOperationException("Cannot join a multiplayer room while already in one."); - Debug.Assert(room.RoomID.Value != null); // Join the server-side room. @@ -183,6 +183,8 @@ namespace osu.Game.Online.Multiplayer // Update the stored room (must be done on update thread for thread-safety). await runOnUpdateThreadAsync(() => { + Debug.Assert(Room == null); + Room = joinedRoom; APIRoom = room; From 24c2d465a9c04f61aad3fde9c87243a458447505 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Apr 2022 14:53:14 +0900 Subject: [PATCH 0247/1276] Move null assert in `MultiplayerPlayer` on to update thread --- osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 043315c790..70f8f1b752 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -133,6 +133,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer failAndBail(); } }), true); + } + + protected override void LoadComplete() + { + base.LoadComplete(); Debug.Assert(client.Room != null); } From 933a722cfcecb2911fcb2db9ebecfb1e50b76922 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Apr 2022 14:56:04 +0900 Subject: [PATCH 0248/1276] Remove secondary null checks which cannot exist (were on wrong thread) --- .../Online/Multiplayer/MultiplayerClient.cs | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 996a4e312f..7808d8939c 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -360,9 +360,6 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -395,9 +392,6 @@ namespace osu.Game.Online.Multiplayer async Task IMultiplayerClient.UserJoined(MultiplayerRoomUser user) { - if (Room == null) - return; - await PopulateUser(user).ConfigureAwait(false); Scheduler.Add(() => @@ -446,9 +440,6 @@ namespace osu.Game.Online.Multiplayer private Task handleUserLeft(MultiplayerRoomUser user, Action? callback) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -470,9 +461,6 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.HostChanged(int userId) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -503,9 +491,6 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.UserStateChanged(int userId, MultiplayerUserState state) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -523,9 +508,6 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.MatchUserStateChanged(int userId, MatchUserState state) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -540,9 +522,6 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.MatchRoomStateChanged(MatchRoomState state) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -557,9 +536,6 @@ namespace osu.Game.Online.Multiplayer public Task MatchEvent(MatchServerEvent e) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -580,9 +556,6 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); @@ -601,9 +574,6 @@ namespace osu.Game.Online.Multiplayer public Task UserModsChanged(int userId, IEnumerable mods) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); @@ -622,9 +592,6 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.LoadRequested() { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -638,9 +605,6 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.MatchStarted() { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -654,9 +618,6 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.ResultsReady() { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -670,9 +631,6 @@ namespace osu.Game.Online.Multiplayer public Task PlaylistItemAdded(MultiplayerPlaylistItem item) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -692,9 +650,6 @@ namespace osu.Game.Online.Multiplayer public Task PlaylistItemRemoved(long playlistItemId) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) @@ -716,9 +671,6 @@ namespace osu.Game.Online.Multiplayer public Task PlaylistItemChanged(MultiplayerPlaylistItem item) { - if (Room == null) - return Task.CompletedTask; - Scheduler.Add(() => { if (Room == null) From 879267862c62fff490bbec9032bf48b07a53bf6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Apr 2022 15:20:22 +0900 Subject: [PATCH 0249/1276] Fix typo in `DrawableOsuHitObject` --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 628d95dff4..a1c8879105 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public readonly IBindable ScaleBindable = new BindableFloat(); public readonly IBindable IndexInCurrentComboBindable = new Bindable(); - // Must be set to update IsHovered as it's used in relax mdo to detect osu hit objects. + // Must be set to update IsHovered as it's used in relax mod to detect osu hit objects. public override bool HandlePositionalInput => true; protected override float SamplePlaybackPosition => HitObject.X / OsuPlayfield.BASE_SIZE.X; From 6bb82432125b7a5957a9472751b2f44ed48ff9fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Apr 2022 17:41:03 +0900 Subject: [PATCH 0250/1276] Fix potential assert failure due to `Room` access from disconnection event --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 7808d8939c..7d4084aeb4 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -142,7 +142,7 @@ namespace osu.Game.Online.Multiplayer [BackgroundDependencyLoader] private void load() { - IsConnected.BindValueChanged(connected => + IsConnected.BindValueChanged(connected => Scheduler.Add(() => { // clean up local room state on server disconnect. if (!connected.NewValue && Room != null) @@ -150,7 +150,7 @@ namespace osu.Game.Online.Multiplayer Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); LeaveRoom(); } - }); + })); } private readonly TaskChain joinOrLeaveTaskChain = new TaskChain(); From 5c571996d8398287e8a7b5ed9eb2a36fe3959a5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Apr 2022 17:55:43 +0900 Subject: [PATCH 0251/1276] Avoid multiplayer crashes when events arrive in unexpected order Intended to be a temporary fix for https://github.com/ppy/osu/issues/17713 while a more permanent solution is established. A proper fix is actually quite simple, but updating the test to work with it is... *slightly* more complicated. This hotfix will mean that if a `UserJoined` event arrives during the unobserved window during room join (of the local user), the local user still won't see the joining user, but the game won't crash as a result. --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 7808d8939c..7bd93c2e34 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -493,11 +493,13 @@ namespace osu.Game.Online.Multiplayer { Scheduler.Add(() => { - if (Room == null) + var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); + + // TODO: user should NEVER be null here, see https://github.com/ppy/osu/issues/17713. + if (user == null) return; - Room.Users.Single(u => u.UserID == userId).State = state; - + user.State = state; updateUserPlayingState(userId, state); RoomUpdated?.Invoke(); @@ -510,10 +512,13 @@ namespace osu.Game.Online.Multiplayer { Scheduler.Add(() => { - if (Room == null) + var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); + + // TODO: user should NEVER be null here, see https://github.com/ppy/osu/issues/17713. + if (user == null) return; - Room.Users.Single(u => u.UserID == userId).MatchState = state; + user.MatchState = state; RoomUpdated?.Invoke(); }, false); From ea4ca6d2ad0b7b14ea448f6f2de7612affc72b56 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Apr 2022 18:09:57 +0900 Subject: [PATCH 0252/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 10685d5990..331c4db01f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7fd2647a8a..308ec7e7d6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index a5efc40d51..311c9ba345 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 9ea6f9b1fe0e3411a94ed07ecf1286105bfd071e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 8 Apr 2022 21:44:44 +0900 Subject: [PATCH 0253/1276] Remove some incorrectly-firing asserts --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 0e9ba323ae..1bd1952832 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -481,11 +481,7 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.SettingsChanged(MultiplayerRoomSettings newSettings) { - Debug.Assert(APIRoom != null); - Debug.Assert(Room != null); - Scheduler.Add(() => updateLocalRoomSettings(newSettings)); - return Task.CompletedTask; } From ae1e1268374f632294aedadf0c228ae724000bd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 Apr 2022 00:30:28 +0900 Subject: [PATCH 0254/1276] Make `HighlightMessage` implicitly show chat overlay --- osu.Game/Online/Chat/MessageNotifier.cs | 2 -- osu.Game/Overlays/ChatOverlay.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index bcfec3cc0f..20c33db61f 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -178,8 +178,6 @@ namespace osu.Game.Online.Chat { notificationOverlay.Hide(); chatOverlay.HighlightMessage(message, channel); - chatOverlay.Show(); - return true; }; } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 3d39c7ce3a..8eb33453de 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -313,6 +313,8 @@ namespace osu.Game.Overlays /// The channel containing the message. public void HighlightMessage(Message message, Channel channel) { + Show(); + Debug.Assert(channel.Id == message.ChannelId); if (currentChannel.Value.Id != channel.Id) From 57fb8616b86c0a43fc51e3679984c88839efe0d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 Apr 2022 00:31:00 +0900 Subject: [PATCH 0255/1276] Fix potential null reference when attempting to highlight a message before overlay has been displayed --- osu.Game/Overlays/ChatOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 8eb33453de..1ad6be132e 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -317,7 +317,7 @@ namespace osu.Game.Overlays Debug.Assert(channel.Id == message.ChannelId); - if (currentChannel.Value.Id != channel.Id) + if (currentChannel.Value?.Id != channel.Id) { if (!channel.Joined.Value) channel = channelManager.JoinChannel(channel); From 34adc1d41070b37024065528da9e96965375a652 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Apr 2022 19:21:37 +0300 Subject: [PATCH 0256/1276] Add quick test coverage --- .../Visual/Online/TestSceneChatOverlay.cs | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 80a6698761..4d1dee1650 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -534,11 +534,33 @@ namespace osu.Game.Tests.Visual.Online }); }); - AddStep("Highlight message and open chat", () => + AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, channel1)); + } + + [Test] + public void TestHighlightWithNullChannel() + { + Message message = null; + + AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); + + AddStep("Send message in channel 1", () => { - chatOverlay.HighlightMessage(message, channel1); - chatOverlay.Show(); + channel1.AddNewMessages(message = new Message + { + ChannelId = channel1.Id, + Content = "Message to highlight!", + Timestamp = DateTimeOffset.Now, + Sender = new APIUser + { + Id = 2, + Username = "Someone", + } + }); }); + + AddStep("Set null channel", () => channelManager.CurrentChannel.Value = null); + AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, channel1)); } private void pressChannelHotkey(int number) From 134d9b5ddf16ea0ea8e27e764bdef755c0b3f17f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Apr 2022 19:27:59 +0300 Subject: [PATCH 0257/1276] Perform `Show()` at the bottom of the method Probably not affecting behaviour, but makes more sense. --- osu.Game/Overlays/ChatOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 1ad6be132e..64b972262b 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -313,8 +313,6 @@ namespace osu.Game.Overlays /// The channel containing the message. public void HighlightMessage(Message message, Channel channel) { - Show(); - Debug.Assert(channel.Id == message.ChannelId); if (currentChannel.Value?.Id != channel.Id) @@ -326,6 +324,8 @@ namespace osu.Game.Overlays } channel.HighlightedMessage.Value = message; + + Show(); } private float startDragChatHeight; From d482ab02877b3f231dd4d177b352e28ef5a4af4a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 9 Apr 2022 22:29:55 +0900 Subject: [PATCH 0258/1276] Fix ParticleSpewer outputting NaN in vertices --- osu.Game/Graphics/ParticleSpewer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 4fc6c4527f..369a4b21c7 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -109,6 +109,9 @@ namespace osu.Game.Graphics { foreach (var p in particles) { + if (p.Duration == 0) + continue; + float timeSinceStart = currentTime - p.StartTime; // ignore particles from the future. From 19b655d75bd589f25fa76057fe3e66ec5d98eb96 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 13:30:21 +0900 Subject: [PATCH 0259/1276] Avoid throwing unobserved exception when `PerformancePointsCounter` requests timed attributes --- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 7a1f724cfb..8b1e0ae83e 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; -using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio.Track; @@ -80,13 +79,16 @@ namespace osu.Game.Screens.Play.HUD difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token) .ContinueWith(task => Schedule(() => { + if (task.Exception != null) + return; + timedAttributes = task.GetResultSafely(); IsValid = true; if (lastJudgement != null) onJudgementChanged(lastJudgement); - }), TaskContinuationOptions.OnlyOnRanToCompletion); + })); } } From 779f7390b78425e9340f9172e536fd9bb733beae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 13:43:43 +0900 Subject: [PATCH 0260/1276] Add test coverage of `DrawableRoom` actually showing post-retrieval text --- .../Visual/Multiplayer/TestSceneDrawableRoom.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index d8ec0ad1f0..7d010592ae 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Utils; +using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; @@ -34,9 +35,11 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestMultipleStatuses() { + FillFlowContainer rooms = null; + AddStep("create rooms", () => { - Child = new FillFlowContainer + Child = rooms = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -124,6 +127,10 @@ namespace osu.Game.Tests.Visual.Multiplayer } }; }); + + AddUntilStep("wait for panel load", () => rooms.Count == 5); + AddUntilStep("correct status text", () => rooms.ChildrenOfType().Count(s => s.Text.ToString().StartsWith("Currently playing", StringComparison.Ordinal)) == 2); + AddUntilStep("correct status text", () => rooms.ChildrenOfType().Count(s => s.Text.ToString().StartsWith("Ready to play", StringComparison.Ordinal)) == 3); } [Test] From 2b74dbca28c24f1d3f2077ab272ed99deac5b33d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 13:36:01 +0900 Subject: [PATCH 0261/1276] Fix potential crash in `DrawableRoom` due to lack of null check on post-lookup beatmap --- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 5adce862a0..8e3aa77e7b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -418,10 +418,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components var retrievedBeatmap = task.GetResultSafely(); statusText.Text = "Currently playing "; - beatmapText.AddLink(retrievedBeatmap.GetDisplayTitleRomanisable(), - LinkAction.OpenBeatmap, - retrievedBeatmap.OnlineID.ToString(), - creationParameters: s => s.Truncate = true); + + if (retrievedBeatmap != null) + { + beatmapText.AddLink(retrievedBeatmap.GetDisplayTitleRomanisable(), + LinkAction.OpenBeatmap, + retrievedBeatmap.OnlineID.ToString(), + creationParameters: s => s.Truncate = true); + } + else + beatmapText.AddText("unknown beatmap"); }), cancellationSource.Token); } } From 808f0ecb74d2f2c65fdc9558d1eb07a616b6ac18 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 14:10:50 +0900 Subject: [PATCH 0262/1276] Ensure running state is updated before performing `Seek` in `GameplayClockContainer.Reset` --- osu.Game/Screens/Play/GameplayClockContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 226f5709c7..f2df00d691 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -122,14 +122,14 @@ namespace osu.Game.Screens.Play if (gameplayStartTime != null) StartTime = gameplayStartTime; - ensureSourceClockSet(); - Seek(StartTime ?? 0); - // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); if (!IsPaused.Value || startClock) Start(); + + ensureSourceClockSet(); + Seek(StartTime ?? 0); } /// From cb6e557212a5156ba2f59dd542943c878414b40f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 14:11:23 +0900 Subject: [PATCH 0263/1276] Fix `MasterGameplayClockContainer` having incorrect rate-based offsets immediately after `LoadComplete` I've attempted to explain why this is required using inline comments. There's also further conversation at https://github.com/ppy/osu/pull/17302#issuecomment-1091850927. --- .../Play/MasterGameplayClockContainer.cs | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 9bcbbf789b..54901746fc 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -130,17 +130,32 @@ namespace osu.Game.Screens.Play protected override void OnIsPausedChanged(ValueChangedEvent isPaused) { - // The source is stopped by a frequency fade first. - if (isPaused.NewValue) + if (IsLoaded) { - this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => + // During normal operation, the source is stopped after performing a frequency ramp. + if (isPaused.NewValue) { - if (IsPaused.Value == isPaused.NewValue) - AdjustableSource.Stop(); - }); + this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => + { + if (IsPaused.Value == isPaused.NewValue) + AdjustableSource.Stop(); + }); + } + else + this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); } else - this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); + { + if (isPaused.NewValue) + AdjustableSource.Stop(); + + // If not yet loaded, we still want to ensure relevant state is correct, as it is used for offset calculations. + pauseFreqAdjust.Value = isPaused.NewValue ? 0 : 1; + + // We must also process underlying gameplay clocks to update rate-adjusted offsets with the new frequency adjustment. + // Without doing this, an initial seek may be performed with the wrong offset. + GameplayClock.UnderlyingClock.ProcessFrame(); + } } public override void Start() From d1de516b1109d69b4c09391cb7997dec590d77e8 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 11 Apr 2022 15:38:11 +0900 Subject: [PATCH 0264/1276] Fix incorrect cursor parameterisation in test scene --- .../TestScenePlaylistsResultsScreen.cs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index ee9a0e263b..f5fe00458a 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -259,7 +259,7 @@ namespace osu.Game.Tests.Visual.Playlists { multiplayerUserScore.ScoresAround.Lower.Scores.Add(new MultiplayerScore { - ID = --highestScoreId, + ID = getNextLowestScoreId(), Accuracy = userScore.Accuracy, Passed = true, Rank = userScore.Rank, @@ -274,7 +274,7 @@ namespace osu.Game.Tests.Visual.Playlists multiplayerUserScore.ScoresAround.Higher.Scores.Add(new MultiplayerScore { - ID = ++lowestScoreId, + ID = getNextHighestScoreId(), Accuracy = userScore.Accuracy, Passed = true, Rank = userScore.Rank, @@ -306,7 +306,7 @@ namespace osu.Game.Tests.Visual.Playlists { result.Scores.Add(new MultiplayerScore { - ID = sort == "score_asc" ? --highestScoreId : ++lowestScoreId, + ID = sort == "score_asc" ? getNextHighestScoreId() : getNextLowestScoreId(), Accuracy = 1, Passed = true, Rank = ScoreRank.X, @@ -327,6 +327,17 @@ namespace osu.Game.Tests.Visual.Playlists return result; } + /// + /// The next highest score ID to appear at the left of the list. Monotonically decreasing. + /// + private int getNextHighestScoreId() => --highestScoreId; + + /// + /// The next lowest score ID to appear at the right of the list. Monotonically increasing. + /// + /// + private int getNextLowestScoreId() => ++lowestScoreId; + private void addCursor(MultiplayerScores scores) { scores.Cursor = new Cursor @@ -342,7 +353,9 @@ namespace osu.Game.Tests.Visual.Playlists { Properties = new Dictionary { - { "sort", JToken.FromObject(scores.Scores[^1].ID > scores.Scores[^2].ID ? "score_asc" : "score_desc") } + // [ 1, 2, 3, ... ] => score_desc (will be added to the right of the list) + // [ 3, 2, 1, ... ] => score_asc (will be added to the left of the list) + { "sort", JToken.FromObject(scores.Scores[^1].ID > scores.Scores[^2].ID ? "score_desc" : "score_asc") } } }; } From abb42a54fde43f6613fd76a4389b4a0a9575f24a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 16:47:34 +0900 Subject: [PATCH 0265/1276] Fix `MultiplayerTestScene` not actually setting match type correctly --- osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index 6c40546325..a26c6f9be9 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -44,6 +44,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return new Room { Name = { Value = "test name" }, + Type = { Value = MatchType.HeadToHead }, Playlist = { new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo) From 34457b474229b383e14a18e2d6cefd4760e1abee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 18:51:37 +0900 Subject: [PATCH 0266/1276] Expose mocked members as `virtual` on `Multiplayer` and `SpectatorClient` --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 4 ++-- osu.Game/Online/Spectator/SpectatorClient.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 1bd1952832..18db710cee 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -42,7 +42,7 @@ namespace osu.Game.Online.Multiplayer /// /// Invoked when a user leaves the room of their own accord. /// - public event Action? UserLeft; + public virtual event Action? UserLeft; /// /// Invoked when a user was kicked from the room forcefully. @@ -107,7 +107,7 @@ namespace osu.Game.Online.Multiplayer /// /// The users in the joined which are participating in the current gameplay loop. /// - public IBindableList CurrentMatchPlayingUserIds => PlayingUserIds; + public virtual IBindableList CurrentMatchPlayingUserIds => PlayingUserIds; protected readonly BindableList PlayingUserIds = new BindableList(); diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 8f22078010..138e8ac7d6 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -54,17 +54,17 @@ namespace osu.Game.Online.Spectator /// /// Called whenever new frames arrive from the server. /// - public event Action? OnNewFrames; + public virtual event Action? OnNewFrames; /// /// Called whenever a user starts a play session, or immediately if the user is being watched and currently in a play session. /// - public event Action? OnUserBeganPlaying; + public virtual event Action? OnUserBeganPlaying; /// /// Called whenever a user finishes a play session. /// - public event Action? OnUserFinishedPlaying; + public virtual event Action? OnUserFinishedPlaying; /// /// All users currently being watched. From 0ccac24f76dbd8f137cd59cda946a1a7d416aaae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 19:15:46 +0900 Subject: [PATCH 0267/1276] Split out base implementation of `MultiplayerGameplayLeaderboard` test --- ...MultiplayerGameplayLeaderboardTestScene.cs | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs new file mode 100644 index 0000000000..eb15282f75 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs @@ -0,0 +1,191 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Linq; +using Moq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Configuration; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.Spectator; +using osu.Game.Replays.Legacy; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.Play.HUD; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public abstract class MultiplayerGameplayLeaderboardTestScene : OsuTestScene + { + protected readonly BindableList MultiplayerUsers = new BindableList(); + private readonly BindableList multiplayerUserIds = new BindableList(); + + protected MultiplayerGameplayLeaderboard Leaderboard { get; private set; } + + private OsuConfigManager config; + + private readonly Mock spectatorClient = new Mock(); + private readonly Mock multiplayerClient = new Mock(); + + private readonly Dictionary lastHeaders = new Dictionary(); + + [BackgroundDependencyLoader] + private void load() + { + Dependencies.Cache(config = new OsuConfigManager(LocalStorage)); + Dependencies.CacheAs(spectatorClient.Object); + Dependencies.CacheAs(multiplayerClient.Object); + + // To emulate `MultiplayerClient.CurrentMatchPlayingUserIds` we need a bindable list of *only IDs*. + // This tracks the list of users 1:1. + MultiplayerUsers.BindCollectionChanged((c, e) => + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + Debug.Assert(e.NewItems != null); + + foreach (var user in e.NewItems.OfType()) + multiplayerUserIds.Add(user.UserID); + break; + + case NotifyCollectionChangedAction.Remove: + Debug.Assert(e.OldItems != null); + + foreach (var user in e.OldItems.OfType()) + multiplayerUserIds.Remove(user.UserID); + break; + + case NotifyCollectionChangedAction.Reset: + multiplayerUserIds.Clear(); + break; + } + }); + + multiplayerClient.SetupGet(c => c.CurrentMatchPlayingUserIds) + .Returns(() => multiplayerUserIds); + } + + [SetUpSteps] + public virtual void SetUpSteps() + { + AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = new APIUser + { + Username = "local", + }); + + AddStep("populate users", () => + { + MultiplayerUsers.Clear(); + for (int i = 0; i < 16; i++) + MultiplayerUsers.Add(CreateUser(i)); + }); + + AddStep("create leaderboard", () => + { + Leaderboard?.Expire(); + + OsuScoreProcessor scoreProcessor; + Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); + + var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + + Child = scoreProcessor = new OsuScoreProcessor(); + + scoreProcessor.ApplyBeatmap(playableBeatmap); + + LoadComponentAsync(Leaderboard = CreateLeaderboard(scoreProcessor), Add); + }); + + AddUntilStep("wait for load", () => Leaderboard.IsLoaded); + } + + protected virtual MultiplayerRoomUser CreateUser(int i) => new MultiplayerRoomUser(i); + + protected abstract MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor); + + [Test] + public void TestScoreUpdates() + { + AddRepeatStep("update state", UpdateUserStatesRandomly, 100); + AddToggleStep("switch compact mode", expanded => Leaderboard.Expanded.Value = expanded); + } + + [Test] + public void TestUserQuit() + { + AddUntilStep("mark users quit", () => + { + if (MultiplayerUsers.Count == 0) + return true; + + MultiplayerUsers.RemoveAt(0); + return false; + }); + } + + [Test] + public void TestChangeScoringMode() + { + AddRepeatStep("update state", UpdateUserStatesRandomly, 5); + AddStep("change to classic", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Classic)); + AddStep("change to standardised", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised)); + } + + protected void UpdateUserStatesRandomly() + { + foreach (var user in MultiplayerUsers) + { + if (RNG.NextBool()) + continue; + + int userId = user.UserID; + + if (!lastHeaders.TryGetValue(userId, out var header)) + { + lastHeaders[userId] = header = new FrameHeader(new ScoreInfo + { + Statistics = new Dictionary + { + [HitResult.Miss] = 0, + [HitResult.Meh] = 0, + [HitResult.Great] = 0 + } + }); + } + + switch (RNG.Next(0, 3)) + { + case 0: + header.Combo = 0; + header.Statistics[HitResult.Miss]++; + break; + + case 1: + header.Combo++; + header.MaxCombo = Math.Max(header.MaxCombo, header.Combo); + header.Statistics[HitResult.Meh]++; + break; + + default: + header.Combo++; + header.MaxCombo = Math.Max(header.MaxCombo, header.Combo); + header.Statistics[HitResult.Great]++; + break; + } + + spectatorClient.Raise(s => s.OnNewFrames -= null, userId, new FrameDataBundle(header, new[] { new LegacyReplayFrame(Time.Current, 0, 0, ReplayButtonState.None) })); + } + } + } +} From b52f070fdb79fa28aafc18d85ba0da7ac0a97d5f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 19:15:57 +0900 Subject: [PATCH 0268/1276] Update existing multiplayer leaderboard tests to use new mocked implementation --- ...TestSceneMultiplayerGameplayLeaderboard.cs | 151 +----------------- ...ceneMultiplayerGameplayLeaderboardTeams.cs | 116 +++----------- 2 files changed, 31 insertions(+), 236 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index bcd4474876..6e4aa48b0e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -1,161 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using System.Linq; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Extensions; -using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; -using osu.Framework.Utils; -using osu.Game.Configuration; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Online.Multiplayer; -using osu.Game.Online.Spectator; -using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Osu.Scoring; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; -using osu.Game.Tests.Visual.OnlinePlay; -using osu.Game.Tests.Visual.Spectator; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMultiplayerGameplayLeaderboard : MultiplayerTestScene + public class TestSceneMultiplayerGameplayLeaderboard : MultiplayerGameplayLeaderboardTestScene { - private static IEnumerable users => Enumerable.Range(0, 16); - - public new TestMultiplayerSpectatorClient SpectatorClient => (TestMultiplayerSpectatorClient)OnlinePlayDependencies?.SpectatorClient; - - private MultiplayerGameplayLeaderboard leaderboard; - private OsuConfigManager config; - - [BackgroundDependencyLoader] - private void load() + protected override MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor) { - Dependencies.Cache(config = new OsuConfigManager(LocalStorage)); - } - - public override void SetUpSteps() - { - base.SetUpSteps(); - - AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = UserLookupCache.GetUserAsync(1).GetResultSafely()); - - AddStep("create leaderboard", () => + return new MultiplayerGameplayLeaderboard(Ruleset.Value, scoreProcessor, MultiplayerUsers.ToArray()) { - leaderboard?.Expire(); - - OsuScoreProcessor scoreProcessor; - Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); - - var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); - var multiplayerUsers = new List(); - - foreach (int user in users) - { - SpectatorClient.SendStartPlay(user, Beatmap.Value.BeatmapInfo.OnlineID); - multiplayerUsers.Add(OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = user }, true)); - } - - Children = new Drawable[] - { - scoreProcessor = new OsuScoreProcessor(), - }; - - scoreProcessor.ApplyBeatmap(playableBeatmap); - - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(Ruleset.Value, scoreProcessor, multiplayerUsers.ToArray()) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, Add); - }); - - AddUntilStep("wait for load", () => leaderboard.IsLoaded); - AddUntilStep("wait for user population", () => MultiplayerClient.CurrentMatchPlayingUserIds.Count > 0); - } - - [Test] - public void TestScoreUpdates() - { - AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 100); - AddToggleStep("switch compact mode", expanded => leaderboard.Expanded.Value = expanded); - } - - [Test] - public void TestUserQuit() - { - foreach (int user in users) - AddStep($"mark user {user} quit", () => MultiplayerClient.RemoveUser(UserLookupCache.GetUserAsync(user).GetResultSafely().AsNonNull())); - } - - [Test] - public void TestChangeScoringMode() - { - AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 5); - AddStep("change to classic", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Classic)); - AddStep("change to standardised", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised)); - } - - protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies(); - - protected class TestDependencies : MultiplayerTestSceneDependencies - { - protected override TestSpectatorClient CreateSpectatorClient() => new TestMultiplayerSpectatorClient(); - } - - public class TestMultiplayerSpectatorClient : TestSpectatorClient - { - private readonly Dictionary lastHeaders = new Dictionary(); - - public void RandomlyUpdateState() - { - foreach ((int userId, _) in WatchedUserStates) - { - if (RNG.NextBool()) - continue; - - if (!lastHeaders.TryGetValue(userId, out var header)) - { - lastHeaders[userId] = header = new FrameHeader(new ScoreInfo - { - Statistics = new Dictionary - { - [HitResult.Miss] = 0, - [HitResult.Meh] = 0, - [HitResult.Great] = 0 - } - }); - } - - switch (RNG.Next(0, 3)) - { - case 0: - header.Combo = 0; - header.Statistics[HitResult.Miss]++; - break; - - case 1: - header.Combo++; - header.MaxCombo = Math.Max(header.MaxCombo, header.Combo); - header.Statistics[HitResult.Meh]++; - break; - - default: - header.Combo++; - header.MaxCombo = Math.Max(header.MaxCombo, header.Combo); - header.Statistics[HitResult.Great]++; - break; - } - - ((ISpectatorClient)this).UserSentFrames(userId, new FrameDataBundle(header, new[] { new LegacyReplayFrame(Time.Current, 0, 0, ReplayButtonState.None) })); - } - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 7f5aced925..56a4f053c5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -1,121 +1,55 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using System.Linq; -using NUnit.Framework; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Utils; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; -using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.Play.HUD; -using osu.Game.Tests.Visual.OnlinePlay; -using osu.Game.Tests.Visual.Spectator; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMultiplayerGameplayLeaderboardTeams : MultiplayerTestScene + public class TestSceneMultiplayerGameplayLeaderboardTeams : MultiplayerGameplayLeaderboardTestScene { - private static IEnumerable users => Enumerable.Range(0, 16); - - public new TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient SpectatorClient => - (TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient)OnlinePlayDependencies?.SpectatorClient; - - protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies(); - - protected class TestDependencies : MultiplayerTestSceneDependencies + protected override MultiplayerRoomUser CreateUser(int i) { - protected override TestSpectatorClient CreateSpectatorClient() => new TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient(); + var user = base.CreateUser(i); + user.MatchState = new TeamVersusUserState + { + TeamID = RNG.Next(0, 2) + }; + return user; } - private MultiplayerGameplayLeaderboard leaderboard; - private GameplayMatchScoreDisplay gameplayScoreDisplay; - - protected override Room CreateRoom() - { - var room = base.CreateRoom(); - room.Type.Value = MatchType.TeamVersus; - return room; - } + protected override MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor) => + new MultiplayerGameplayLeaderboard(Ruleset.Value, scoreProcessor, MultiplayerUsers.ToArray()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; public override void SetUpSteps() { base.SetUpSteps(); - AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = UserLookupCache.GetUserAsync(1).GetResultSafely()); - - AddStep("create leaderboard", () => + AddStep("Add external display components", () => { - leaderboard?.Expire(); - - OsuScoreProcessor scoreProcessor; - Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); - - var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); - var multiplayerUsers = new List(); - - foreach (int user in users) + LoadComponentAsync(new MatchScoreDisplay { - SpectatorClient.SendStartPlay(user, Beatmap.Value.BeatmapInfo.OnlineID); - var roomUser = OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = user }, true); + Team1Score = { BindTarget = Leaderboard.TeamScores[0] }, + Team2Score = { BindTarget = Leaderboard.TeamScores[1] } + }, Add); - roomUser.MatchState = new TeamVersusUserState - { - TeamID = RNG.Next(0, 2) - }; - - multiplayerUsers.Add(roomUser); - } - - Children = new Drawable[] + LoadComponentAsync(new GameplayMatchScoreDisplay { - scoreProcessor = new OsuScoreProcessor(), - }; - - scoreProcessor.ApplyBeatmap(playableBeatmap); - - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(Ruleset.Value, scoreProcessor, multiplayerUsers.ToArray()) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, gameplayLeaderboard => - { - LoadComponentAsync(new MatchScoreDisplay - { - Team1Score = { BindTarget = leaderboard.TeamScores[0] }, - Team2Score = { BindTarget = leaderboard.TeamScores[1] } - }, Add); - - LoadComponentAsync(gameplayScoreDisplay = new GameplayMatchScoreDisplay - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Team1Score = { BindTarget = leaderboard.TeamScores[0] }, - Team2Score = { BindTarget = leaderboard.TeamScores[1] } - }, Add); - - Add(gameplayLeaderboard); - }); - }); - - AddUntilStep("wait for load", () => leaderboard.IsLoaded); - AddUntilStep("wait for user population", () => MultiplayerClient.CurrentMatchPlayingUserIds.Count > 0); - } - - [Test] - public void TestScoreUpdates() - { - AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 100); - AddToggleStep("switch compact mode", expanded => - { - leaderboard.Expanded.Value = expanded; - gameplayScoreDisplay.Expanded.Value = expanded; + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Team1Score = { BindTarget = Leaderboard.TeamScores[0] }, + Team2Score = { BindTarget = Leaderboard.TeamScores[1] } + }, Add); }); } } From 6a61fd95eb3e5b0b6bfdab55c8ed78b89ce9025c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 19:19:45 +0900 Subject: [PATCH 0269/1276] Tidy up base implementation --- ...MultiplayerGameplayLeaderboardTestScene.cs | 21 +++++++++---------- ...ceneMultiplayerGameplayLeaderboardTeams.cs | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs index eb15282f75..7c8a61b231 100644 --- a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs @@ -28,10 +28,15 @@ namespace osu.Game.Tests.Visual.Multiplayer public abstract class MultiplayerGameplayLeaderboardTestScene : OsuTestScene { protected readonly BindableList MultiplayerUsers = new BindableList(); - private readonly BindableList multiplayerUserIds = new BindableList(); protected MultiplayerGameplayLeaderboard Leaderboard { get; private set; } + protected virtual MultiplayerRoomUser CreateUser(int userId) => new MultiplayerRoomUser(userId); + + protected abstract MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor); + + private readonly BindableList multiplayerUserIds = new BindableList(); + private OsuConfigManager config; private readonly Mock spectatorClient = new Mock(); @@ -81,7 +86,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = new APIUser { - Username = "local", + Id = 1, }); AddStep("populate users", () => @@ -95,25 +100,19 @@ namespace osu.Game.Tests.Visual.Multiplayer { Leaderboard?.Expire(); - OsuScoreProcessor scoreProcessor; Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); - var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); - - Child = scoreProcessor = new OsuScoreProcessor(); - + OsuScoreProcessor scoreProcessor = new OsuScoreProcessor(); scoreProcessor.ApplyBeatmap(playableBeatmap); + Child = scoreProcessor; + LoadComponentAsync(Leaderboard = CreateLeaderboard(scoreProcessor), Add); }); AddUntilStep("wait for load", () => Leaderboard.IsLoaded); } - protected virtual MultiplayerRoomUser CreateUser(int i) => new MultiplayerRoomUser(i); - - protected abstract MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor); - [Test] public void TestScoreUpdates() { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 56a4f053c5..b37ba470ed 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -14,9 +14,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiplayerGameplayLeaderboardTeams : MultiplayerGameplayLeaderboardTestScene { - protected override MultiplayerRoomUser CreateUser(int i) + protected override MultiplayerRoomUser CreateUser(int userId) { - var user = base.CreateUser(i); + var user = base.CreateUser(userId); user.MatchState = new TeamVersusUserState { TeamID = RNG.Next(0, 2) From 09d560c93e7a04f7fa9389ba68678c13b76a6f7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 19:27:14 +0900 Subject: [PATCH 0270/1276] Add test coverage of spectator requests --- .../MultiplayerGameplayLeaderboardTestScene.cs | 14 ++++++++++++++ osu.Game/Online/Spectator/SpectatorClient.cs | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs index 7c8a61b231..98e6cd514b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs @@ -84,6 +84,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUpSteps] public virtual void SetUpSteps() { + AddStep("reset counts", () => spectatorClient.Invocations.Clear()); + AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = new APIUser { Id = 1, @@ -111,6 +113,12 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddUntilStep("wait for load", () => Leaderboard.IsLoaded); + + AddStep("check watch requests were sent", () => + { + foreach (var user in MultiplayerUsers) + spectatorClient.Verify(s => s.WatchUser(user.UserID), Times.Once); + }); } [Test] @@ -131,6 +139,12 @@ namespace osu.Game.Tests.Visual.Multiplayer MultiplayerUsers.RemoveAt(0); return false; }); + + AddStep("check stop watching requests were sent", () => + { + foreach (var user in MultiplayerUsers) + spectatorClient.Verify(s => s.StopWatchingUser(user.UserID), Times.Once); + }); } [Test] diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 138e8ac7d6..78beda6298 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -221,7 +221,7 @@ namespace osu.Game.Online.Spectator }); } - public void WatchUser(int userId) + public virtual void WatchUser(int userId) { Debug.Assert(ThreadSafety.IsUpdateThread); From 01cec7d3fb0e11f8e8f0b55a4ad509b6f6865a8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 19:37:01 +0900 Subject: [PATCH 0271/1276] Remove unnecessary string literals --- osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index 5ec9c6c474..b90bd93002 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Gameplay BeatmapInfo = { AudioLeadIn = leadIn } }); - AddStep($"check first frame time", () => + AddStep("check first frame time", () => { Assert.That(player.FirstFrameClockTime, Is.Not.Null); Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard); - AddStep($"check first frame time", () => + AddStep("check first frame time", () => { Assert.That(player.FirstFrameClockTime, Is.Not.Null); Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Gameplay loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard); - AddStep($"check first frame time", () => + AddStep("check first frame time", () => { Assert.That(player.FirstFrameClockTime, Is.Not.Null); Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); From 4ab6c1107905210e10ea4286521bac7fb4d14550 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 23:57:15 +0900 Subject: [PATCH 0272/1276] Add failing test coverage of skin import after exporting from lazer --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 9b0facd625..dde8715764 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Game.Database; +using osu.Game.Extensions; using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Skinning; @@ -110,6 +111,27 @@ namespace osu.Game.Tests.Skins.IO assertImportedOnce(import1, import2); }); + [Test] + public Task TestImportExportedSkinFilename() => runSkinTest(async osu => + { + MemoryStream exportStream = new MemoryStream(); + + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "custom.osk")); + assertCorrectMetadata(import1, "name 1 [custom]", "author 1", osu); + + import1.PerformRead(s => + { + new LegacySkinExporter(osu.Dependencies.Get()).ExportModelTo(s, exportStream); + }); + + string exportFilename = import1.GetDisplayString(); + + var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(exportStream, $"{exportFilename}.osk")); + assertCorrectMetadata(import2, "name 1 [custom]", "author 1", osu); + + assertImportedOnce(import1, import2); + }); + [Test] public Task TestSameMetadataNameSameFolderName() => runSkinTest(async osu => { From 82939b05567eea3dd6ccd2773f4139cd5e9af250 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 23:57:27 +0900 Subject: [PATCH 0273/1276] Fix skins getting duplicated when importing after export Closes #17614. --- osu.Game/Skinning/SkinModelManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinModelManager.cs b/osu.Game/Skinning/SkinModelManager.cs index 33e49ce486..23813e8eb2 100644 --- a/osu.Game/Skinning/SkinModelManager.cs +++ b/osu.Game/Skinning/SkinModelManager.cs @@ -104,7 +104,9 @@ namespace osu.Game.Skinning // For imports, we want to use the archive or folder name as part of the metadata, in addition to any existing skin.ini metadata. // In an ideal world, skin.ini would be the only source of metadata, but a lot of skin creators and users don't update it when making modifications. // In both of these cases, the expectation from the user is that the filename or folder name is displayed somewhere to identify the skin. - if (archiveName != item.Name) + if (archiveName != item.Name + // lazer exports use this format + && archiveName != item.GetDisplayString()) item.Name = @$"{item.Name} [{archiveName}]"; } From 74dfa79f17c36949189e247b4ab9cdd0aaa8b949 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Apr 2022 20:50:19 +0300 Subject: [PATCH 0274/1276] Add failing test case --- .../UserInterface/TestScenePopupDialog.cs | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs index 6bd6115e68..b5f2544071 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs @@ -4,25 +4,58 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Testing; using osu.Game.Overlays.Dialog; +using osuTK; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { - [TestFixture] - public class TestScenePopupDialog : OsuTestScene + public class TestScenePopupDialog : OsuManualInputManagerTestScene { - public TestScenePopupDialog() + private TestPopupDialog dialog; + + [SetUpSteps] + public void SetUpSteps() { AddStep("new popup", () => - Add(new TestPopupDialog + { + Add(dialog = new TestPopupDialog { RelativeSizeAxes = Axes.Both, State = { Value = Framework.Graphics.Containers.Visibility.Visible }, - })); + }); + }); + } + + [Test] + public void TestDangerousButton([Values(false, true)] bool atEdge) + { + if (atEdge) + { + AddStep("move mouse to button edge", () => + { + var dangerousButtonQuad = dialog.DangerousButton.ScreenSpaceDrawQuad; + InputManager.MoveMouseTo(new Vector2(dangerousButtonQuad.TopLeft.X + 5, dangerousButtonQuad.Centre.Y)); + }); + } + else + AddStep("move mouse to button", () => InputManager.MoveMouseTo(dialog.DangerousButton)); + + AddStep("click button", () => InputManager.Click(MouseButton.Left)); + AddAssert("action not invoked", () => !dialog.DangerousButtonInvoked); + + AddStep("hold button", () => InputManager.PressButton(MouseButton.Left)); + AddUntilStep("action invoked", () => dialog.DangerousButtonInvoked); + AddStep("release button", () => InputManager.ReleaseButton(MouseButton.Left)); } private class TestPopupDialog : PopupDialog { + public PopupDialogDangerousButton DangerousButton { get; } + + public bool DangerousButtonInvoked; + public TestPopupDialog() { Icon = FontAwesome.Solid.AssistiveListeningSystems; @@ -40,9 +73,10 @@ namespace osu.Game.Tests.Visual.UserInterface { Text = @"You're a fake!", }, - new PopupDialogDangerousButton + DangerousButton = new PopupDialogDangerousButton { Text = @"Careful with this one..", + Action = () => DangerousButtonInvoked = true, }, }; } From bad30d9e602537a6e2afbfb41333d89d9235a4c4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Apr 2022 21:05:37 +0300 Subject: [PATCH 0275/1276] Fix dialog dangerous button being clickable at edges --- .../Dialog/PopupDialogDangerousButton.cs | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs b/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs index 1911a4fa56..adc627e15b 100644 --- a/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs +++ b/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs @@ -12,37 +12,38 @@ namespace osu.Game.Overlays.Dialog { public class PopupDialogDangerousButton : PopupDialogButton { + private Box progressBox; + private DangerousConfirmContainer confirmContainer; + [BackgroundDependencyLoader] private void load(OsuColour colours) { ButtonColour = colours.Red3; - ColourContainer.Add(new ConfirmFillBox + ColourContainer.Add(progressBox = new Box { - Action = () => Action(), RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, }); + + AddInternal(confirmContainer = new DangerousConfirmContainer + { + Action = () => Action(), + RelativeSizeAxes = Axes.Both, + }); } - private class ConfirmFillBox : HoldToConfirmContainer + protected override void LoadComplete() { - private Box box; + base.LoadComplete(); + confirmContainer.Progress.BindValueChanged(progress => progressBox.Width = (float)progress.NewValue, true); + } + + private class DangerousConfirmContainer : HoldToConfirmContainer + { protected override double? HoldActivationDelay => 500; - protected override void LoadComplete() - { - base.LoadComplete(); - - Child = box = new Box - { - RelativeSizeAxes = Axes.Both, - }; - - Progress.BindValueChanged(progress => box.Width = (float)progress.NewValue, true); - } - protected override bool OnMouseDown(MouseDownEvent e) { BeginConfirm(); From a0e3a2102acd70cf3fc24e35bd70c647d7d40db3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 10:49:57 +0900 Subject: [PATCH 0276/1276] Also expand match score component when leaderboard is (in test) --- .../TestSceneMultiplayerGameplayLeaderboardTeams.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index b37ba470ed..5cd3bb2f0f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -48,7 +48,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Team1Score = { BindTarget = Leaderboard.TeamScores[0] }, - Team2Score = { BindTarget = Leaderboard.TeamScores[1] } + Team2Score = { BindTarget = Leaderboard.TeamScores[1] }, + Expanded = { BindTarget = Leaderboard.Expanded }, }, Add); }); } From ebee9e68886925a368e87596c0e68d03c3fe025f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 11:04:53 +0900 Subject: [PATCH 0277/1276] Fix `MultiplayerGameplayLeaderboard` not immediately updating totals on scoring mode change --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 4f5edab526..d0726248aa 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -75,7 +75,10 @@ namespace osu.Game.Screens.Play.HUD foreach (var user in playingUsers) { var trackedUser = CreateUserData(user, ruleset, scoreProcessor); + trackedUser.ScoringMode.BindTo(scoringMode); + trackedUser.Score.BindValueChanged(_ => Scheduler.AddOnce(updateTotals)); + UserScores[user.UserID] = trackedUser; if (trackedUser.Team is int team && !TeamScores.ContainsKey(team)) @@ -175,8 +178,6 @@ namespace osu.Game.Screens.Play.HUD trackedData.Frames.Add(new TimedFrame(bundle.Frames.First().Time, bundle.Header)); trackedData.UpdateScore(); - - updateTotals(); }); private void updateTotals() From 577e29351ef5cf6e64336572b71a35214d94a1c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 11:46:21 +0900 Subject: [PATCH 0278/1276] Ensure players are always on both leaderboard teams In a very rare case, the randomisation may cause all users to be on one team, causing a test failure. The odds make it basically impossible, but if adjusting the number of users in the test scene this can more readily be hit. --- .../TestSceneMultiplayerGameplayLeaderboardTeams.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 5cd3bb2f0f..5caab9487e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -3,7 +3,6 @@ using System.Linq; using osu.Framework.Graphics; -using osu.Framework.Utils; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Rulesets.Osu.Scoring; @@ -14,12 +13,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiplayerGameplayLeaderboardTeams : MultiplayerGameplayLeaderboardTestScene { + private int team; + protected override MultiplayerRoomUser CreateUser(int userId) { var user = base.CreateUser(userId); user.MatchState = new TeamVersusUserState { - TeamID = RNG.Next(0, 2) + TeamID = team++ % 2 }; return user; } From 0ba95a448306c888163b442a2c8bb8c6b3768c23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 11:54:30 +0900 Subject: [PATCH 0279/1276] Ensure all users are shown on leaderboard (even when API lookup fails) --- .../Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index d0726248aa..11498a02cd 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -89,10 +89,13 @@ namespace osu.Game.Screens.Play.HUD { var users = task.GetResultSafely(); - foreach (var user in users) + for (int i = 0; i < users.Length; i++) { - if (user == null) - continue; + var user = users[i] ??= new APIUser + { + Id = playingUsers[i].UserID, + Username = "Unknown user", + }; var trackedUser = UserScores[user.Id]; From f29e32970003986caf7297139b3fa3c57c6f7a2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 12:11:51 +0900 Subject: [PATCH 0280/1276] Move user count to a constant to allow easier adjustment --- .../Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs index 98e6cd514b..ab187a2f76 100644 --- a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs @@ -27,6 +27,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { public abstract class MultiplayerGameplayLeaderboardTestScene : OsuTestScene { + private const int total_users = 16; + protected readonly BindableList MultiplayerUsers = new BindableList(); protected MultiplayerGameplayLeaderboard Leaderboard { get; private set; } @@ -94,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("populate users", () => { MultiplayerUsers.Clear(); - for (int i = 0; i < 16; i++) + for (int i = 0; i < total_users; i++) MultiplayerUsers.Add(CreateUser(i)); }); From 22c75a518ed4fd35f59eaa0fa1a98a4f404b1b53 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 12:38:15 +0900 Subject: [PATCH 0281/1276] Fix headers not getting reset on re-run of test --- .../Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs index ab187a2f76..4e6342868a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs @@ -86,7 +86,11 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUpSteps] public virtual void SetUpSteps() { - AddStep("reset counts", () => spectatorClient.Invocations.Clear()); + AddStep("reset counts", () => + { + spectatorClient.Invocations.Clear(); + lastHeaders.Clear(); + }); AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = new APIUser { From 0e1d17d1d191f87bcff7c324b9ad7a2da5de0b37 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 12 Apr 2022 13:11:22 +0900 Subject: [PATCH 0282/1276] Use zh-Hant instead of zh-TW --- osu.Game/Localisation/Language.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index dc1fac47a8..ab96d01c82 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -110,6 +110,6 @@ namespace osu.Game.Localisation // zh_hk, [Description(@"繁體中文(台灣)")] - zh_tw + zh_hant } } From 8b1cee75faa0b855d29a4d288f773d03cba348b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 14:13:07 +0900 Subject: [PATCH 0283/1276] Use `BindableLong` instead of `BindableInt` for user score tracking --- .../Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs | 6 +++--- .../OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs | 4 ++-- osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs | 6 +++--- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs index bdc348b043..bcb36a585f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -26,10 +26,10 @@ namespace osu.Game.Tests.Visual.Multiplayer var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo; var score = TestResources.CreateTestScoreInfo(beatmapInfo); - SortedDictionary teamScores = new SortedDictionary + SortedDictionary teamScores = new SortedDictionary { - { 0, new BindableInt(team1Score) }, - { 1, new BindableInt(team2Score) } + { 0, new BindableLong(team1Score) }, + { 1, new BindableLong(team2Score) } }; Stack.Push(screen = new MultiplayerTeamResultsScreen(score, 1, new PlaylistItem(beatmapInfo), teamScores)); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs index 14a779dedf..3f0f3e043c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs @@ -24,12 +24,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerTeamResultsScreen : MultiplayerResultsScreen { - private readonly SortedDictionary teamScores; + private readonly SortedDictionary teamScores; private Container winnerBackground; private Drawable winnerText; - public MultiplayerTeamResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary teamScores) + public MultiplayerTeamResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary teamScores) : base(score, roomId, playlistItem) { if (teamScores.Count != 2) diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index 88cf9529bf..2129000268 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -19,8 +19,8 @@ namespace osu.Game.Screens.Play.HUD private const float bar_height = 18; private const float font_size = 50; - public BindableInt Team1Score = new BindableInt(); - public BindableInt Team2Score = new BindableInt(); + public BindableLong Team1Score = new BindableLong(); + public BindableLong Team2Score = new BindableLong(); protected MatchScoreCounter Score1Text; protected MatchScoreCounter Score2Text; @@ -133,7 +133,7 @@ namespace osu.Game.Screens.Play.HUD var winningBar = Team1Score.Value > Team2Score.Value ? score1Bar : score2Bar; var losingBar = Team1Score.Value <= Team2Score.Value ? score1Bar : score2Bar; - int diff = Math.Max(Team1Score.Value, Team2Score.Value) - Math.Min(Team1Score.Value, Team2Score.Value); + long diff = Math.Max(Team1Score.Value, Team2Score.Value) - Math.Min(Team1Score.Value, Team2Score.Value); losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint); diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 11498a02cd..e569e5368b 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Play.HUD { protected readonly Dictionary UserScores = new Dictionary(); - public readonly SortedDictionary TeamScores = new SortedDictionary(); + public readonly SortedDictionary TeamScores = new SortedDictionary(); [Resolved] private OsuColour colours { get; set; } @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Play.HUD UserScores[user.UserID] = trackedUser; if (trackedUser.Team is int team && !TeamScores.ContainsKey(team)) - TeamScores.Add(team, new BindableInt()); + TeamScores.Add(team, new BindableLong()); } userLookupCache.GetUsersAsync(playingUsers.Select(u => u.UserID).ToArray()).ContinueWith(task => Schedule(() => From 45d79c1a737a8e6128e1ce631b5e37185fd48d9f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 18:01:29 +0900 Subject: [PATCH 0284/1276] Ensure previous test score is cleaned up to allow visual test runs to work better --- .../Gameplay/TestSceneReplayDownloadButton.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 5d25287e45..e89350de1a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -26,6 +26,8 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneReplayDownloadButton : OsuManualInputManagerTestScene { + private const long online_score_id = 2553163309; + [Resolved] private RulesetStore rulesets { get; set; } @@ -43,6 +45,15 @@ namespace osu.Game.Tests.Visual.Gameplay beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); } + [SetUpSteps] + public void SetUpSteps() + { + AddStep("delete previous imports", () => + { + scoreManager.Delete(s => s.OnlineID == online_score_id); + }); + } + [Test] public void TestDisplayStates() { @@ -180,7 +191,7 @@ namespace osu.Game.Tests.Visual.Gameplay { return new APIScore { - OnlineID = 2553163309, + OnlineID = online_score_id, RulesetID = 0, Beatmap = CreateAPIBeatmapSet(new OsuRuleset().RulesetInfo).Beatmaps.First(), HasReplay = replayAvailable, From b51abfc72220646e1af51663fdc317bf58b0b3bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 18:01:13 +0900 Subject: [PATCH 0285/1276] Fix `ScoreDownloadTracker` matching on empty hash equality --- osu.Game/Online/ScoreDownloadTracker.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs index d7e31c8a59..ed1c566dbe 100644 --- a/osu.Game/Online/ScoreDownloadTracker.cs +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -47,7 +47,10 @@ namespace osu.Game.Online Downloader.DownloadBegan += downloadBegan; Downloader.DownloadFailed += downloadFailed; - realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s => ((s.OnlineID > 0 && s.OnlineID == TrackedItem.OnlineID) || s.Hash == TrackedItem.Hash) && !s.DeletePending), (items, changes, ___) => + realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s => + ((s.OnlineID > 0 && s.OnlineID == TrackedItem.OnlineID) + || (!string.IsNullOrEmpty(s.Hash) && s.Hash == TrackedItem.Hash)) + && !s.DeletePending), (items, changes, ___) => { if (items.Any()) Schedule(() => UpdateState(DownloadState.LocallyAvailable)); From 6dbfc261585fd239e8b7b13a6f400938f581e238 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 18:07:55 +0900 Subject: [PATCH 0286/1276] Add log output when a model is undeleted --- osu.Game/Stores/RealmArchiveModelImporter.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Stores/RealmArchiveModelImporter.cs b/osu.Game/Stores/RealmArchiveModelImporter.cs index 1d0e16d549..353a2164bb 100644 --- a/osu.Game/Stores/RealmArchiveModelImporter.cs +++ b/osu.Game/Stores/RealmArchiveModelImporter.cs @@ -389,7 +389,10 @@ namespace osu.Game.Stores LogForModel(item, @$"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import."); if (existing.DeletePending) + { + LogForModel(item, $@"Existing {HumanisedModelName}'s deletion flag has been removed"); UndeleteForReuse(existing); + } transaction.Commit(); From 20eca9bf6a3b7cde91545a85b58648f72fda7ac2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 19:26:02 +0900 Subject: [PATCH 0287/1276] Refactor `TestSceneRankRangePill` to not depend on `TestMultiplayerClient` --- .../Multiplayer/TestSceneRankRangePill.cs | 76 ++++++++++--------- .../Online/Multiplayer/MultiplayerClient.cs | 4 +- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs index f95e73ff3c..b758c83a0a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs @@ -1,67 +1,68 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; +using Moq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneRankRangePill : MultiplayerTestScene + public class TestSceneRankRangePill : OsuTestScene { - [SetUp] - public new void Setup() => Schedule(() => + private readonly Mock multiplayerClient = new Mock(); + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => + // not used but required by the component. + new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); + + [BackgroundDependencyLoader] + private void load() { + Dependencies.CacheAs(multiplayerClient.Object); + Child = new RankRangePill { Anchor = Anchor.Centre, Origin = Anchor.Centre }; - }); + } [Test] public void TestSingleUser() { - AddStep("add user", () => + setupRoomWithUsers(new APIUser { - MultiplayerClient.AddUser(new APIUser - { - Id = 2, - Statistics = { GlobalRank = 1234 } - }); - - // Remove the local user so only the one above is displayed. - MultiplayerClient.RemoveUser(API.LocalUser.Value); + Id = 2, + Statistics = { GlobalRank = 1234 } }); } [Test] public void TestMultipleUsers() { - AddStep("add users", () => - { - MultiplayerClient.AddUser(new APIUser + setupRoomWithUsers( + new APIUser { Id = 2, Statistics = { GlobalRank = 1234 } - }); - - MultiplayerClient.AddUser(new APIUser + }, + new APIUser { Id = 3, Statistics = { GlobalRank = 3333 } - }); - - MultiplayerClient.AddUser(new APIUser + }, + new APIUser { Id = 4, Statistics = { GlobalRank = 4321 } }); - - // Remove the local user so only the ones above are displayed. - MultiplayerClient.RemoveUser(API.LocalUser.Value); - }); } [TestCase(1, 10)] @@ -73,22 +74,29 @@ namespace osu.Game.Tests.Visual.Multiplayer [TestCase(1000000, 10000000)] public void TestRange(int min, int max) { - AddStep("add users", () => - { - MultiplayerClient.AddUser(new APIUser + setupRoomWithUsers( + new APIUser { Id = 2, Statistics = { GlobalRank = min } - }); - - MultiplayerClient.AddUser(new APIUser + }, + new APIUser { Id = 3, Statistics = { GlobalRank = max } }); + } - // Remove the local user so only the ones above are displayed. - MultiplayerClient.RemoveUser(API.LocalUser.Value); + private void setupRoomWithUsers(params APIUser[] users) + { + AddStep("setup room", () => + { + multiplayerClient.SetupGet(m => m.Room).Returns(new MultiplayerRoom(0) + { + Users = new List(users.Select(apiUser => new MultiplayerRoomUser(apiUser.Id) { User = apiUser })) + }); + + multiplayerClient.Raise(m => m.RoomUpdated -= null); }); } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 18db710cee..4c10949225 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -32,7 +32,7 @@ namespace osu.Game.Online.Multiplayer /// /// Invoked when any change occurs to the multiplayer room. /// - public event Action? RoomUpdated; + public virtual event Action? RoomUpdated; /// /// Invoked when a new user joins the room. @@ -88,7 +88,7 @@ namespace osu.Game.Online.Multiplayer /// /// The joined . /// - public MultiplayerRoom? Room + public virtual MultiplayerRoom? Room { get { From bf677ad0ac8dc6383e5c249492f03d544becda9b Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 12 Apr 2022 20:28:14 +0800 Subject: [PATCH 0288/1276] Remove usages of BinaryFormatter. --- osu.Game/IO/Legacy/SerializationReader.cs | 91 +---------------------- osu.Game/IO/Legacy/SerializationWriter.cs | 11 +-- 2 files changed, 2 insertions(+), 100 deletions(-) diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs index f7b3f33e87..64e25d5add 100644 --- a/osu.Game/IO/Legacy/SerializationReader.cs +++ b/osu.Game/IO/Legacy/SerializationReader.cs @@ -3,11 +3,8 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.Reflection; using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters.Binary; using System.Text; namespace osu.Game.IO.Legacy @@ -186,98 +183,12 @@ namespace osu.Game.IO.Legacy return ReadCharArray(); case ObjType.otherType: - return DynamicDeserializer.Deserialize(BaseStream); + throw new IOException("Deserialization of arbitrary type is not supported."); default: return null; } } - - public static class DynamicDeserializer - { - private static VersionConfigToNamespaceAssemblyObjectBinder versionBinder; - private static BinaryFormatter formatter; - - private static void initialize() - { - versionBinder = new VersionConfigToNamespaceAssemblyObjectBinder(); - formatter = new BinaryFormatter - { - // AssemblyFormat = FormatterAssemblyStyle.Simple, - Binder = versionBinder - }; - } - - public static object Deserialize(Stream stream) - { - if (formatter == null) - initialize(); - - Debug.Assert(formatter != null, "formatter != null"); - - // ReSharper disable once PossibleNullReferenceException - return formatter.Deserialize(stream); - } - - #region Nested type: VersionConfigToNamespaceAssemblyObjectBinder - - public sealed class VersionConfigToNamespaceAssemblyObjectBinder : SerializationBinder - { - private readonly Dictionary cache = new Dictionary(); - - public override Type BindToType(string assemblyName, string typeName) - { - if (cache.TryGetValue(assemblyName + typeName, out var typeToDeserialize)) - return typeToDeserialize; - - List tmpTypes = new List(); - Type genType = null; - - if (typeName.Contains("System.Collections.Generic") && typeName.Contains("[[")) - { - string[] splitTypes = typeName.Split('['); - - foreach (string typ in splitTypes) - { - if (typ.Contains("Version")) - { - string asmTmp = typ.Substring(typ.IndexOf(',') + 1); - string asmName = asmTmp.Remove(asmTmp.IndexOf(']')).Trim(); - string typName = typ.Remove(typ.IndexOf(',')); - tmpTypes.Add(BindToType(asmName, typName)); - } - else if (typ.Contains("Generic")) - { - genType = BindToType(assemblyName, typ); - } - } - - if (genType != null && tmpTypes.Count > 0) - { - return genType.MakeGenericType(tmpTypes.ToArray()); - } - } - - string toAssemblyName = assemblyName.Split(',')[0]; - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - - foreach (Assembly a in assemblies) - { - if (a.FullName.Split(',')[0] == toAssemblyName) - { - typeToDeserialize = a.GetType(typeName); - break; - } - } - - cache.Add(assemblyName + typeName, typeToDeserialize); - - return typeToDeserialize; - } - } - - #endregion - } } public enum ObjType : byte diff --git a/osu.Game/IO/Legacy/SerializationWriter.cs b/osu.Game/IO/Legacy/SerializationWriter.cs index 9ebeaf616e..6dfb21e5bd 100644 --- a/osu.Game/IO/Legacy/SerializationWriter.cs +++ b/osu.Game/IO/Legacy/SerializationWriter.cs @@ -5,8 +5,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters; -using System.Runtime.Serialization.Formatters.Binary; using System.Text; // ReSharper disable ConditionIsAlwaysTrueOrFalse (we're allowing nulls to be passed to the writer where the underlying class doesn't). @@ -218,14 +216,7 @@ namespace osu.Game.IO.Legacy break; default: - Write((byte)ObjType.otherType); - BinaryFormatter b = new BinaryFormatter - { - // AssemblyFormat = FormatterAssemblyStyle.Simple, - TypeFormat = FormatterTypeStyle.TypesWhenNeeded - }; - b.Serialize(BaseStream, obj); - break; + throw new IOException("Serialization of arbitrary type is not supported."); } // switch } // if obj==null } // WriteObject From 80e312dbbc5737e6d6c093a92961ace301b1482a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 12 Apr 2022 20:29:13 +0800 Subject: [PATCH 0289/1276] Remove unused binary serialization related code. --- osu.Game/IO/Legacy/SerializationReader.cs | 10 ---------- osu.Game/IO/Legacy/SerializationWriter.cs | 8 -------- 2 files changed, 18 deletions(-) diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs index 64e25d5add..5423485c95 100644 --- a/osu.Game/IO/Legacy/SerializationReader.cs +++ b/osu.Game/IO/Legacy/SerializationReader.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Runtime.Serialization; using System.Text; namespace osu.Game.IO.Legacy @@ -23,15 +22,6 @@ namespace osu.Game.IO.Legacy public int RemainingBytes => (int)(stream.Length - stream.Position); - /// Static method to take a SerializationInfo object (an input to an ISerializable constructor) - /// and produce a SerializationReader from which serialized objects can be read . - public static SerializationReader GetReader(SerializationInfo info) - { - byte[] byteArray = (byte[])info.GetValue("X", typeof(byte[])); - MemoryStream ms = new MemoryStream(byteArray); - return new SerializationReader(ms); - } - /// Reads a string from the buffer. Overrides the base implementation so it can cope with nulls. public override string ReadString() { diff --git a/osu.Game/IO/Legacy/SerializationWriter.cs b/osu.Game/IO/Legacy/SerializationWriter.cs index 6dfb21e5bd..c9fff05bcc 100644 --- a/osu.Game/IO/Legacy/SerializationWriter.cs +++ b/osu.Game/IO/Legacy/SerializationWriter.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Runtime.Serialization; using System.Text; // ReSharper disable ConditionIsAlwaysTrueOrFalse (we're allowing nulls to be passed to the writer where the underlying class doesn't). @@ -221,13 +220,6 @@ namespace osu.Game.IO.Legacy } // if obj==null } // WriteObject - /// Adds the SerializationWriter buffer to the SerializationInfo at the end of GetObjectData(). - public void AddToInfo(SerializationInfo info) - { - byte[] b = ((MemoryStream)BaseStream).ToArray(); - info.AddValue("X", b, typeof(byte[])); - } - public void WriteRawBytes(byte[] b) { base.Write(b); From 1ebbef88ec75034295826c319b2a42189e505804 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 22:56:48 +0900 Subject: [PATCH 0290/1276] Refactor `GameplayChatDisplay` test scene to not require `MultiplayerTestScene` inheritance --- .../Visual/Multiplayer/TestSceneGameplayChatDisplay.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs index 512d206a06..c3487751b9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs @@ -9,13 +9,14 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; +using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.Play; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneGameplayChatDisplay : MultiplayerTestScene + public class TestSceneGameplayChatDisplay : OsuManualInputManagerTestScene { private GameplayChatDisplay chatDisplay; @@ -35,11 +36,9 @@ namespace osu.Game.Tests.Visual.Multiplayer } [SetUpSteps] - public override void SetUpSteps() + public void SetUpSteps() { - base.SetUpSteps(); - - AddStep("load chat display", () => Child = chatDisplay = new GameplayChatDisplay(SelectedRoom.Value) + AddStep("load chat display", () => Child = chatDisplay = new GameplayChatDisplay(new Room()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From ef7ab12b40c92c87c5cf2db5a57585efeec7466f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Apr 2022 23:01:37 +0900 Subject: [PATCH 0291/1276] Fix `MultiplayerMatchFooter` test crash due to missing `PopoverContainer` Clicking the countdown button would crash. I did consider removing the test altogether but maybe it'll be useful in the future? --- .../Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs index 6536ef2ca1..ff834d71a1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs @@ -3,7 +3,7 @@ using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; namespace osu.Game.Tests.Visual.Multiplayer @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUp] public new void Setup() => Schedule(() => { - Child = new Container + Child = new PopoverContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 63e2a84d7f4784f8bb824b70509f6d57c0a8d0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Apr 2022 20:54:54 +0200 Subject: [PATCH 0292/1276] Nest one more container to fix popover not being dismissable --- .../Multiplayer/TestSceneMultiplayerMatchFooter.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs index ff834d71a1..111f51675d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; @@ -17,9 +18,15 @@ namespace osu.Game.Tests.Visual.Multiplayer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Height = 50, - Child = new MultiplayerMatchFooter() + RelativeSizeAxes = Axes.Both, + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Height = 50, + Child = new MultiplayerMatchFooter() + } }; }); } From d73be7f1773425012c69d1aa25ff9efe0f3138cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 09:36:44 +0900 Subject: [PATCH 0293/1276] Reword comment to read clearer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs index b758c83a0a..b0a977dcbb 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private readonly Mock multiplayerClient = new Mock(); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => - // not used but required by the component. + // not used directly in component, but required due to it inheriting from OnlinePlayComposite. new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); [BackgroundDependencyLoader] From 282fccb4c8842ff30cde671989a620303f4dc0f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 12:24:47 +0900 Subject: [PATCH 0294/1276] Fix typo in xmldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 6ec21ef924..be19565657 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -610,7 +610,7 @@ namespace osu.Game.Screens.Play /// Specify and seek to a custom start time from which gameplay should be observed. /// /// - /// This performance a non-frame-stable seek. Intermediate hitobject judgements may not be applied or reverted correctly during this seek. + /// This performs a non-frame-stable seek. Intermediate hitobject judgements may not be applied or reverted correctly during this seek. /// /// The destination time to seek to. protected void SetGameplayStartTime(double time) From 017f3852c8a8354b08cb6a3f22a1624dea4b45db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 13:32:34 +0900 Subject: [PATCH 0295/1276] Replace incorrectly chosen `??=` with `??` --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index e569e5368b..41b40e9a91 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -91,7 +91,7 @@ namespace osu.Game.Screens.Play.HUD for (int i = 0; i < users.Length; i++) { - var user = users[i] ??= new APIUser + var user = users[i] ?? new APIUser { Id = playingUsers[i].UserID, Username = "Unknown user", From d4286255a094ac759e40e34c9be3aaa9caaf85b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 13:49:58 +0900 Subject: [PATCH 0296/1276] Expose and set `GameplayStartTime` directly, rather than via `Reset` parameter --- .../Multiplayer/Spectate/MultiSpectatorScreen.cs | 3 ++- osu.Game/Screens/Play/GameplayClockContainer.cs | 8 ++------ osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 3 ++- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 523301f4fd..2d03276fe5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -197,7 +197,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate .DefaultIfEmpty(0) .Min(); - masterClockContainer.Reset(true, startTime); + masterClockContainer.StartTime = startTime; + masterClockContainer.Reset(true); // Although the clock has been started, this flag is set to allow for later synchronisation state changes to also be able to start it. canStartMasterClock = true; diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index f2df00d691..721abc66f8 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Play /// If not set, a value of zero will be used. /// Importantly, the value will be inferred from the current ruleset in unless specified. /// - protected double? StartTime { get; set; } + public double? StartTime { get; set; } /// /// Creates a new . @@ -116,12 +116,8 @@ namespace osu.Game.Screens.Play /// Resets this and the source to an initial state ready for gameplay. /// /// Whether to start the clock immediately, if not already started. - /// A time to use for future calls as the definite start of gameplay. - public void Reset(bool startClock = false, double? gameplayStartTime = null) + public void Reset(bool startClock = false) { - if (gameplayStartTime != null) - StartTime = gameplayStartTime; - // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 54901746fc..88dc6db0f7 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -100,9 +100,9 @@ namespace osu.Game.Screens.Play bool isStarted = !IsPaused.Value; // If a custom start time was not specified, calculate the best value to use. - double gameplayStartTime = StartTime ?? findEarliestStartTime(); + StartTime ??= findEarliestStartTime(); - Reset(startClock: isStarted, gameplayStartTime: gameplayStartTime); + Reset(startClock: isStarted); } private double findEarliestStartTime() diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index be19565657..f99b07c313 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -621,7 +621,8 @@ namespace osu.Game.Screens.Play bool wasFrameStable = DrawableRuleset.FrameStablePlayback; DrawableRuleset.FrameStablePlayback = false; - GameplayClockContainer.Reset(gameplayStartTime: time); + GameplayClockContainer.StartTime = time; + GameplayClockContainer.Reset(); // Delay resetting frame-stable playback for one frame to give the FrameStabilityContainer a chance to seek. frameStablePlaybackResetDelegate = ScheduleAfterChildren(() => DrawableRuleset.FrameStablePlayback = wasFrameStable); From a59c6013c790f305d8567d98ec4e3a846c29fdeb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 14:03:52 +0900 Subject: [PATCH 0297/1276] Rename `latestGameplayStartTime` to `skipTargetTime` --- .../Play/MasterGameplayClockContainer.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 88dc6db0f7..61f31db435 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Play private IDisposable beatmapOffsetSubscription; - private readonly double latestGameplayStartTime; + private readonly double skipTargetTime; [Resolved] private RealmAccess realm { get; set; } @@ -70,17 +70,17 @@ namespace osu.Game.Screens.Play /// Create a new master gameplay clock container. /// /// The beatmap to be used for time and metadata references. - /// The latest time which should be used when introducing gameplay. Will be used when skipping forward. - /// Whether to start from the provided latest start time rather than zero. - public MasterGameplayClockContainer(WorkingBeatmap beatmap, double latestGameplayStartTime, bool startFromLatestStartTime = false) + /// The latest time which should be used when introducing gameplay. Will be used when skipping forward. + /// Whether to start from the provided latest start time rather than zero. + public MasterGameplayClockContainer(WorkingBeatmap beatmap, double skipTargetTime, bool startFromSkipTarget = false) : base(beatmap.Track) { this.beatmap = beatmap; - this.latestGameplayStartTime = latestGameplayStartTime; + this.skipTargetTime = skipTargetTime; - if (startFromLatestStartTime) - StartTime = latestGameplayStartTime; + if (startFromSkipTarget) + StartTime = skipTargetTime; } protected override void LoadComplete() @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Play // generally this is either zero, or some point earlier than zero in the case of storyboards, lead-ins etc. // start with the originally provided latest time (if before zero). - double time = Math.Min(0, latestGameplayStartTime); + double time = Math.Min(0, skipTargetTime); // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. // this is commonly used to display an intro before the audio track start. @@ -183,10 +183,10 @@ namespace osu.Game.Screens.Play /// public void Skip() { - if (GameplayClock.CurrentTime > latestGameplayStartTime - MINIMUM_SKIP_TIME) + if (GameplayClock.CurrentTime > skipTargetTime - MINIMUM_SKIP_TIME) return; - double skipTarget = latestGameplayStartTime - MINIMUM_SKIP_TIME; + double skipTarget = skipTargetTime - MINIMUM_SKIP_TIME; if (GameplayClock.CurrentTime < 0 && skipTarget > 6000) // double skip exception for storyboards with very long intros From fbf0e5a45cf4f4f073126a717b35c6edf258c412 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 14:09:49 +0900 Subject: [PATCH 0298/1276] Remove `startFromSkipTarget` parameter and update usages that required said behaviour --- osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs | 7 +++++-- .../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 8 ++++---- osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs | 2 +- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 7 +------ 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index 76ec35d87d..e0a497cf24 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Gameplay [Test] public void TestSampleHasLifetimeEndWithInitialClockTime() { - GameplayClockContainer gameplayContainer = null; + MasterGameplayClockContainer gameplayContainer = null; DrawableStoryboardSample sample = null; AddStep("create container", () => @@ -96,8 +96,11 @@ namespace osu.Game.Tests.Gameplay var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); working.LoadTrack(); - Add(gameplayContainer = new MasterGameplayClockContainer(working, 1000, true) + const double start_time = 1000; + + Add(gameplayContainer = new MasterGameplayClockContainer(working, start_time) { + StartTime = start_time, IsPaused = { Value = true }, Child = new FrameStabilityContainer { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 653a35417e..703b526e8c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -464,16 +464,16 @@ namespace osu.Game.Tests.Visual.Multiplayer private class TestMultiSpectatorScreen : MultiSpectatorScreen { - private readonly double? gameplayStartTime; + private readonly double? startTime; - public TestMultiSpectatorScreen(Room room, MultiplayerRoomUser[] users, double? gameplayStartTime = null) + public TestMultiSpectatorScreen(Room room, MultiplayerRoomUser[] users, double? startTime = null) : base(room, users) { - this.gameplayStartTime = gameplayStartTime; + this.startTime = startTime; } protected override MasterGameplayClockContainer CreateMasterGameplayClockContainer(WorkingBeatmap beatmap) - => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0, true); + => new MasterGameplayClockContainer(beatmap, 0) { StartTime = startTime ?? 0 }; } } } diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs index f49603c754..d9e19df350 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Edit.GameplayTest } protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) - => new MasterGameplayClockContainer(beatmap, editorState.Time, true); + => new MasterGameplayClockContainer(beatmap, gameplayStart) { StartTime = editorState.Time }; protected override void LoadComplete() { diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 61f31db435..c5a5c14070 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -71,16 +71,11 @@ namespace osu.Game.Screens.Play /// /// The beatmap to be used for time and metadata references. /// The latest time which should be used when introducing gameplay. Will be used when skipping forward. - /// Whether to start from the provided latest start time rather than zero. - public MasterGameplayClockContainer(WorkingBeatmap beatmap, double skipTargetTime, bool startFromSkipTarget = false) + public MasterGameplayClockContainer(WorkingBeatmap beatmap, double skipTargetTime) : base(beatmap.Track) { this.beatmap = beatmap; - this.skipTargetTime = skipTargetTime; - - if (startFromSkipTarget) - StartTime = skipTargetTime; } protected override void LoadComplete() From 56427becbba54e042c167edf8c59b03e37136aba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 14:33:28 +0900 Subject: [PATCH 0299/1276] Move logging and early return into `UndeleteForReuse` method itself --- osu.Game/Stores/RealmArchiveModelImporter.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Stores/RealmArchiveModelImporter.cs b/osu.Game/Stores/RealmArchiveModelImporter.cs index 353a2164bb..6d1449a4b4 100644 --- a/osu.Game/Stores/RealmArchiveModelImporter.cs +++ b/osu.Game/Stores/RealmArchiveModelImporter.cs @@ -351,8 +351,7 @@ namespace osu.Game.Stores using (var transaction = realm.BeginWrite()) { - if (existing.DeletePending) - UndeleteForReuse(existing); + UndeleteForReuse(existing); transaction.Commit(); } @@ -388,12 +387,7 @@ namespace osu.Game.Stores { LogForModel(item, @$"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import."); - if (existing.DeletePending) - { - LogForModel(item, $@"Existing {HumanisedModelName}'s deletion flag has been removed"); - UndeleteForReuse(existing); - } - + UndeleteForReuse(existing); transaction.Commit(); return existing.ToLive(Realm); @@ -539,6 +533,10 @@ namespace osu.Game.Stores /// The existing model. protected virtual void UndeleteForReuse(TModel existing) { + if (!existing.DeletePending) + return; + + LogForModel(existing, $@"Existing {HumanisedModelName}'s deletion flag has been removed to allow for reuse."); existing.DeletePending = false; } From 2bb0d9e6d3d52a4e9da8f7405a95cedc1a8cfe2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 17:02:33 +0900 Subject: [PATCH 0300/1276] Expose required classes/properties for mocking --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 4 ++-- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 4c10949225..e29aee2c6d 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -114,12 +114,12 @@ namespace osu.Game.Online.Multiplayer /// /// The corresponding to the local player, if available. /// - public MultiplayerRoomUser? LocalUser => Room?.Users.SingleOrDefault(u => u.User?.Id == API.LocalUser.Value.Id); + public virtual MultiplayerRoomUser? LocalUser => Room?.Users.SingleOrDefault(u => u.User?.Id == API.LocalUser.Value.Id); /// /// Whether the is the host in . /// - public bool IsHost + public virtual bool IsHost { get { diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 07506ba1f0..4ca6d79b19 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -25,7 +25,7 @@ namespace osu.Game.Online.Rooms /// This differs from a regular download tracking composite as this accounts for the /// databased beatmap set's checksum, to disallow from playing with an altered version of the beatmap. /// - public sealed class OnlinePlayBeatmapAvailabilityTracker : CompositeDrawable + public class OnlinePlayBeatmapAvailabilityTracker : CompositeDrawable { public readonly IBindable SelectedItem = new Bindable(); @@ -41,7 +41,7 @@ namespace osu.Game.Online.Rooms /// /// The availability state of the currently selected playlist item. /// - public IBindable Availability => availability; + public virtual IBindable Availability => availability; private readonly Bindable availability = new Bindable(BeatmapAvailability.NotDownloaded()); From 1f01714ec207c4fb4090efe72769234037bc9071 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 17:02:53 +0900 Subject: [PATCH 0301/1276] Apply initial structural changes to `TestSceneMatchStartControl` --- .../Multiplayer/TestSceneMatchStartControl.cs | 233 +++++++++++------- 1 file changed, 148 insertions(+), 85 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 5c2fd26857..dcf73f9633 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -3,74 +3,119 @@ using System; using System.Linq; +using Moq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Utils; -using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.Countdown; using osu.Game.Online.Rooms; -using osu.Game.Rulesets; +using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; -using osu.Game.Tests.Resources; using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMatchStartControl : MultiplayerTestScene + public class TestSceneMatchStartControl : OsuManualInputManagerTestScene { + private readonly Mock multiplayerClient = new Mock(); + private readonly Mock availabilityTracker = new Mock(); + + private readonly Bindable beatmapAvailability = new Bindable(); + private readonly Bindable room = new Bindable(); + + private MultiplayerRoom multiplayerRoom; + private MultiplayerRoomUser localUser; + private OngoingOperationTracker ongoingOperationTracker; + + private PopoverContainer content; private MatchStartControl control; - private BeatmapSetInfo importedSet; - private readonly Bindable selectedItem = new Bindable(); - - private BeatmapManager beatmaps; - private RulesetStore rulesets; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => + new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) { Model = { BindTarget = room } }; [BackgroundDependencyLoader] - private void load(GameHost host, AudioManager audio) + private void load() { - Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default)); - Dependencies.Cache(Realm); + Dependencies.CacheAs(multiplayerClient.Object); + Dependencies.CacheAs(ongoingOperationTracker = new OngoingOperationTracker()); + + availabilityTracker.SetupGet(a => a.Availability).Returns(beatmapAvailability); + Dependencies.CacheAs(availabilityTracker.Object); + + multiplayerClient.SetupGet(m => m.LocalUser).Returns(() => localUser); + multiplayerClient.SetupGet(m => m.Room).Returns(() => multiplayerRoom); + + // By default, the local user is to be the host. + multiplayerClient.SetupGet(m => m.IsHost).Returns(true); + + // Assume all state changes are accepted by the server. + multiplayerClient.Setup(m => m.ChangeState(It.IsAny())) + .Callback((MultiplayerUserState r) => + { + localUser.State = r; + raiseRoomUpdated(); + }); + + Children = new Drawable[] + { + ongoingOperationTracker, + content = new PopoverContainer { RelativeSizeAxes = Axes.Both } + }; } - [SetUp] - public new void Setup() => Schedule(() => + [SetUpSteps] + public void SetUpSteps() { - AvailabilityTracker.SelectedItem.BindTo(selectedItem); - - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); - importedSet = beatmaps.GetAllUsableBeatmapSets().First(); - Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()); - - selectedItem.Value = new PlaylistItem(Beatmap.Value.BeatmapInfo) + AddStep("reset state", () => { - RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID - }; + multiplayerClient.Invocations.Clear(); - Child = new PopoverContainer + beatmapAvailability.Value = BeatmapAvailability.LocallyAvailable(); + + var playlistItem = new PlaylistItem(Beatmap.Value.BeatmapInfo) + { + RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID + }; + + room.Value = new Room + { + Playlist = { playlistItem }, + CurrentPlaylistItem = { Value = playlistItem } + }; + + multiplayerRoom = new MultiplayerRoom(0) + { + Playlist = + { + new MultiplayerPlaylistItem(playlistItem), + }, + Users = + { + (localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id) { User = API.LocalUser.Value }), + } + }; + }); + + AddStep("create control", () => { - RelativeSizeAxes = Axes.Both, - Child = control = new MatchStartControl + content.Child = control = new MatchStartControl { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(250, 50), - } - }; - }); + }; + }); + } [Test] public void TestStartWithCountdown() @@ -85,8 +130,12 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - AddStep("finish countdown", () => MultiplayerClient.SkipToEndOfCountdown()); - AddUntilStep("match started", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.WaitingForLoad); + AddStep("check request received", () => + { + multiplayerClient.Verify(m => m.SendMatchRequest(It.Is(req => + req.Duration == TimeSpan.FromSeconds(10) + )), Times.Once); + }); } [Test] @@ -110,7 +159,7 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - AddStep("finish countdown", () => MultiplayerClient.SkipToEndOfCountdown()); + // AddStep("finish countdown", skipToEndOfCountdown); AddUntilStep("match not started", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); } @@ -119,32 +168,32 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add second user as host", () => { - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); - MultiplayerClient.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }); + multiplayerClient.Object.TransferHost(2); }); - AddStep("start with countdown", () => MultiplayerClient.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(2) }).WaitSafely()); + AddStep("start with countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(2) }).WaitSafely()); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); } [Test] public void TestCountdownWhileSpectating() { - AddStep("set spectating", () => MultiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating)); - AddUntilStep("local user is spectating", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating); + AddStep("set spectating", () => changeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating)); + AddUntilStep("local user is spectating", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Spectating); AddAssert("countdown button is visible", () => this.ChildrenOfType().Single().IsPresent); AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); - AddStep("add second user", () => MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" })); + AddStep("add second user", () => addUser(new APIUser { Id = 2, Username = "Another user" })); AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); - AddStep("set second user ready", () => MultiplayerClient.ChangeUserState(2, MultiplayerUserState.Ready)); + AddStep("set second user ready", () => changeUserState(2, MultiplayerUserState.Ready)); AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); } @@ -153,44 +202,44 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add second user as host", () => { - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); - MultiplayerClient.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }); + multiplayerClient.Object.TransferHost(2); }); - AddStep("start countdown", () => MultiplayerClient.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(1) }).WaitSafely()); - AddUntilStep("countdown started", () => MultiplayerClient.Room?.Countdown != null); + AddStep("start countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(1) }).WaitSafely()); + AddUntilStep("countdown started", () => multiplayerClient.Object.Room?.Countdown != null); - AddStep("transfer host to local user", () => MultiplayerClient.TransferHost(API.LocalUser.Value.OnlineID)); - AddUntilStep("local user is host", () => MultiplayerClient.Room?.Host?.Equals(MultiplayerClient.LocalUser) == true); + AddStep("transfer host to local user", () => multiplayerClient.Object.TransferHost(API.LocalUser.Value.OnlineID)); + AddUntilStep("local user is host", () => multiplayerClient.Object.Room?.Host?.Equals(multiplayerClient.Object.LocalUser) == true); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); - AddAssert("countdown still active", () => MultiplayerClient.Room?.Countdown != null); + AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); + AddAssert("countdown still active", () => multiplayerClient.Object.Room?.Countdown != null); } [Test] public void TestCountdownButtonVisibilityWithAutoStartEnablement() { ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); + AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); AddUntilStep("countdown button visible", () => this.ChildrenOfType().Single().IsPresent); - AddStep("enable auto start", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); + AddStep("enable auto start", () => multiplayerClient.Object.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); + AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); AddUntilStep("countdown button not visible", () => !this.ChildrenOfType().Single().IsPresent); } [Test] public void TestClickingReadyButtonUnReadiesDuringAutoStart() { - AddStep("enable auto start", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); + AddStep("enable auto start", () => multiplayerClient.Object.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); + AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("local user became idle", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Idle); + AddUntilStep("local user became idle", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Idle); } [Test] @@ -204,9 +253,9 @@ namespace osu.Game.Tests.Visual.Multiplayer return readyButton.Enabled.Value; }); - AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); + // TODO: AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); AddUntilStep("ready button disabled", () => !readyButton.Enabled.Value); - AddStep("undelete beatmap", () => beatmaps.Undelete(importedSet)); + // TODO: AddStep("undelete beatmap", () => beatmaps.Undelete(importedSet)); AddUntilStep("ready button enabled back", () => readyButton.Enabled.Value); } @@ -215,15 +264,15 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add second user as host", () => { - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); - MultiplayerClient.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }); + multiplayerClient.Object.TransferHost(2); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); } [TestCase(true)] @@ -232,14 +281,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("setup", () => { - MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0); + multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); if (!allReady) - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); + addUser(new APIUser { Id = 2, Username = "Another user" }); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); verifyGameplayStartFlow(); } @@ -249,12 +298,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add host", () => { - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); - MultiplayerClient.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }); + multiplayerClient.Object.TransferHost(2); }); ClickButtonWhenEnabled(); - AddStep("make user host", () => MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0)); + AddStep("make user host", () => multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0)); verifyGameplayStartFlow(); } @@ -264,17 +313,17 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("setup", () => { - MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0); - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); + multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); + addUser(new APIUser { Id = 2, Username = "Another user" }); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); - AddStep("transfer host", () => MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[1].UserID ?? 0)); + AddStep("transfer host", () => multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[1].UserID ?? 0)); ClickButtonWhenEnabled(); - AddUntilStep("user is idle (match not started)", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle (match not started)", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } @@ -285,42 +334,56 @@ namespace osu.Game.Tests.Visual.Multiplayer const int users = 10; AddStep("setup", () => { - MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0); + multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); for (int i = 0; i < users; i++) - MultiplayerClient.AddUser(new APIUser { Id = i, Username = "Another user" }); + addUser(new APIUser { Id = i, Username = "Another user" }); }); if (!isHost) - AddStep("transfer host", () => MultiplayerClient.TransferHost(2)); + AddStep("transfer host", () => multiplayerClient.Object.TransferHost(2)); ClickButtonWhenEnabled(); AddRepeatStep("change user ready state", () => { - MultiplayerClient.ChangeUserState(RNG.Next(0, users), RNG.NextBool() ? MultiplayerUserState.Ready : MultiplayerUserState.Idle); + changeUserState(RNG.Next(0, users), RNG.NextBool() ? MultiplayerUserState.Ready : MultiplayerUserState.Idle); }, 20); AddRepeatStep("ready all users", () => { - var nextUnready = MultiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + var nextUnready = multiplayerClient.Object.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); if (nextUnready != null) - MultiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); + changeUserState(nextUnready.UserID, MultiplayerUserState.Ready); }, users); } private void verifyGameplayStartFlow() { - AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user waiting for load", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + AddUntilStep("user waiting for load", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); AddStep("finish gameplay", () => { - MultiplayerClient.ChangeUserState(MultiplayerClient.Room?.Users[0].UserID ?? 0, MultiplayerUserState.Loaded); - MultiplayerClient.ChangeUserState(MultiplayerClient.Room?.Users[0].UserID ?? 0, MultiplayerUserState.FinishedPlay); + changeUserState(multiplayerClient.Object.Room?.Users[0].UserID ?? 0, MultiplayerUserState.Loaded); + changeUserState(multiplayerClient.Object.Room?.Users[0].UserID ?? 0, MultiplayerUserState.FinishedPlay); }); AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } + + private void changeUserState(int userId, MultiplayerUserState newState) + { + multiplayerRoom.Users.Single(u => u.UserID == userId).State = newState; + raiseRoomUpdated(); + } + + private void addUser(APIUser user) + { + multiplayerRoom.Users.Add(new MultiplayerRoomUser(user.Id) { User = user }); + raiseRoomUpdated(); + } + + private void raiseRoomUpdated() => multiplayerClient.Raise(m => m.RoomUpdated -= null); } } From ca44d2c4dcda8df808ce615f25ef5002e534b98a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 17:32:04 +0900 Subject: [PATCH 0302/1276] Setup start/stop requests --- .../Multiplayer/TestSceneMatchStartControl.cs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index dcf73f9633..dabf8c5267 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -66,6 +66,23 @@ namespace osu.Game.Tests.Visual.Multiplayer raiseRoomUpdated(); }); + multiplayerClient.Setup(m => m.SendMatchRequest(It.IsAny())) + .Callback((MatchUserRequest request) => + { + switch (request) + { + case StartMatchCountdownRequest countdownStart: + multiplayerRoom.Countdown = new MatchStartCountdown { TimeRemaining = countdownStart.Duration }; + raiseRoomUpdated(); + break; + + case StopCountdownRequest _: + multiplayerRoom.Countdown = null; + raiseRoomUpdated(); + break; + } + }); + Children = new Drawable[] { ongoingOperationTracker, @@ -151,6 +168,13 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); + AddStep("check request received", () => + { + multiplayerClient.Verify(m => m.SendMatchRequest(It.Is(req => + req.Duration == TimeSpan.FromSeconds(10) + )), Times.Once); + }); + ClickButtonWhenEnabled(); AddStep("click the cancel button", () => { @@ -159,8 +183,10 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - // AddStep("finish countdown", skipToEndOfCountdown); - AddUntilStep("match not started", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); + AddStep("check request received", () => + { + multiplayerClient.Verify(m => m.SendMatchRequest(It.IsAny()), Times.Once); + }); } [Test] From 0a5a3415f8e5a54374f22f841a10850aea1a1430 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 20:28:53 +0900 Subject: [PATCH 0303/1276] Setup host switching and avoid starting gameplay --- .../Multiplayer/TestSceneMatchStartControl.cs | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index dabf8c5267..dccfd98c4d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Threading.Tasks; using Moq; using NUnit.Framework; using osu.Framework.Allocation; @@ -11,6 +12,7 @@ using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Logging; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; @@ -62,18 +64,22 @@ namespace osu.Game.Tests.Visual.Multiplayer multiplayerClient.Setup(m => m.ChangeState(It.IsAny())) .Callback((MultiplayerUserState r) => { + Logger.Log($"Changing local user state from {localUser.State} to {r}"); localUser.State = r; raiseRoomUpdated(); }); + // We don't want to have to handle entering into actual gameplay in this test. + // Returning a failed task should always allow the button to always stay in a clickable state. + multiplayerClient.Setup(m => m.StartMatch()).Returns(Task.FromException(new InvalidOperationException())); + multiplayerClient.Setup(m => m.SendMatchRequest(It.IsAny())) .Callback((MatchUserRequest request) => { switch (request) { case StartMatchCountdownRequest countdownStart: - multiplayerRoom.Countdown = new MatchStartCountdown { TimeRemaining = countdownStart.Duration }; - raiseRoomUpdated(); + setRoomCountdown(countdownStart.Duration); break; case StopCountdownRequest _: @@ -110,16 +116,16 @@ namespace osu.Game.Tests.Visual.Multiplayer CurrentPlaylistItem = { Value = playlistItem } }; + localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id) { User = API.LocalUser.Value }; + multiplayerRoom = new MultiplayerRoom(0) { Playlist = { new MultiplayerPlaylistItem(playlistItem), }, - Users = - { - (localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id) { User = API.LocalUser.Value }), - } + Users = { localUser }, + Host = localUser, }; }); @@ -160,6 +166,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { ClickButtonWhenEnabled(); AddUntilStep("countdown button shown", () => this.ChildrenOfType().SingleOrDefault()?.IsPresent == true); + ClickButtonWhenEnabled(); AddStep("click the first countdown button", () => { @@ -192,19 +199,15 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestReadyAndUnReadyDuringCountdown() { - AddStep("add second user as host", () => - { - addUser(new APIUser { Id = 2, Username = "Another user" }); - multiplayerClient.Object.TransferHost(2); - }); + AddStep("add second user as host", () => addUser(new APIUser { Id = 2, Username = "Another user" }, true)); - AddStep("start with countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(2) }).WaitSafely()); + AddStep("start countdown", () => setRoomCountdown(TimeSpan.FromMinutes(1))); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => localUser.State == MultiplayerUserState.Idle); } [Test] @@ -398,15 +401,27 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } + private void setRoomCountdown(TimeSpan duration) + { + multiplayerRoom.Countdown = new MatchStartCountdown { TimeRemaining = duration }; + raiseRoomUpdated(); + } + private void changeUserState(int userId, MultiplayerUserState newState) { multiplayerRoom.Users.Single(u => u.UserID == userId).State = newState; raiseRoomUpdated(); } - private void addUser(APIUser user) + private void addUser(APIUser user, bool asHost = false) { - multiplayerRoom.Users.Add(new MultiplayerRoomUser(user.Id) { User = user }); + var multiplayerRoomUser = new MultiplayerRoomUser(user.Id) { User = user }; + + multiplayerRoom.Users.Add(multiplayerRoomUser); + + if (asHost) + multiplayerRoom.Host = multiplayerRoomUser; + raiseRoomUpdated(); } From 331242d585ab23d8f11d8863ac991bb74600d038 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 20:38:39 +0900 Subject: [PATCH 0304/1276] Update transfer host logic and simplify local user checks --- .../Multiplayer/TestSceneMatchStartControl.cs | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index dccfd98c4d..e10a63d010 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -231,14 +231,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add second user as host", () => { - addUser(new APIUser { Id = 2, Username = "Another user" }); - multiplayerClient.Object.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }, true); }); AddStep("start countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(1) }).WaitSafely()); AddUntilStep("countdown started", () => multiplayerClient.Object.Room?.Countdown != null); - AddStep("transfer host to local user", () => multiplayerClient.Object.TransferHost(API.LocalUser.Value.OnlineID)); + AddStep("transfer host to local user", () => transferHost(localUser)); AddUntilStep("local user is host", () => multiplayerClient.Object.Room?.Host?.Equals(multiplayerClient.Object.LocalUser) == true); ClickButtonWhenEnabled(); @@ -293,15 +292,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add second user as host", () => { - addUser(new APIUser { Id = 2, Username = "Another user" }); - multiplayerClient.Object.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }, true); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => localUser.State == MultiplayerUserState.Idle); } [TestCase(true)] @@ -310,14 +308,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("setup", () => { - multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); + transferHost(multiplayerRoom.Users[0]); if (!allReady) addUser(new APIUser { Id = 2, Username = "Another user" }); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); verifyGameplayStartFlow(); } @@ -327,12 +325,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add host", () => { - addUser(new APIUser { Id = 2, Username = "Another user" }); - multiplayerClient.Object.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }, true); }); ClickButtonWhenEnabled(); - AddStep("make user host", () => multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0)); + + AddStep("make user host", () => transferHost(localUser)); verifyGameplayStartFlow(); } @@ -342,17 +340,16 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("setup", () => { - multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); addUser(new APIUser { Id = 2, Username = "Another user" }); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); - AddStep("transfer host", () => multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[1].UserID ?? 0)); + AddStep("transfer host", () => transferHost(multiplayerRoom.Users[1])); ClickButtonWhenEnabled(); - AddUntilStep("user is idle (match not started)", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle (match not started)", () => localUser.State == MultiplayerUserState.Idle); AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } @@ -363,14 +360,11 @@ namespace osu.Game.Tests.Visual.Multiplayer const int users = 10; AddStep("setup", () => { - multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); + transferHost(localUser); for (int i = 0; i < users; i++) - addUser(new APIUser { Id = i, Username = "Another user" }); + addUser(new APIUser { Id = i, Username = "Another user" }, !isHost && i == 2); }); - if (!isHost) - AddStep("transfer host", () => multiplayerClient.Object.TransferHost(2)); - ClickButtonWhenEnabled(); AddRepeatStep("change user ready state", () => @@ -388,14 +382,14 @@ namespace osu.Game.Tests.Visual.Multiplayer private void verifyGameplayStartFlow() { - AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user waiting for load", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + AddUntilStep("user waiting for load", () => localUser.State == MultiplayerUserState.WaitingForLoad); AddStep("finish gameplay", () => { - changeUserState(multiplayerClient.Object.Room?.Users[0].UserID ?? 0, MultiplayerUserState.Loaded); - changeUserState(multiplayerClient.Object.Room?.Users[0].UserID ?? 0, MultiplayerUserState.FinishedPlay); + changeUserState(localUser.UserID, MultiplayerUserState.Loaded); + changeUserState(localUser.UserID, MultiplayerUserState.FinishedPlay); }); AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); @@ -420,11 +414,17 @@ namespace osu.Game.Tests.Visual.Multiplayer multiplayerRoom.Users.Add(multiplayerRoomUser); if (asHost) - multiplayerRoom.Host = multiplayerRoomUser; + transferHost(multiplayerRoomUser); raiseRoomUpdated(); } + private void transferHost(MultiplayerRoomUser user) + { + multiplayerRoom.Host = user; + raiseRoomUpdated(); + } + private void raiseRoomUpdated() => multiplayerClient.Raise(m => m.RoomUpdated -= null); } } From 4b6a9fbf474031d3df852a099cce731a95c7fa91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 20:41:56 +0900 Subject: [PATCH 0305/1276] Reimplement beatmap availability test --- .../Visual/Multiplayer/TestSceneMatchStartControl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index e10a63d010..16f7426360 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -281,9 +281,9 @@ namespace osu.Game.Tests.Visual.Multiplayer return readyButton.Enabled.Value; }); - // TODO: AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); + AddStep("mark beatmap not available", () => beatmapAvailability.Value = BeatmapAvailability.NotDownloaded()); AddUntilStep("ready button disabled", () => !readyButton.Enabled.Value); - // TODO: AddStep("undelete beatmap", () => beatmaps.Undelete(importedSet)); + AddStep("mark beatmap available", () => beatmapAvailability.Value = BeatmapAvailability.LocallyAvailable()); AddUntilStep("ready button enabled back", () => readyButton.Enabled.Value); } From 8a7b37856c8902681a35f5f9b786af5f79fc23bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 20:51:05 +0900 Subject: [PATCH 0306/1276] Implement `IsHost` to return correct value for current state --- osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 16f7426360..bf980b0694 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Multiplayer multiplayerClient.SetupGet(m => m.Room).Returns(() => multiplayerRoom); // By default, the local user is to be the host. - multiplayerClient.SetupGet(m => m.IsHost).Returns(true); + multiplayerClient.SetupGet(m => m.IsHost).Returns(() => ReferenceEquals(multiplayerRoom.Host, localUser)); // Assume all state changes are accepted by the server. multiplayerClient.Setup(m => m.ChangeState(It.IsAny())) From c0ad91796d75e2f38b5117c0b484589cba1c588b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 20:57:40 +0900 Subject: [PATCH 0307/1276] Fix gameplay start flow --- .../Multiplayer/TestSceneMatchStartControl.cs | 16 +++++++--------- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index bf980b0694..8af1187b67 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using System.Threading.Tasks; using Moq; using NUnit.Framework; using osu.Framework.Allocation; @@ -69,9 +68,8 @@ namespace osu.Game.Tests.Visual.Multiplayer raiseRoomUpdated(); }); - // We don't want to have to handle entering into actual gameplay in this test. - // Returning a failed task should always allow the button to always stay in a clickable state. - multiplayerClient.Setup(m => m.StartMatch()).Returns(Task.FromException(new InvalidOperationException())); + multiplayerClient.Setup(m => m.StartMatch()) + .Callback(() => multiplayerClient.Raise(m => m.LoadRequested -= null)); multiplayerClient.Setup(m => m.SendMatchRequest(It.IsAny())) .Callback((MatchUserRequest request) => @@ -384,13 +382,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); + + AddStep("check start request received", () => multiplayerClient.Verify(m => m.StartMatch(), Times.Once)); AddUntilStep("user waiting for load", () => localUser.State == MultiplayerUserState.WaitingForLoad); - AddStep("finish gameplay", () => - { - changeUserState(localUser.UserID, MultiplayerUserState.Loaded); - changeUserState(localUser.UserID, MultiplayerUserState.FinishedPlay); - }); + AddUntilStep("ready button disabled", () => !control.ChildrenOfType().Single().Enabled.Value); + + AddStep("finish gameplay", () => changeUserState(localUser.UserID, MultiplayerUserState.Idle)); AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index e29aee2c6d..967220abbf 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -67,7 +67,7 @@ namespace osu.Game.Online.Multiplayer /// /// Invoked when the multiplayer server requests the current beatmap to be loaded into play. /// - public event Action? LoadRequested; + public virtual event Action? LoadRequested; /// /// Invoked when the multiplayer server requests gameplay to be started. From 8b1fd051c65c40febb3193a7cb25910135768f9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 21:04:15 +0900 Subject: [PATCH 0308/1276] Fix remaining autostart and gameplay tests --- .../Multiplayer/TestSceneMatchStartControl.cs | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 8af1187b67..acee35d3ba 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -69,7 +69,13 @@ namespace osu.Game.Tests.Visual.Multiplayer }); multiplayerClient.Setup(m => m.StartMatch()) - .Callback(() => multiplayerClient.Raise(m => m.LoadRequested -= null)); + .Callback(() => + { + multiplayerClient.Raise(m => m.LoadRequested -= null); + + // immediately "end" gameplay, as we don't care about that part of the process. + changeUserState(localUser.UserID, MultiplayerUserState.Idle); + }); multiplayerClient.Setup(m => m.SendMatchRequest(It.IsAny())) .Callback((MatchUserRequest request) => @@ -244,13 +250,13 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestCountdownButtonVisibilityWithAutoStartEnablement() + public void TestCountdownButtonVisibilityWithAutoStart() { ClickButtonWhenEnabled(); AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); AddUntilStep("countdown button visible", () => this.ChildrenOfType().Single().IsPresent); - AddStep("enable auto start", () => multiplayerClient.Object.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); + AddStep("enable auto start", () => changeRoomSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) })); ClickButtonWhenEnabled(); AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); @@ -260,7 +266,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestClickingReadyButtonUnReadiesDuringAutoStart() { - AddStep("enable auto start", () => multiplayerClient.Object.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); + AddStep("enable auto start", () => changeRoomSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) })); ClickButtonWhenEnabled(); AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); @@ -384,13 +390,6 @@ namespace osu.Game.Tests.Visual.Multiplayer ClickButtonWhenEnabled(); AddStep("check start request received", () => multiplayerClient.Verify(m => m.StartMatch(), Times.Once)); - AddUntilStep("user waiting for load", () => localUser.State == MultiplayerUserState.WaitingForLoad); - - AddUntilStep("ready button disabled", () => !control.ChildrenOfType().Single().Enabled.Value); - - AddStep("finish gameplay", () => changeUserState(localUser.UserID, MultiplayerUserState.Idle)); - - AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } private void setRoomCountdown(TimeSpan duration) @@ -423,6 +422,20 @@ namespace osu.Game.Tests.Visual.Multiplayer raiseRoomUpdated(); } + private void changeRoomSettings(MultiplayerRoomSettings settings) + { + multiplayerRoom.Settings = settings; + + // Changing settings should reset all user ready statuses. + foreach (var user in multiplayerRoom.Users) + { + if (user.State == MultiplayerUserState.Ready) + user.State = MultiplayerUserState.Idle; + } + + raiseRoomUpdated(); + } + private void raiseRoomUpdated() => multiplayerClient.Raise(m => m.RoomUpdated -= null); } } From c77a7b75d326c90b5444bc26302b4ce7d38e38a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 21:11:45 +0900 Subject: [PATCH 0309/1276] Tidy things up --- .../Multiplayer/TestSceneMatchStartControl.cs | 70 +++++++++---------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index acee35d3ba..ff6c02c4e5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -41,6 +41,8 @@ namespace osu.Game.Tests.Visual.Multiplayer private PopoverContainer content; private MatchStartControl control; + private OsuButton readyButton => control.ChildrenOfType().Single(); + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) { Model = { BindTarget = room } }; @@ -49,9 +51,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { Dependencies.CacheAs(multiplayerClient.Object); Dependencies.CacheAs(ongoingOperationTracker = new OngoingOperationTracker()); + Dependencies.CacheAs(availabilityTracker.Object); availabilityTracker.SetupGet(a => a.Availability).Returns(beatmapAvailability); - Dependencies.CacheAs(availabilityTracker.Object); multiplayerClient.SetupGet(m => m.LocalUser).Returns(() => localUser); multiplayerClient.SetupGet(m => m.Room).Returns(() => multiplayerRoom); @@ -149,6 +151,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { ClickButtonWhenEnabled(); AddUntilStep("countdown button shown", () => this.ChildrenOfType().SingleOrDefault()?.IsPresent == true); + ClickButtonWhenEnabled(); AddStep("click the first countdown button", () => { @@ -208,17 +211,17 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("start countdown", () => setRoomCountdown(TimeSpan.FromMinutes(1))); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => localUser.State == MultiplayerUserState.Idle); + checkLocalUserState(MultiplayerUserState.Idle); } [Test] public void TestCountdownWhileSpectating() { AddStep("set spectating", () => changeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating)); - AddUntilStep("local user is spectating", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Spectating); + checkLocalUserState(MultiplayerUserState.Spectating); AddAssert("countdown button is visible", () => this.ChildrenOfType().Single().IsPresent); AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); @@ -239,27 +242,27 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddStep("start countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(1) }).WaitSafely()); - AddUntilStep("countdown started", () => multiplayerClient.Object.Room?.Countdown != null); + AddUntilStep("countdown started", () => multiplayerRoom.Countdown != null); AddStep("transfer host to local user", () => transferHost(localUser)); - AddUntilStep("local user is host", () => multiplayerClient.Object.Room?.Host?.Equals(multiplayerClient.Object.LocalUser) == true); + AddUntilStep("local user is host", () => multiplayerRoom.Host?.Equals(multiplayerClient.Object.LocalUser) == true); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); - AddAssert("countdown still active", () => multiplayerClient.Object.Room?.Countdown != null); + checkLocalUserState(MultiplayerUserState.Ready); + AddAssert("countdown still active", () => multiplayerRoom.Countdown != null); } [Test] public void TestCountdownButtonVisibilityWithAutoStart() { ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); AddUntilStep("countdown button visible", () => this.ChildrenOfType().Single().IsPresent); AddStep("enable auto start", () => changeRoomSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) })); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); AddUntilStep("countdown button not visible", () => !this.ChildrenOfType().Single().IsPresent); } @@ -268,25 +271,20 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("enable auto start", () => changeRoomSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) })); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("local user became idle", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Idle); + checkLocalUserState(MultiplayerUserState.Idle); } [Test] public void TestDeletedBeatmapDisableReady() { - OsuButton readyButton = null; - - AddUntilStep("ensure ready button enabled", () => - { - readyButton = control.ChildrenOfType().Single(); - return readyButton.Enabled.Value; - }); + AddUntilStep("ready button enabled", () => readyButton.Enabled.Value); AddStep("mark beatmap not available", () => beatmapAvailability.Value = BeatmapAvailability.NotDownloaded()); AddUntilStep("ready button disabled", () => !readyButton.Enabled.Value); + AddStep("mark beatmap available", () => beatmapAvailability.Value = BeatmapAvailability.LocallyAvailable()); AddUntilStep("ready button enabled back", () => readyButton.Enabled.Value); } @@ -300,26 +298,21 @@ namespace osu.Game.Tests.Visual.Multiplayer }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => localUser.State == MultiplayerUserState.Idle); + checkLocalUserState(MultiplayerUserState.Idle); } [TestCase(true)] [TestCase(false)] public void TestToggleStateWhenHost(bool allReady) { - AddStep("setup", () => - { - transferHost(multiplayerRoom.Users[0]); - - if (!allReady) - addUser(new APIUser { Id = 2, Username = "Another user" }); - }); + if (!allReady) + AddStep("add other user", () => addUser(new APIUser { Id = 2, Username = "Another user" })); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); verifyGameplayStartFlow(); } @@ -334,7 +327,7 @@ namespace osu.Game.Tests.Visual.Multiplayer ClickButtonWhenEnabled(); - AddStep("make user host", () => transferHost(localUser)); + AddStep("make local user host", () => transferHost(localUser)); verifyGameplayStartFlow(); } @@ -348,13 +341,13 @@ namespace osu.Game.Tests.Visual.Multiplayer }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); AddStep("transfer host", () => transferHost(multiplayerRoom.Users[1])); ClickButtonWhenEnabled(); - AddUntilStep("user is idle (match not started)", () => localUser.State == MultiplayerUserState.Idle); - AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); + checkLocalUserState(MultiplayerUserState.Idle); + AddUntilStep("ready button enabled", () => readyButton.Enabled.Value); } [TestCase(true)] @@ -362,9 +355,9 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestManyUsersChangingState(bool isHost) { const int users = 10; - AddStep("setup", () => + + AddStep("add many users", () => { - transferHost(localUser); for (int i = 0; i < users; i++) addUser(new APIUser { Id = i, Username = "Another user" }, !isHost && i == 2); }); @@ -378,7 +371,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddRepeatStep("ready all users", () => { - var nextUnready = multiplayerClient.Object.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + var nextUnready = multiplayerRoom.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); if (nextUnready != null) changeUserState(nextUnready.UserID, MultiplayerUserState.Ready); }, users); @@ -386,12 +379,15 @@ namespace osu.Game.Tests.Visual.Multiplayer private void verifyGameplayStartFlow() { - AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); ClickButtonWhenEnabled(); AddStep("check start request received", () => multiplayerClient.Verify(m => m.StartMatch(), Times.Once)); } + private void checkLocalUserState(MultiplayerUserState state) => + AddUntilStep($"local user is {state}", () => localUser.State == state); + private void setRoomCountdown(TimeSpan duration) { multiplayerRoom.Countdown = new MatchStartCountdown { TimeRemaining = duration }; From 9b433280bdd9623b0a296b9a78ceccdec2a4215d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 21:24:33 +0900 Subject: [PATCH 0310/1276] Remove countdown implementation from `TestMultiplayerClient` --- .../Multiplayer/TestMultiplayerClient.cs | 89 +------------------ 1 file changed, 1 insertion(+), 88 deletions(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 4a974cf61d..21774b73a0 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -7,19 +7,16 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Development; using osu.Framework.Extensions; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; -using osu.Game.Online.Multiplayer.Countdown; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Mods; -using APIUser = osu.Game.Online.API.Requests.Responses.APIUser; namespace osu.Game.Tests.Visual.Multiplayer { @@ -141,16 +138,6 @@ namespace osu.Game.Tests.Visual.Multiplayer switch (Room.State) { case MultiplayerRoomState.Open: - // If there are no remaining ready users or the host is not ready, stop any existing countdown. - // Todo: This doesn't yet support non-match-start countdowns. - if (Room.Settings.AutoStartEnabled) - { - bool shouldHaveCountdown = !APIRoom.Playlist.GetCurrentItem()!.Expired && Room.Users.Any(u => u.State == MultiplayerUserState.Ready); - - if (shouldHaveCountdown && Room.Countdown == null) - startCountdown(new MatchStartCountdown { TimeRemaining = Room.Settings.AutoStartDuration }, StartMatch); - } - break; case MultiplayerRoomState.WaitingForLoad: @@ -317,16 +304,6 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } - private CancellationTokenSource? countdownSkipSource; - private CancellationTokenSource? countdownStopSource; - private Task countdownTask = Task.CompletedTask; - - /// - /// Skips to the end of the currently-running countdown, if one is running, - /// and runs the callback (e.g. to start the match) as soon as possible unless the countdown has been cancelled. - /// - public void SkipToEndOfCountdown() => countdownSkipSource?.Cancel(); - public override async Task SendMatchRequest(MatchUserRequest request) { Debug.Assert(Room != null); @@ -334,14 +311,6 @@ namespace osu.Game.Tests.Visual.Multiplayer switch (request) { - case StartMatchCountdownRequest matchCountdownRequest: - startCountdown(new MatchStartCountdown { TimeRemaining = matchCountdownRequest.Duration }, StartMatch); - break; - - case StopCountdownRequest _: - stopCountdown(); - break; - case ChangeTeamRequest changeTeam: TeamVersusRoomState roomState = (TeamVersusRoomState)Room.MatchState!; @@ -360,62 +329,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } } - private void startCountdown(MultiplayerCountdown countdown, Func continuation) - { - Debug.Assert(Room != null); - Debug.Assert(ThreadSafety.IsUpdateThread); - - stopCountdown(); - - // Note that this will leak CTSs, however this is a test method and we haven't noticed foregoing disposal of non-linked CTSs to be detrimental. - // If necessary, this can be moved into the final schedule below, and the class-level fields be nulled out accordingly. - var stopSource = countdownStopSource = new CancellationTokenSource(); - var skipSource = countdownSkipSource = new CancellationTokenSource(); - - Task lastCountdownTask = countdownTask; - countdownTask = start(); - - async Task start() - { - await lastCountdownTask; - - Schedule(() => - { - if (stopSource.IsCancellationRequested) - return; - - Room.Countdown = countdown; - MatchEvent(new CountdownChangedEvent { Countdown = countdown }); - }); - - try - { - using (var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(stopSource.Token, skipSource.Token)) - await Task.Delay(countdown.TimeRemaining, cancellationSource.Token).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // Clients need to be notified of cancellations in the following code. - } - - Schedule(() => - { - if (Room.Countdown != countdown) - return; - - Room.Countdown = null; - MatchEvent(new CountdownChangedEvent { Countdown = null }); - - if (stopSource.IsCancellationRequested) - return; - - continuation().WaitSafely(); - }); - } - } - - private void stopCountdown() => countdownStopSource?.Cancel(); - public override Task StartMatch() { Debug.Assert(Room != null); From 9c28c5b8470b76a915d0239e960551c9ebe44500 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 14 Apr 2022 11:52:05 +0900 Subject: [PATCH 0311/1276] Upgrade to Resharper 2022.1.0-EAP10 --- .config/dotnet-tools.json | 4 ++-- .github/workflows/ci.yml | 2 +- InspectCode.ps1 | 2 +- InspectCode.sh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 4177c402aa..7b94ee5bdd 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,7 +9,7 @@ ] }, "jetbrains.resharper.globaltools": { - "version": "2020.3.2", + "version": "2022.1.0-eap10", "commands": [ "jb" ] @@ -33,4 +33,4 @@ ] } } -} +} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec3816d541..95864a79f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -138,7 +138,7 @@ jobs: # run: dotnet format --dry-run --check - name: InspectCode - run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --output=$(pwd)/inspectcodereport.xml --cachesDir=$(pwd)/inspectcode --verbosity=WARN + run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --verbosity=WARN - name: NVika run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors diff --git a/InspectCode.ps1 b/InspectCode.ps1 index 8316f48ff3..e311bd8755 100644 --- a/InspectCode.ps1 +++ b/InspectCode.ps1 @@ -5,7 +5,7 @@ dotnet tool restore # - cmd: dotnet format --dry-run --check dotnet CodeFileSanity -dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN +dotnet jb inspectcode "osu.Desktop.slnf" --no-build --output="inspectcodereport.xml" --verbosity=WARN dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors exit $LASTEXITCODE diff --git a/InspectCode.sh b/InspectCode.sh index cf2bc18175..1985b14e1e 100755 --- a/InspectCode.sh +++ b/InspectCode.sh @@ -2,5 +2,5 @@ dotnet tool restore dotnet CodeFileSanity -dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN +dotnet jb inspectcode "osu.Desktop.slnf" --no-build --output="inspectcodereport.xml" --verbosity=WARN dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors From fb409db14872e55d6fb6d443fca0b54b09e1bb2d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 14 Apr 2022 12:31:18 +0900 Subject: [PATCH 0312/1276] Add caches-home --- InspectCode.ps1 | 2 +- InspectCode.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/InspectCode.ps1 b/InspectCode.ps1 index e311bd8755..df0d73ea43 100644 --- a/InspectCode.ps1 +++ b/InspectCode.ps1 @@ -5,7 +5,7 @@ dotnet tool restore # - cmd: dotnet format --dry-run --check dotnet CodeFileSanity -dotnet jb inspectcode "osu.Desktop.slnf" --no-build --output="inspectcodereport.xml" --verbosity=WARN +dotnet jb inspectcode "osu.Desktop.slnf" --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors exit $LASTEXITCODE diff --git a/InspectCode.sh b/InspectCode.sh index 1985b14e1e..65b55e0da0 100755 --- a/InspectCode.sh +++ b/InspectCode.sh @@ -2,5 +2,5 @@ dotnet tool restore dotnet CodeFileSanity -dotnet jb inspectcode "osu.Desktop.slnf" --no-build --output="inspectcodereport.xml" --verbosity=WARN +dotnet jb inspectcode "osu.Desktop.slnf" --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors From 494955aff1eaf1d321183590bec88ce5cd7d034f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 14 Apr 2022 13:11:11 +0900 Subject: [PATCH 0313/1276] Resolve inspection issues --- .../EmptyFreeformDifficultyCalculator.cs | 3 ++- .../PippidonDifficultyCalculator.cs | 3 ++- .../EmptyScrollingDifficultyCalculator.cs | 3 ++- .../PippidonDifficultyCalculator.cs | 3 ++- osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs | 4 +--- osu.Game.Tests/Online/TestAPIModJsonSerialization.cs | 5 +++-- .../Visual/Gameplay/TestSceneGameplaySamplePlayback.cs | 2 +- .../Screens/Ladder/Components/ProgressionPath.cs | 6 +----- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 2 +- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 4 ++-- osu.sln.DotSettings | 1 + 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs index fae3784f5e..312d3d5e9a 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -25,6 +26,6 @@ namespace osu.Game.Rulesets.EmptyFreeform protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => Array.Empty(); } } diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index ca64636076..f6addab279 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -25,6 +26,6 @@ namespace osu.Game.Rulesets.Pippidon protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => Array.Empty(); } } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs index 63a8b48b3c..a4dc1762d5 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -25,6 +26,6 @@ namespace osu.Game.Rulesets.EmptyScrolling protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => Array.Empty(); } } diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index ca64636076..f6addab279 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -25,6 +26,6 @@ namespace osu.Game.Rulesets.Pippidon protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => Array.Empty(); } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs index 63854e7ead..5c7e3954e8 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneFlyingHits.cs @@ -28,9 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests // flying hits all land in one common scrolling container (and stay there for rewind purposes), // so we need to manually get the latest one. - flyingHit = this.ChildrenOfType() - .OrderByDescending(h => h.HitObject.StartTime) - .FirstOrDefault(); + flyingHit = this.ChildrenOfType().MaxBy(h => h.HitObject.StartTime); }); AddAssert("hit type is correct", () => flyingHit.HitObject.Type == hitType); diff --git a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index e98ea98bb2..0622514783 100644 --- a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -33,9 +33,10 @@ namespace osu.Game.Tests.Online var converted = deserialized?.ToMod(new TestRuleset()); + Assert.NotNull(converted); Assert.That(converted, Is.TypeOf(typeof(UnknownMod))); - Assert.That(converted?.Type, Is.EqualTo(ModType.System)); - Assert.That(converted?.Acronym, Is.EqualTo("WNG??")); + Assert.That(converted.Type, Is.EqualTo(ModType.System)); + Assert.That(converted.Acronym, Is.EqualTo("WNG??")); } [Test] diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs index 6b3fc304e0..ae2bc60fc6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("get variables", () => { sampleDisabler = Player; - slider = Player.ChildrenOfType().OrderBy(s => s.HitObject.StartTime).FirstOrDefault(); + slider = Player.ChildrenOfType().MinBy(s => s.HitObject.StartTime); samples = slider?.ChildrenOfType().ToArray(); return slider != null; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs index cb73985b11..960c4f41cc 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs @@ -36,11 +36,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components bool progressionToRight = q2.TopLeft.X > q1.TopLeft.X; if (!progressionToRight) - { - var temp = q2; - q2 = q1; - q1 = temp; - } + (q2, q1) = (q1, q2); var c1 = getCenteredVector(q1.TopRight, q1.BottomRight) + new Vector2(padding, 0); var c2 = getCenteredVector(q2.TopLeft, q2.BottomLeft) - new Vector2(padding, 0); diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 7d59c95396..591e4cf73e 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -253,7 +253,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores noScoresPlaceholder.Hide(); - if (Beatmap.Value == null || Beatmap.Value.OnlineID <= 0 || (Beatmap.Value?.BeatmapSet as IBeatmapSetOnlineInfo)?.Status <= BeatmapOnlineStatus.Pending) + if (Beatmap.Value == null || Beatmap.Value.OnlineID <= 0 || (Beatmap.Value.BeatmapSet as IBeatmapSetOnlineInfo)?.Status <= BeatmapOnlineStatus.Pending) { Scores = null; Hide(); diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 795dddfaf5..0d759bacf0 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Play new Sprite { RelativeSizeAxes = Axes.Both, - Texture = beatmap?.Background, + Texture = beatmap.Background, Origin = Anchor.Centre, Anchor = Anchor.Centre, FillMode = FillMode.Fill, @@ -126,7 +126,7 @@ namespace osu.Game.Screens.Play { new OsuSpriteText { - Text = beatmap?.BeatmapInfo?.DifficultyName, + Text = beatmap.BeatmapInfo.DifficultyName, Font = OsuFont.GetFont(size: 26, italics: true), Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 2ff0f4d30b..68cf8138e2 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -118,6 +118,7 @@ WARNING WARNING WARNING + HINT WARNING WARNING WARNING From 7160a48bab4fc69166e30c19954d0585528097f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 12:33:59 +0900 Subject: [PATCH 0314/1276] Adjust hold to confirm animation curve to better show intention --- osu.Game/Graphics/Containers/HoldToConfirmContainer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index b2f08eee0a..1b802a0a14 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -70,7 +70,10 @@ namespace osu.Game.Graphics.Containers confirming = false; Fired = false; - this.TransformBindableTo(Progress, 0, fadeout_delay, Easing.Out); + this + .TransformBindableTo(Progress, Progress.Value) + .Delay(200) + .TransformBindableTo(Progress, 0, fadeout_delay, Easing.InSine); } } } From 46cce94a75ee41c1b13501a5e18f8c7c3f58dfd1 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 14 Apr 2022 14:34:43 +0900 Subject: [PATCH 0315/1276] Add inspectcode CI cache --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95864a79f5..5e7f9628fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,6 +120,12 @@ jobs: - name: Restore Packages run: dotnet restore + - name: Restore inspectcode cache + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/inspectcode + key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }} + - name: CodeFileSanity run: | # TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround. @@ -138,7 +144,7 @@ jobs: # run: dotnet format --dry-run --check - name: InspectCode - run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --verbosity=WARN + run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN - name: NVika run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors From f48a9ba90a611424d57e55832316d0177e645dac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 16:19:32 +0900 Subject: [PATCH 0316/1276] Add test coverage of nested screen stacks not handling dialog dismissal properly --- .../Navigation/TestScenePerformFromScreen.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 1ebceed15d..5c5d6679aa 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -10,6 +10,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Screens; +using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; @@ -171,6 +172,46 @@ namespace osu.Game.Tests.Visual.Navigation } } + [TestCase(true)] + [TestCase(false)] + public void TestPerformBlockedByDialogSubScreen(bool confirm) + { + TestScreenWithNestedStack screenWithNestedStack = null; + + PushAndConfirm(() => screenWithNestedStack = new TestScreenWithNestedStack()); + + AddAssert("wait for nested screen", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); + + AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true)); + + AddUntilStep("wait for dialog", () => screenWithNestedStack.Blocker.ExitAttempts == 1); + + AddWaitStep("wait a bit", 10); + + AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + + AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); + AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); + + AddAssert("did not perform", () => !actionPerformed); + + AddAssert("only one exit attempt", () => screenWithNestedStack.Blocker.ExitAttempts == 1); + + if (confirm) + { + AddStep("accept dialog", () => InputManager.Key(Key.Number1)); + AddAssert("nested screen changed", () => screenWithNestedStack.SubScreenStack.CurrentScreen != screenWithNestedStack.Blocker); + AddUntilStep("did perform", () => actionPerformed); + } + else + { + AddStep("cancel dialog", () => InputManager.Key(Key.Number2)); + AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); + AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); + AddAssert("did not perform", () => !actionPerformed); + } + } + private void importAndWaitForSongSelect() { AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); @@ -200,5 +241,30 @@ namespace osu.Game.Tests.Visual.Navigation return base.OnExiting(next); } } + + public class TestScreenWithNestedStack : OsuScreen, IHasSubScreenStack + { + public DialogBlockingScreen Blocker { get; private set; } + + public ScreenStack SubScreenStack { get; } = new ScreenStack(); + + public TestScreenWithNestedStack() + { + AddInternal(SubScreenStack); + + SubScreenStack.Push(Blocker = new DialogBlockingScreen()); + } + + public override bool OnExiting(IScreen next) + { + if (SubScreenStack.CurrentScreen != null) + { + SubScreenStack.CurrentScreen.Exit(); + return true; + } + + return base.OnExiting(next); + } + } } } From f9d9e6aa61ee45c7d605441ea77a6892a1f07ce8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 15:58:03 +0900 Subject: [PATCH 0317/1276] Ensure popup dialogs are hidden before running any associated actions --- osu.Game/Overlays/Dialog/PopupDialog.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index a70a7f26cc..f5e6aed41b 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -87,10 +87,13 @@ namespace osu.Game.Overlays.Dialog { if (actionInvoked) return; + // Hide the dialog before running the action. + // This is important as the code which is performed may check for a dialog being present (ie. `OsuGame.PerformFromScreen`) + // and we don't want it to see the already dismissed dialog. + Hide(); + actionInvoked = true; action?.Invoke(); - - Hide(); }; } } From 2bc4bb9e206dd4c7ec337ddd44c8690ffe8ad787 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 16:03:34 +0900 Subject: [PATCH 0318/1276] Consider `SubScreenStack`s when allowing for exit confirmation sequence in `PerformFromMenuRunner` --- osu.Game/PerformFromMenuRunner.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 6f979b8dc8..7f0db96e6d 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -125,6 +125,18 @@ namespace osu.Game /// Whether a dialog blocked interaction. private bool checkForDialog(IScreen current) { + // An exit process may traverse multiple levels. + // When checking for dismissing dialogs, let's also consider sub screens. + while (current is IHasSubScreenStack currentWithSubScreenStack) + { + var nestedCurrent = currentWithSubScreenStack.SubScreenStack.CurrentScreen; + + if (nestedCurrent == null) + break; + + current = nestedCurrent; + } + var currentDialog = dialogOverlay.CurrentDialog; if (lastEncounteredDialog != null) From 8b901fe60b2b4de540008ac3efab127213e02bdb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 16:22:40 +0900 Subject: [PATCH 0319/1276] Fix potential null reference when running recursive `findValidTarget` --- osu.Game/PerformFromMenuRunner.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 7f0db96e6d..528470768c 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -97,11 +97,14 @@ namespace osu.Game // if this has a sub stack, recursively check the screens within it. if (current is IHasSubScreenStack currentSubScreen) { - if (findValidTarget(currentSubScreen.SubScreenStack.CurrentScreen)) + var nestedCurrent = currentSubScreen.SubScreenStack.CurrentScreen; + + if (nestedCurrent != null) { // should be correct in theory, but currently untested/unused in existing implementations. - current.MakeCurrent(); - return true; + // note that calling findValidTarget actually performs the final operation. + if (findValidTarget(nestedCurrent)) + return true; } } From 58d5cf4560d35f5100681d1dd069e8424f31d16d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 16:37:59 +0900 Subject: [PATCH 0320/1276] Remove unused using statement --- osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 5c5d6679aa..f045e3a3e8 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -10,7 +10,6 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Screens; -using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; From e010dfb1500c445ba8016ae9eb3eee2305d113ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 17:32:47 +0900 Subject: [PATCH 0321/1276] Handle virtual track string to avoid throwing later in lookup --- osu.Game/Beatmaps/WorkingBeatmapCache.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmapCache.cs b/osu.Game/Beatmaps/WorkingBeatmapCache.cs index 7d28208157..bc810ee35e 100644 --- a/osu.Game/Beatmaps/WorkingBeatmapCache.cs +++ b/osu.Game/Beatmaps/WorkingBeatmapCache.cs @@ -26,6 +26,11 @@ namespace osu.Game.Beatmaps { private readonly WeakList workingCache = new WeakList(); + /// + /// Beatmap files may specify this filename to denote that they don't have an audio track. + /// + private const string virtual_track_filename = @"virtual"; + /// /// A default representation of a WorkingBeatmap to use when no beatmap is available. /// @@ -40,7 +45,8 @@ namespace osu.Game.Beatmaps [CanBeNull] private readonly GameHost host; - public WorkingBeatmapCache(ITrackStore trackStore, AudioManager audioManager, IResourceStore resources, IResourceStore files, WorkingBeatmap defaultBeatmap = null, GameHost host = null) + public WorkingBeatmapCache(ITrackStore trackStore, AudioManager audioManager, IResourceStore resources, IResourceStore files, WorkingBeatmap defaultBeatmap = null, + GameHost host = null) { DefaultBeatmap = defaultBeatmap; @@ -157,6 +163,9 @@ namespace osu.Game.Beatmaps if (string.IsNullOrEmpty(Metadata?.AudioFile)) return null; + if (Metadata.AudioFile == virtual_track_filename) + return null; + try { return resources.Tracks.Get(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile)); @@ -173,6 +182,9 @@ namespace osu.Game.Beatmaps if (string.IsNullOrEmpty(Metadata?.AudioFile)) return null; + if (Metadata.AudioFile == virtual_track_filename) + return null; + try { var trackData = GetStream(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile)); From 1fdf742d3888fca4cf89e26f441ab4ef0b8e3a7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 17:51:50 +0900 Subject: [PATCH 0322/1276] Fix potentially incorrect button being pressed during `PopupDialog` hide due to flag set too late --- osu.Game/Overlays/Dialog/PopupDialog.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index f5e6aed41b..6159ab59f0 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -87,12 +87,13 @@ namespace osu.Game.Overlays.Dialog { if (actionInvoked) return; + actionInvoked = true; + // Hide the dialog before running the action. // This is important as the code which is performed may check for a dialog being present (ie. `OsuGame.PerformFromScreen`) // and we don't want it to see the already dismissed dialog. Hide(); - actionInvoked = true; action?.Invoke(); }; } From c8db7cf2a89dec23eec10b0ec217ef28802dd47a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 18:09:23 +0900 Subject: [PATCH 0323/1276] Reset audio offset before each test run in `TestSceneMasterGameplayClockContainer` --- .../Gameplay/TestSceneMasterGameplayClockContainer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs index 75be7b0362..5c04ac88a7 100644 --- a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs @@ -26,6 +26,12 @@ namespace osu.Game.Tests.Gameplay Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage)); } + [SetUpSteps] + public void SetUpSteps() + { + AddStep("reset audio offset", () => localConfig.SetValue(OsuSetting.AudioOffset, 0.0)); + } + [Test] public void TestStartThenElapsedTime() { From 2abdbe53e7e90a7dfb86541ee475a93fe4ec6665 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 14 Apr 2022 18:55:12 +0900 Subject: [PATCH 0324/1276] Cleanup whitespace --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index c5a5c14070..ea43fb1546 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -251,7 +251,6 @@ namespace osu.Game.Screens.Play } private class HardwareCorrectionOffsetClock : FramedOffsetClock - { private readonly BindableDouble pauseRateAdjust; @@ -297,7 +296,6 @@ namespace osu.Game.Screens.Play } private class MasterGameplayClock : GameplayClock - { public readonly List> MutableNonGameplayAdjustments = new List>(); public override IEnumerable> NonGameplayAdjustments => MutableNonGameplayAdjustments; From 392ce7390e179d58ae582afcb3bd8e393f844236 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 18:58:16 +0900 Subject: [PATCH 0325/1276] Add test scene covering `ScalingContainer` delayed animations --- .../TestSceneScalingContainer.cs | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneScalingContainer.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScalingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScalingContainer.cs new file mode 100644 index 0000000000..5d554719a5 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScalingContainer.cs @@ -0,0 +1,114 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shapes; +using osu.Game.Configuration; +using osu.Game.Graphics.Containers; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.UserInterface +{ + [TestFixture] + public class TestSceneScalingContainer : OsuTestScene + { + private OsuConfigManager osuConfigManager { get; set; } + + private ScalingContainer scaling1; + private ScalingContainer scaling2; + private Box scaleTarget; + + [BackgroundDependencyLoader] + private void load() + { + osuConfigManager = new OsuConfigManager(LocalStorage); + + Dependencies.CacheAs(osuConfigManager); + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + scaling1 = new ScalingContainer(ScalingMode.Everything) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.8f), + Children = new Drawable[] + { + scaling2 = new ScalingContainer(ScalingMode.Everything) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.8f), + Children = new Drawable[] + { + new Box + { + Colour = Color4.Purple, + RelativeSizeAxes = Axes.Both, + }, + scaleTarget = new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.White, + Size = new Vector2(100), + }, + } + } + } + } + } + }, + }; + } + + [Test] + public void TestScaling() + { + AddStep("adjust scale", () => osuConfigManager.SetValue(OsuSetting.UIScale, 2f)); + + checkForCorrectness(); + + AddStep("adjust scale", () => osuConfigManager.SetValue(OsuSetting.UIScale, 0.5f)); + + checkForCorrectness(); + } + + private void checkForCorrectness() + { + Quad? scaling1LastQuad = null; + Quad? scaling2LastQuad = null; + Quad? scalingTargetLastQuad = null; + + AddUntilStep("ensure dimensions don't change", () => + { + if (scaling1LastQuad.HasValue && scaling2LastQuad.HasValue) + { + // check inter-frame changes to make sure they match expectations. + Assert.That(scaling1.ScreenSpaceDrawQuad.AlmostEquals(scaling1LastQuad.Value), Is.True); + Assert.That(scaling2.ScreenSpaceDrawQuad.AlmostEquals(scaling2LastQuad.Value), Is.True); + } + + scaling1LastQuad = scaling1.ScreenSpaceDrawQuad; + scaling2LastQuad = scaling2.ScreenSpaceDrawQuad; + + // wait for scaling to stop. + bool scalingFinished = scalingTargetLastQuad.HasValue && scaleTarget.ScreenSpaceDrawQuad.AlmostEquals(scalingTargetLastQuad.Value); + + scalingTargetLastQuad = scaleTarget.ScreenSpaceDrawQuad; + + return scalingFinished; + }); + } + } +} From bb4322a4c488a429580bbd191e4e5415de96679f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 18:58:20 +0900 Subject: [PATCH 0326/1276] Fix UI scale being applied unevenly, causing elements to move around relative to each other --- .../Graphics/Containers/ScalingContainer.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index 58d18e1b21..12e570d62d 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -21,6 +21,8 @@ namespace osu.Game.Graphics.Containers /// public class ScalingContainer : Container { + private const float duration = 500; + private Bindable sizeX; private Bindable sizeY; private Bindable posX; @@ -82,6 +84,8 @@ namespace osu.Game.Graphics.Containers private readonly bool applyUIScale; private Bindable uiScale; + private readonly Bindable currentScale = new Bindable(1); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; public ScalingDrawSizePreservingFillContainer(bool applyUIScale) @@ -101,8 +105,15 @@ namespace osu.Game.Graphics.Containers private void scaleChanged(ValueChangedEvent args) { - this.ScaleTo(new Vector2(args.NewValue), 500, Easing.Out); - this.ResizeTo(new Vector2(1 / args.NewValue), 500, Easing.Out); + this.TransformBindableTo(currentScale, args.NewValue, duration, Easing.OutQuart); + } + + protected override void Update() + { + Scale = new Vector2(currentScale.Value); + Size = new Vector2(1 / currentScale.Value); + + base.Update(); } } @@ -140,8 +151,6 @@ namespace osu.Game.Graphics.Containers private void updateSize() { - const float duration = 500; - if (targetMode == ScalingMode.Everything) { // the top level scaling container manages the background to be displayed while scaling. From 7582c2a5e8f5cab06caeda0d3831026f6e644b6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 19:13:54 +0900 Subject: [PATCH 0327/1276] Remove unnecessary bindable --- osu.Game/Graphics/Containers/ScalingContainer.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index 12e570d62d..ca8b6f388f 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -84,7 +84,7 @@ namespace osu.Game.Graphics.Containers private readonly bool applyUIScale; private Bindable uiScale; - private readonly Bindable currentScale = new Bindable(1); + private float currentScale = 1; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; @@ -99,19 +99,14 @@ namespace osu.Game.Graphics.Containers if (applyUIScale) { uiScale = osuConfig.GetBindable(OsuSetting.UIScale); - uiScale.BindValueChanged(scaleChanged, true); + uiScale.BindValueChanged(args => this.TransformTo(nameof(currentScale), args.NewValue, duration, Easing.OutQuart), true); } } - private void scaleChanged(ValueChangedEvent args) - { - this.TransformBindableTo(currentScale, args.NewValue, duration, Easing.OutQuart); - } - protected override void Update() { - Scale = new Vector2(currentScale.Value); - Size = new Vector2(1 / currentScale.Value); + Scale = new Vector2(currentScale); + Size = new Vector2(1 / currentScale); base.Update(); } From 566a20a623a5d82f73d4e11f6c2ea9836ce14566 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 14 Apr 2022 13:27:06 +0300 Subject: [PATCH 0328/1276] Add keyword "delay" to hold-to-confirm activation time setting --- .../Overlays/Settings/Sections/UserInterface/GeneralSettings.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 59894cbcae..6e1558f7d7 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -39,6 +39,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { LabelText = UserInterfaceStrings.HoldToConfirmActivationTime, Current = config.GetBindable(OsuSetting.UIHoldActivationDelay), + Keywords = new[] { @"delay" }, KeyboardStep = 50 }, }; From 74bfb25c78d58ad5cc8dacb1f04428f703f6a586 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 19:30:02 +0900 Subject: [PATCH 0329/1276] Commit new rider metadata changes --- .idea/.idea.osu.Desktop/.idea/misc.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.idea/.idea.osu.Desktop/.idea/misc.xml b/.idea/.idea.osu.Desktop/.idea/misc.xml index 1d8c84d0af..4e1d56f4dd 100644 --- a/.idea/.idea.osu.Desktop/.idea/misc.xml +++ b/.idea/.idea.osu.Desktop/.idea/misc.xml @@ -1,5 +1,10 @@ + + + From 59bb6b8f7cbbde0078a51c88e51245bdc9ea88c3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 14 Apr 2022 13:43:09 +0300 Subject: [PATCH 0330/1276] Add background dim effect to inactive settings sections --- osu.Game/Overlays/Settings/SettingsSection.cs | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 2539c32806..50a82b3d96 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -13,6 +13,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.Settings { @@ -23,6 +24,8 @@ namespace osu.Game.Overlays.Settings private IBindable selectedSection; + private Box dim; + private OsuSpriteText header; public abstract Drawable CreateIcon(); @@ -78,25 +81,40 @@ namespace osu.Game.Overlays.Settings }, new Container { - Padding = new MarginPadding - { - Top = 28, - Bottom = 40, - }, + Padding = new MarginPadding { Top = border_size }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Children = new Drawable[] { - header = new OsuSpriteText + dim = new Box { - Font = OsuFont.TorusAlternate.With(size: header_size), - Text = Header, - Margin = new MarginPadding - { - Horizontal = SettingsPanel.CONTENT_MARGINS - } + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0f, }, - FlowContent + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Top = 24, + Bottom = 40, + }, + Children = new Drawable[] + { + header = new OsuSpriteText + { + Font = OsuFont.TorusAlternate.With(size: header_size), + Text = Header, + Margin = new MarginPadding + { + Horizontal = SettingsPanel.CONTENT_MARGINS + } + }, + FlowContent + } + } } }, }); @@ -135,15 +153,16 @@ namespace osu.Game.Overlays.Settings private void updateContentFade() { float contentFade = 1; - float headerFade = 1; + float dimFade = 0; if (!isCurrentSection) { - contentFade = 0.25f; - headerFade = IsHovered ? 0.5f : 0.25f; + contentFade = IsHovered ? 0.5f : 0.25f; + dimFade = IsHovered ? 0.125f : 0.25f; } - header.FadeTo(headerFade, 500, Easing.OutQuint); + dim.FadeTo(dimFade, 500, Easing.OutQuint); + header.FadeTo(contentFade, 500, Easing.OutQuint); FlowContent.FadeTo(contentFade, 500, Easing.OutQuint); } } From 50a478ea5f3a5538eca5d9254644f9286680809c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 20:05:13 +0900 Subject: [PATCH 0331/1276] Add failing test coverage of searching too fast not hiding filtered settings --- .../Visual/Settings/TestSceneSettingsPanel.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index f9c9b2a68b..0de1cedd0c 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -4,10 +4,12 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; +using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; using osu.Game.Overlays.Settings.Sections.Input; using osuTK.Input; @@ -34,6 +36,26 @@ namespace osu.Game.Tests.Visual.Settings }); } + [Test] + public void TestQuickFiltering() + { + AddStep("set filter", () => + { + settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"; + }); + + AddUntilStep("wait for items to load", () => settings.SectionsContainer.ChildrenOfType().Any()); + + AddAssert("ensure all items match filter", () => settings.SectionsContainer + .ChildrenOfType().Where(f => f.IsPresent) + .All(section => + section.ChildrenOfType().Where(f => f.IsPresent) + .OfType() + .Where(f => !(f is IHasFilterableChildren)) + .All(f => f.FilterTerms.Any(t => t.Contains("scaling"))) + )); + } + [Test] public void ToggleVisibility() { From 296420ae08b41305e179dfcd0cf0f935971e8ed1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 20:06:00 +0900 Subject: [PATCH 0332/1276] Fix settings textbox focus operation running too late --- osu.Game/Overlays/SettingsPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index b11b6fde27..ea386e26c6 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -163,6 +163,7 @@ namespace osu.Game.Overlays Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); + searchTextBox.TakeFocus(); searchTextBox.HoldFocus = true; } @@ -213,7 +214,6 @@ namespace osu.Game.Overlays loading.Hide(); searchTextBox.Current.BindValueChanged(term => SectionsContainer.SearchTerm = term.NewValue, true); - searchTextBox.TakeFocus(); loadSidebarButtons(); }); From 9c7af9898213d92e214e6e8683a52c8265292f29 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 20:06:15 +0900 Subject: [PATCH 0333/1276] Fix alpha cross-talk causing settings to potentially not filter when they should --- osu.Game/Overlays/Settings/SettingsItem.cs | 7 +++---- osu.Game/Overlays/Settings/SettingsSection.cs | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 098090bf78..2d139a01e9 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -100,10 +100,9 @@ namespace osu.Game.Overlays.Settings public IEnumerable Keywords { get; set; } - public bool MatchingFilter - { - set => Alpha = value ? 1 : 0; - } + public override bool IsPresent => MatchingFilter; + + public bool MatchingFilter { get; set; } public bool FilteringActive { get; set; } diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 2539c32806..a39aacf6b8 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -21,6 +21,8 @@ namespace osu.Game.Overlays.Settings protected FillFlowContainer FlowContent; protected override Container Content => FlowContent; + public override bool IsPresent => MatchingFilter; + private IBindable selectedSection; private OsuSpriteText header; @@ -36,10 +38,7 @@ namespace osu.Game.Overlays.Settings private const int header_size = 24; private const int border_size = 4; - public bool MatchingFilter - { - set => this.FadeTo(value ? 1 : 0); - } + public bool MatchingFilter { get; set; } public bool FilteringActive { get; set; } From 21a5a2fd697322acdbc535797b9b6727a7023e97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 20:36:48 +0900 Subject: [PATCH 0334/1276] Add failing test coverage of incorrect current section after quick setting search --- osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 0de1cedd0c..66242b19cd 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -54,6 +54,8 @@ namespace osu.Game.Tests.Visual.Settings .Where(f => !(f is IHasFilterableChildren)) .All(f => f.FilterTerms.Any(t => t.Contains("scaling"))) )); + + AddAssert("ensure section is current", () => settings.CurrentSection.Value != null); } [Test] From 316c0845ec8f56c92d36ea702038b3ec545e24e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 20:36:11 +0900 Subject: [PATCH 0335/1276] Fix current section not being correct after early search --- osu.Game/Overlays/SettingsPanel.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index ea386e26c6..8c1129508a 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -287,7 +287,10 @@ namespace osu.Game.Overlays set { SearchContainer.SearchTerm = value; - InvalidateScrollPosition(); + + // Schedule required as search term takes a frame to update. + // Without this sections may not be in the correct state to ascertain CurrentSection. + Schedule(InvalidateScrollPosition); } } From f19208a2451d26a7973f2361b3d7e4250964ffed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 21:04:52 +0900 Subject: [PATCH 0336/1276] Move dim layer to top and only apply dimming at one location --- osu.Game/Overlays/Settings/SettingsSection.cs | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 50a82b3d96..49f0d5c153 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -13,7 +13,6 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.Settings { @@ -26,8 +25,6 @@ namespace osu.Game.Overlays.Settings private Box dim; - private OsuSpriteText header; - public abstract Drawable CreateIcon(); public abstract LocalisableString Header { get; } @@ -86,12 +83,6 @@ namespace osu.Game.Overlays.Settings AutoSizeAxes = Axes.Y, Children = new Drawable[] { - dim = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0f, - }, new Container { RelativeSizeAxes = Axes.X, @@ -103,7 +94,7 @@ namespace osu.Game.Overlays.Settings }, Children = new Drawable[] { - header = new OsuSpriteText + new OsuSpriteText { Font = OsuFont.TorusAlternate.With(size: header_size), Text = Header, @@ -114,7 +105,13 @@ namespace osu.Game.Overlays.Settings }, FlowContent } - } + }, + dim = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background5, + Alpha = 0f, + }, } }, }); @@ -152,18 +149,12 @@ namespace osu.Game.Overlays.Settings private void updateContentFade() { - float contentFade = 1; float dimFade = 0; if (!isCurrentSection) - { - contentFade = IsHovered ? 0.5f : 0.25f; - dimFade = IsHovered ? 0.125f : 0.25f; - } + dimFade = IsHovered ? 0.5f : 0.8f; - dim.FadeTo(dimFade, 500, Easing.OutQuint); - header.FadeTo(contentFade, 500, Easing.OutQuint); - FlowContent.FadeTo(contentFade, 500, Easing.OutQuint); + dim.FadeTo(dimFade, 300, Easing.OutQuint); } } } From 9240148c8e817869fcddc59f093f2eeb916c13dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 21:16:54 +0900 Subject: [PATCH 0337/1276] Avoid initial fade of dim layer --- osu.Game/Overlays/Settings/SettingsSection.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 49f0d5c153..6645d20dbe 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -25,6 +25,8 @@ namespace osu.Game.Overlays.Settings private Box dim; + private const float inactive_alpha = 0.8f; + public abstract Drawable CreateIcon(); public abstract LocalisableString Header { get; } @@ -110,7 +112,7 @@ namespace osu.Game.Overlays.Settings { RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background5, - Alpha = 0f, + Alpha = inactive_alpha, }, } }, @@ -152,7 +154,9 @@ namespace osu.Game.Overlays.Settings float dimFade = 0; if (!isCurrentSection) - dimFade = IsHovered ? 0.5f : 0.8f; + { + dimFade = IsHovered ? 0.5f : inactive_alpha; + } dim.FadeTo(dimFade, 300, Easing.OutQuint); } From b0889590458b16f1c43acb3ec5966af00e72a196 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 21:17:06 +0900 Subject: [PATCH 0338/1276] Reduce settings fade in duration to higher alpha/colour cross-talk --- osu.Game/Overlays/SettingsPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index b11b6fde27..d5b36713f3 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -197,7 +197,7 @@ namespace osu.Game.Overlays ContentContainer.Margin = new MarginPadding { Left = Sidebar?.DrawWidth ?? 0 }; } - private const double fade_in_duration = 1000; + private const double fade_in_duration = 500; private void loadSections() { From c57a918bbba4d3cb329b0c5a130e825d06571f7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 23:02:00 +0900 Subject: [PATCH 0339/1276] Mark settings elements as matching filter by default to handle cases where no filtering is provided --- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- osu.Game/Overlays/Settings/SettingsSection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 2d139a01e9..329917d50a 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -102,7 +102,7 @@ namespace osu.Game.Overlays.Settings public override bool IsPresent => MatchingFilter; - public bool MatchingFilter { get; set; } + public bool MatchingFilter { get; set; } = true; public bool FilteringActive { get; set; } diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index a39aacf6b8..cd012f10c7 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Settings private const int header_size = 24; private const int border_size = 4; - public bool MatchingFilter { get; set; } + public bool MatchingFilter { get; set; } = true; public bool FilteringActive { get; set; } From a0ac316d6e9eed23f86bfe334d34fab6129d834e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Apr 2022 01:41:55 +0900 Subject: [PATCH 0340/1276] Fix test matching conditions --- osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 66242b19cd..71c001887a 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -49,7 +49,8 @@ namespace osu.Game.Tests.Visual.Settings AddAssert("ensure all items match filter", () => settings.SectionsContainer .ChildrenOfType().Where(f => f.IsPresent) .All(section => - section.ChildrenOfType().Where(f => f.IsPresent) + section.Children.Where(f => f.IsPresent) + .OfType() .OfType() .Where(f => !(f is IHasFilterableChildren)) .All(f => f.FilterTerms.Any(t => t.Contains("scaling"))) From 6081c29ae17fe8e202d0bc2ebc9b050a6c7a70b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Apr 2022 10:18:29 +0900 Subject: [PATCH 0341/1276] Remove ununsed using statement --- osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 71c001887a..6e2ba88ea0 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -4,7 +4,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; From 423fae52f7269ea0ce638afb3b0128e3dc41583a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Apr 2022 10:19:36 +0900 Subject: [PATCH 0342/1276] Add back `base.IsPresent` call --- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- osu.Game/Overlays/Settings/SettingsSection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 329917d50a..1c5668479f 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -100,7 +100,7 @@ namespace osu.Game.Overlays.Settings public IEnumerable Keywords { get; set; } - public override bool IsPresent => MatchingFilter; + public override bool IsPresent => base.IsPresent && MatchingFilter; public bool MatchingFilter { get; set; } = true; diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index cd012f10c7..ace618aa46 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings protected FillFlowContainer FlowContent; protected override Container Content => FlowContent; - public override bool IsPresent => MatchingFilter; + public override bool IsPresent => base.IsPresent && MatchingFilter; private IBindable selectedSection; From a7b04a8c19848c29443378b9c993c81842909c2f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Apr 2022 19:59:38 +0900 Subject: [PATCH 0343/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 331c4db01f..77deec031b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 308ec7e7d6..cf2245aa3a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 311c9ba345..b77dff9f41 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 8e6c0158568462e42d50a23307400de8f2c7cb76 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 15 Apr 2022 20:31:59 +0900 Subject: [PATCH 0344/1276] Use a fallback sample when a MainMenuButton doesn't provide a click sample --- osu.Game/Screens/Menu/MainMenuButton.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index 88bea43b23..c07ada9419 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -185,8 +185,7 @@ namespace osu.Game.Screens.Menu private void load(AudioManager audio) { sampleHover = audio.Samples.Get(@"Menu/button-hover"); - if (!string.IsNullOrEmpty(sampleName)) - sampleClick = audio.Samples.Get($@"Menu/{sampleName}"); + sampleClick = audio.Samples.Get(!string.IsNullOrEmpty(sampleName) ? $@"Menu/{sampleName}" : @"UI/button-select"); } protected override bool OnMouseDown(MouseDownEvent e) From ef5ed262ca83d09bf6cda980fbbbd9201b3eea9e Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 15 Apr 2022 20:32:41 +0900 Subject: [PATCH 0345/1276] Add pitch randomisation to the OsuLogo hover sample --- osu.Game/Screens/Menu/OsuLogo.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index c82efe2d32..1d3aef0653 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -283,9 +283,15 @@ namespace osu.Game.Screens.Menu this.Delay(early_activation).Schedule(() => { if (beatIndex % timingPoint.TimeSignature.Numerator == 0) - sampleDownbeat.Play(); + { + sampleDownbeat?.Play(); + } else - sampleBeat.Play(); + { + var channel = sampleBeat.GetChannel(); + channel.Frequency.Value = 0.95 + RNG.NextDouble(0.1); + channel.Play(); + } }); } From ceb5b4f9b404b949571c0654858eda885777ea89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Apr 2022 20:46:49 +0900 Subject: [PATCH 0346/1276] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 77deec031b..ec638f7736 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index cf2245aa3a..be4bee7c9d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index b77dff9f41..0a0cab91a4 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From f1dd319fd99393c2a9ae2a6741f5621c6d47c77d Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Fri, 15 Apr 2022 15:38:47 +0100 Subject: [PATCH 0347/1276] Implement `ChannelList` for new chat design Reference design: https://www.figma.com/file/f8b2dHp9LJCMOqYP4mdrPZ/Client%2FChat?node-id=1%3A297 Adds new component `ChannelList` which makes up the sidebar channel selector of the new chat design. Contains two separate fill flows for public and private `ChannelItem` child components. Exposed bindable `SelectorActive` to indicate current state of the "Add more channels" button. Requires `Bindable` from parent component. Renames and updates the `TestSceneChannelListItem` to `TestSceneChannelList` to make use of new component and having both tests seemed redundant. --- .../Visual/Online/TestSceneChannelList.cs | 187 ++++++++++++++++++ .../Visual/Online/TestSceneChannelListItem.cs | 163 --------------- .../Overlays/Chat/ChannelList/ChannelList.cs | 174 ++++++++++++++++ .../Chat/ChannelList/ChannelListSelector.cs | 84 ++++++++ 4 files changed, 445 insertions(+), 163 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneChannelList.cs delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneChannelListItem.cs create mode 100644 osu.Game/Overlays/Chat/ChannelList/ChannelList.cs create mode 100644 osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs new file mode 100644 index 0000000000..d6a1e71f0d --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -0,0 +1,187 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; +using osu.Game.Overlays; +using osu.Game.Overlays.Chat.ChannelList; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneChannelList : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + + [Cached] + private readonly Bindable selected = new Bindable(); + + private OsuSpriteText selectorText; + private OsuSpriteText selectedText; + private OsuSpriteText leaveText; + private ChannelList channelList; + + [SetUp] + public void SetUp() + { + Schedule(() => + { + Child = new GridContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Height = 0.7f, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 20), + new Dimension(GridSizeMode.Absolute, 20), + new Dimension(GridSizeMode.Absolute, 20), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + selectorText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + }, + new Drawable[] + { + selectedText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + }, + new Drawable[] + { + leaveText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + }, + new Drawable[] + { + channelList = new ChannelList + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Y, + Width = 190, + }, + }, + }, + }; + + channelList.OnRequestSelect += channel => + { + channelList.SelectorActive.Value = false; + selected.Value = channel; + }; + + channelList.OnRequestLeave += channel => + { + leaveText.Text = $"OnRequestLeave: {channel.Name}"; + leaveText.FadeOutFromOne(1000, Easing.InQuint); + selected.Value = null; + channelList.RemoveChannel(channel); + }; + + channelList.SelectorActive.BindValueChanged(change => + { + selectorText.Text = $"Channel Selector Active: {change.NewValue}"; + }, true); + + selected.BindValueChanged(change => + { + selectedText.Text = $"Selected Channel: {change.NewValue?.Name ?? "[null]"}"; + }, true); + }); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Add Public Channels", () => + { + for (int i = 0; i < 10; i++) + channelList.AddChannel(createRandomPublicChannel()); + }); + + AddStep("Add Private Channels", () => + { + for (int i = 0; i < 10; i++) + channelList.AddChannel(createRandomPrivateChannel()); + }); + } + + [Test] + public void TestVisual() + { + AddStep("Unread Selected", () => + { + if (selected.Value != null) + channelList.GetItem(selected.Value).Unread.Value = true; + }); + + AddStep("Read Selected", () => + { + if (selected.Value != null) + channelList.GetItem(selected.Value).Unread.Value = false; + }); + + AddStep("Add Mention Selected", () => + { + if (selected.Value != null) + channelList.GetItem(selected.Value).Mentions.Value++; + }); + + AddStep("Add 98 Mentions Selected", () => + { + if (selected.Value != null) + channelList.GetItem(selected.Value).Mentions.Value += 98; + }); + + AddStep("Clear Mentions Selected", () => + { + if (selected.Value != null) + channelList.GetItem(selected.Value).Mentions.Value = 0; + }); + } + + private Channel createRandomPublicChannel() + { + int id = RNG.Next(0, 10000); + return new Channel + { + Name = $"#channel-{id}", + Type = ChannelType.Public, + Id = id, + }; + } + + private Channel createRandomPrivateChannel() + { + int id = RNG.Next(0, 10000); + return new Channel(new APIUser + { + Id = id, + Username = $"test user {id}", + }); + } + } +} diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelListItem.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelListItem.cs deleted file mode 100644 index af419c8b91..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelListItem.cs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -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.Graphics.Sprites; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Online.Chat; -using osu.Game.Overlays; -using osu.Game.Overlays.Chat.ChannelList; -using osuTK; - -namespace osu.Game.Tests.Visual.Online -{ - [TestFixture] - public class TestSceneChannelListItem : OsuTestScene - { - [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); - - [Cached] - private readonly Bindable selected = new Bindable(); - - private static readonly List channels = new List - { - createPublicChannel("#public-channel"), - createPublicChannel("#public-channel-long-name"), - createPrivateChannel("test user", 2), - createPrivateChannel("test user long name", 3), - }; - - private readonly Dictionary channelMap = new Dictionary(); - - private FillFlowContainer flow; - private OsuSpriteText selectedText; - private OsuSpriteText leaveText; - - [SetUp] - public void SetUp() - { - Schedule(() => - { - foreach (var item in channelMap.Values) - item.Expire(); - - channelMap.Clear(); - - Child = new FillFlowContainer - { - Direction = FillDirection.Vertical, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(10), - Children = new Drawable[] - { - selectedText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - leaveText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Height = 16, - AlwaysPresent = true, - }, - new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Y, - Width = 190, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background6, - }, - flow = new FillFlowContainer - { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - }, - }, - }, - }; - - selected.BindValueChanged(change => - { - selectedText.Text = $"Selected Channel: {change.NewValue?.Name ?? "[null]"}"; - }, true); - - foreach (var channel in channels) - { - var item = new ChannelListItem(channel); - flow.Add(item); - channelMap.Add(channel, item); - item.OnRequestSelect += c => selected.Value = c; - item.OnRequestLeave += leaveChannel; - } - }); - } - - [Test] - public void TestVisual() - { - AddStep("Select second item", () => selected.Value = channels.Skip(1).First()); - - AddStep("Unread Selected", () => - { - if (selected.Value != null) - channelMap[selected.Value].Unread.Value = true; - }); - - AddStep("Read Selected", () => - { - if (selected.Value != null) - channelMap[selected.Value].Unread.Value = false; - }); - - AddStep("Add Mention Selected", () => - { - if (selected.Value != null) - channelMap[selected.Value].Mentions.Value++; - }); - - AddStep("Add 98 Mentions Selected", () => - { - if (selected.Value != null) - channelMap[selected.Value].Mentions.Value += 98; - }); - - AddStep("Clear Mentions Selected", () => - { - if (selected.Value != null) - channelMap[selected.Value].Mentions.Value = 0; - }); - } - - private void leaveChannel(Channel channel) - { - leaveText.Text = $"OnRequestLeave: {channel.Name}"; - leaveText.FadeOutFromOne(1000, Easing.InQuint); - } - - private static Channel createPublicChannel(string name) => - new Channel { Name = name, Type = ChannelType.Public, Id = 1234 }; - - private static Channel createPrivateChannel(string username, int id) - => new Channel(new APIUser { Id = id, Username = username }); - } -} diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs new file mode 100644 index 0000000000..4ffb6be451 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -0,0 +1,174 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using osuTK; + +namespace osu.Game.Overlays.Chat.ChannelList +{ + public class ChannelList : Container + { + public Action? OnRequestSelect; + public Action? OnRequestLeave; + + public readonly BindableBool SelectorActive = new BindableBool(); + + private readonly Dictionary channelMap = new Dictionary(); + + private ChannelListItemFlow publicChannelFlow = null!; + private ChannelListItemFlow privateChannelFlow = null!; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background6, + }, + new ChannelListScrollContainer + { + Padding = new MarginPadding { Vertical = 7 }, + RelativeSizeAxes = Axes.Both, + ScrollbarAnchor = Anchor.TopLeft, + ScrollDistance = 35f, + Child = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + publicChannelFlow = new ChannelListItemFlow("CHANNELS"), + new ChannelListSelector + { + Margin = new MarginPadding { Bottom = 10 }, + SelectorActive = { BindTarget = SelectorActive }, + }, + privateChannelFlow = new ChannelListItemFlow("DIRECT MESSAGES"), + }, + }, + }, + }; + } + + public void AddChannel(Channel channel) + { + if (channelMap.ContainsKey(channel)) + return; + + ChannelListItem item = new ChannelListItem(channel); + item.OnRequestSelect += channel => this.OnRequestSelect?.Invoke(channel); + item.OnRequestLeave += channel => this.OnRequestLeave?.Invoke(channel); + + ChannelListItemFlow flow = getFlowForChannel(channel); + channelMap.Add(channel, item); + flow.Add(item); + } + + public void RemoveChannel(Channel channel) + { + if (!channelMap.ContainsKey(channel)) + return; + + ChannelListItem item = channelMap[channel]; + ChannelListItemFlow flow = getFlowForChannel(channel); + + channelMap.Remove(channel); + flow.Remove(item); + item.Expire(); + } + + public ChannelListItem GetItem(Channel channel) + { + if (!channelMap.ContainsKey(channel)) + throw new ArgumentOutOfRangeException(); + + return channelMap[channel]; + } + + private ChannelListItemFlow getFlowForChannel(Channel channel) + { + switch (channel.Type) + { + case ChannelType.Public: + return publicChannelFlow; + + case ChannelType.PM: + return privateChannelFlow; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + private class ChannelListItemFlow : FillFlowContainer + { + public ChannelListItemFlow(string label) + { + Direction = FillDirection.Vertical; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Add(new OsuSpriteText + { + Text = label, + Margin = new MarginPadding { Left = 18, Bottom = 5 }, + Font = OsuFont.Torus.With(size: 12), + }); + } + } + + private class ChannelListScrollContainer : OsuScrollContainer + { + protected override bool OnHover(HoverEvent e) + { + ScrollbarVisible = true; + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + ScrollbarVisible = false; + base.OnHoverLost(e); + } + + protected override ScrollbarContainer CreateScrollbar(Direction direction) + => new ChannelListScrollBar(direction); + + protected class ChannelListScrollBar : OsuScrollbar + { + private const float BAR_SIZE = 4; + private const float BAR_MARGIN = 7; + + public ChannelListScrollBar(Direction scrollDir) : base(scrollDir) + { + Size = new Vector2(BAR_SIZE); + Margin = new MarginPadding { Left = BAR_MARGIN }; + CornerRadius = 2; + } + + public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None) + { + Vector2 size = new Vector2(BAR_SIZE, val); + this.ResizeTo(size, duration, easing); + } + } + } + } +} diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs new file mode 100644 index 0000000000..5bc9a01598 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs @@ -0,0 +1,84 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.Chat.ChannelList +{ + public class ChannelListSelector : OsuClickableContainer + { + public readonly BindableBool SelectorActive = new BindableBool(); + + private Box hoverBox = null!; + private Box selectBox = null!; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Height = 30; + RelativeSizeAxes = Axes.X; + + Children = new Drawable[] + { + hoverBox = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background3, + Alpha = 0f, + }, + selectBox = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4, + Alpha = 0f, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = 18, Right = 10 }, + Child = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "Add More Channels", + Font = OsuFont.Torus.With(size: 17, weight: FontWeight.SemiBold), + Colour = colourProvider.Light3, + Margin = new MarginPadding { Bottom = 2 }, + RelativeSizeAxes = Axes.X, + Truncate = true, + }, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + SelectorActive.BindValueChanged(selected => selectBox.FadeTo(selected.NewValue ? 1 : 0)); + Action = () => SelectorActive.Value = true; + } + + protected override bool OnHover(HoverEvent e) + { + hoverBox.FadeIn(300, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + hoverBox.FadeOut(200, Easing.OutQuint); + base.OnHoverLost(e); + } + } +} From a0a2f8118e520003c23993bb2074d5664803758f Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Fri, 15 Apr 2022 22:05:20 +0100 Subject: [PATCH 0348/1276] Code quality fixes --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 4ffb6be451..9fcd675dc5 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -73,8 +73,8 @@ namespace osu.Game.Overlays.Chat.ChannelList return; ChannelListItem item = new ChannelListItem(channel); - item.OnRequestSelect += channel => this.OnRequestSelect?.Invoke(channel); - item.OnRequestLeave += channel => this.OnRequestLeave?.Invoke(channel); + item.OnRequestSelect += chan => OnRequestSelect?.Invoke(chan); + item.OnRequestLeave += chan => OnRequestLeave?.Invoke(chan); ChannelListItemFlow flow = getFlowForChannel(channel); channelMap.Add(channel, item); @@ -153,19 +153,19 @@ namespace osu.Game.Overlays.Chat.ChannelList protected class ChannelListScrollBar : OsuScrollbar { - private const float BAR_SIZE = 4; - private const float BAR_MARGIN = 7; + private const float bar_size = 4; + private const float bar_margin = 7; public ChannelListScrollBar(Direction scrollDir) : base(scrollDir) { - Size = new Vector2(BAR_SIZE); - Margin = new MarginPadding { Left = BAR_MARGIN }; + Size = new Vector2(bar_size); + Margin = new MarginPadding { Left = bar_margin }; CornerRadius = 2; } public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None) { - Vector2 size = new Vector2(BAR_SIZE, val); + Vector2 size = new Vector2(bar_size, val); this.ResizeTo(size, duration, easing); } } From b6631fb9f97e5add211cc4187fef2f03210b71fd Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Fri, 15 Apr 2022 22:22:20 +0100 Subject: [PATCH 0349/1276] Add newline for base class call --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 9fcd675dc5..94c05770ab 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -156,7 +156,8 @@ namespace osu.Game.Overlays.Chat.ChannelList private const float bar_size = 4; private const float bar_margin = 7; - public ChannelListScrollBar(Direction scrollDir) : base(scrollDir) + public ChannelListScrollBar(Direction scrollDir) + : base(scrollDir) { Size = new Vector2(bar_size); Margin = new MarginPadding { Left = bar_margin }; From 4a0c5a07813fb94107e40acc9295f3f315abdf08 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 16 Apr 2022 19:16:06 +0100 Subject: [PATCH 0350/1276] Remove hover override for `ChannelListScrollContainer` --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 94c05770ab..ef2f132501 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -10,7 +10,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -136,18 +135,6 @@ namespace osu.Game.Overlays.Chat.ChannelList private class ChannelListScrollContainer : OsuScrollContainer { - protected override bool OnHover(HoverEvent e) - { - ScrollbarVisible = true; - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - ScrollbarVisible = false; - base.OnHoverLost(e); - } - protected override ScrollbarContainer CreateScrollbar(Direction direction) => new ChannelListScrollBar(direction); From 36aea9009e754ebd18872d259a137e2df8f52a88 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 16 Apr 2022 19:19:18 +0100 Subject: [PATCH 0351/1276] Move `ChannelList` scrollbar to the right --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index ef2f132501..759242a779 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Chat.ChannelList { Padding = new MarginPadding { Vertical = 7 }, RelativeSizeAxes = Axes.Both, - ScrollbarAnchor = Anchor.TopLeft, + ScrollbarAnchor = Anchor.TopRight, ScrollDistance = 35f, Child = new FillFlowContainer { @@ -147,7 +147,7 @@ namespace osu.Game.Overlays.Chat.ChannelList : base(scrollDir) { Size = new Vector2(bar_size); - Margin = new MarginPadding { Left = bar_margin }; + Margin = new MarginPadding { Horizontal = bar_margin }; CornerRadius = 2; } From 2f41cddb00124179b13ae5c69a633f00805dbec9 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 16 Apr 2022 19:28:24 +0100 Subject: [PATCH 0352/1276] Remove redundant `Expire` call --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 759242a779..250e0cdf21 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -90,7 +90,6 @@ namespace osu.Game.Overlays.Chat.ChannelList channelMap.Remove(channel); flow.Remove(item); - item.Expire(); } public ChannelListItem GetItem(Channel channel) From 47c8b8010f1580ee9c7e583d0340f85f4c791e1d Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 16 Apr 2022 19:30:03 +0100 Subject: [PATCH 0353/1276] Use semibold font weight for `ChannelListItemFlow` label --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 250e0cdf21..5c2a1ad89f 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -127,7 +127,7 @@ namespace osu.Game.Overlays.Chat.ChannelList { Text = label, Margin = new MarginPadding { Left = 18, Bottom = 5 }, - Font = OsuFont.Torus.With(size: 12), + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), }); } } From 399d8eac36d8171a806b004843dc9ab747d0e500 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Apr 2022 14:44:28 +0900 Subject: [PATCH 0354/1276] Update `LocalisationAnalyser` with dotnet 6 compatibility changes --- .config/dotnet-tools.json | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 7b94ee5bdd..5a3eadf607 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -27,10 +27,10 @@ ] }, "ppy.localisationanalyser.tools": { - "version": "2022.320.0", + "version": "2022.417.0", "commands": [ "localisation" ] } } -} \ No newline at end of file +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index be4bee7c9d..94614bea76 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From a3cc07ff3f6adcf9c42355772eb0e489cfd35931 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Apr 2022 15:08:15 +0900 Subject: [PATCH 0355/1276] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index ec638f7736..a8952df329 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 94614bea76..3246255815 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 0a0cab91a4..668d23f82e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From f37444938fb20b951fa7de9bdcba609173681967 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 02:05:09 +0900 Subject: [PATCH 0356/1276] Update squirrel to latest version Includes disk space checks and better erroring. --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 3ca6411812..a4f309c6ac 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -24,7 +24,7 @@ - + From 67c44db8d5aad22d8eb381574fb7421495840c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 27 Mar 2022 22:55:52 +0200 Subject: [PATCH 0357/1276] Add extension points required for replacing old mod overlay --- osu.Game/Overlays/Mods/ModColumn.cs | 7 +- osu.Game/Overlays/Mods/ModSelectScreen.cs | 89 +++++++++++++++++------ 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index f84ae4ac8a..abd243e1f7 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -54,6 +54,8 @@ namespace osu.Game.Overlays.Mods public Bindable> SelectedMods = new Bindable>(Array.Empty()); + protected virtual ModPanel CreateModPanel(Mod mod) => new ModPanel(mod); + private readonly Key[]? toggleKeys; private readonly Bindable>> availableMods = new Bindable>>(); @@ -258,10 +260,7 @@ namespace osu.Game.Overlays.Mods cancellationTokenSource?.Cancel(); - var panels = newMods.Select(mod => new ModPanel(mod) - { - Shear = new Vector2(-ModPanel.SHEAR_X, 0) - }); + var panels = newMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ModPanel.SHEAR_X, 0))); Task? loadTask; diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 62080ec1b5..d174474076 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics; @@ -31,16 +33,43 @@ namespace osu.Game.Overlays.Mods protected override bool StartHidden => true; + private Func isValidMod = m => true; + + public Func IsValidMod + { + get => isValidMod; + set + { + isValidMod = value ?? throw new ArgumentNullException(nameof(value)); + + if (IsLoaded) + updateAvailableMods(); + } + } + + /// + /// Whether configurable s can be configured by the local user. + /// + protected virtual bool AllowConfiguration => true; + + /// + /// Whether the total score multiplier calculated from the current selected set of mods should be shown. + /// + protected virtual bool ShowTotalMultiplier => true; + + protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys); + private readonly BindableBool customisationVisible = new BindableBool(); - private DifficultyMultiplierDisplay multiplierDisplay; - private ModSettingsArea modSettingsArea; - private FillFlowContainer columnFlow; - private GridContainer grid; - private Container mainContent; + private DifficultyMultiplierDisplay? multiplierDisplay; + private ModSettingsArea modSettingsArea = null!; + private Container aboveColumnsContainer = null!; + private FillFlowContainer columnFlow = null!; + private GridContainer grid = null!; + private Container mainContent = null!; - private PopupScreenTitle header; - private Container footer; + private PopupScreenTitle header = null!; + private Container footer = null!; [BackgroundDependencyLoader] private void load() @@ -82,7 +111,7 @@ namespace osu.Game.Overlays.Mods }, new Drawable[] { - new Container + aboveColumnsContainer = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -94,11 +123,6 @@ namespace osu.Game.Overlays.Mods { Horizontal = 100, Vertical = 10 - }, - Child = multiplierDisplay = new DifficultyMultiplierDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre } } }, @@ -126,11 +150,11 @@ namespace osu.Game.Overlays.Mods Margin = new MarginPadding { Right = 70 }, Children = new[] { - new ModColumn(ModType.DifficultyReduction, false, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), - new ModColumn(ModType.DifficultyIncrease, false, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), - new ModColumn(ModType.Automation, false, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), - new ModColumn(ModType.Conversion, false), - new ModColumn(ModType.Fun, false) + CreateModColumn(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), + CreateModColumn(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), + CreateModColumn(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), + CreateModColumn(ModType.Conversion), + CreateModColumn(ModType.Fun) } } } @@ -182,6 +206,15 @@ namespace osu.Game.Overlays.Mods } }; + if (ShowTotalMultiplier) + { + aboveColumnsContainer.Add(multiplierDisplay = new DifficultyMultiplierDisplay + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight + }); + } + columnFlow.Shear = new Vector2(ModPanel.SHEAR_X, 0); } @@ -204,10 +237,15 @@ namespace osu.Game.Overlays.Mods } customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); + + updateAvailableMods(); } private void updateMultiplier() { + if (multiplierDisplay == null) + return; + double multiplier = 1.0; foreach (var mod in SelectedMods.Value) @@ -216,8 +254,17 @@ namespace osu.Game.Overlays.Mods multiplierDisplay.Current.Value = multiplier; } + private void updateAvailableMods() + { + foreach (var column in columnFlow) + column.Filter = isValidMod; + } + private void updateCustomisation(ValueChangedEvent> valueChangedEvent) { + if (!AllowConfiguration) + return; + bool anyCustomisableMod = false; bool anyModWithRequiredCustomisationAdded = false; @@ -292,7 +339,7 @@ namespace osu.Game.Overlays.Mods header.MoveToY(0, fade_in_duration, Easing.OutQuint); footer.MoveToY(0, fade_in_duration, Easing.OutQuint); - multiplierDisplay + multiplierDisplay? .Delay(fade_in_duration * 0.65f) .FadeIn(fade_in_duration / 2, Easing.OutQuint) .ScaleTo(1, fade_in_duration, Easing.OutElastic); @@ -313,7 +360,7 @@ namespace osu.Game.Overlays.Mods base.PopOut(); this.FadeOut(fade_out_duration, Easing.OutQuint); - multiplierDisplay + multiplierDisplay? .FadeOut(fade_out_duration / 2, Easing.OutQuint) .ScaleTo(0.75f, fade_out_duration, Easing.OutQuint); @@ -368,7 +415,7 @@ namespace osu.Game.Overlays.Mods { public BindableBool HandleMouse { get; } = new BindableBool(); - public Action OnClicked { get; set; } + public Action? OnClicked { get; set; } protected override bool Handle(UIEvent e) { From 7eebc2012465f58c95b6ff3bc2bb452f30d1a314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 27 Mar 2022 23:07:52 +0200 Subject: [PATCH 0358/1276] Add replacements for mod overlays used by game --- osu.Game/Overlays/Mods/UserModSelectScreen.cs | 24 +++++++++++++++ .../Screens/OnlinePlay/FreeModSelectScreen.cs | 29 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 osu.Game/Overlays/Mods/UserModSelectScreen.cs create mode 100644 osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs diff --git a/osu.Game/Overlays/Mods/UserModSelectScreen.cs b/osu.Game/Overlays/Mods/UserModSelectScreen.cs new file mode 100644 index 0000000000..81943da514 --- /dev/null +++ b/osu.Game/Overlays/Mods/UserModSelectScreen.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Game.Rulesets.Mods; +using osuTK.Input; + +namespace osu.Game.Overlays.Mods +{ + public class UserModSelectScreen : ModSelectScreen + { + protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys); + + private class UserModColumn : ModColumn + { + public UserModColumn(ModType modType, bool allowBulkSelection, [CanBeNull] Key[] toggleKeys = null) + : base(modType, allowBulkSelection, toggleKeys) + { + } + + protected override ModPanel CreateModPanel(Mod mod) => new IncompatibilityDisplayingModPanel(mod); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs new file mode 100644 index 0000000000..438b334e0b --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; +using osuTK.Input; + +namespace osu.Game.Screens.OnlinePlay +{ + public class FreeModSelectScreen : ModSelectScreen + { + protected override bool AllowConfiguration => false; + protected override bool ShowTotalMultiplier => false; + + public new Func IsValidMod + { + get => base.IsValidMod; + set => base.IsValidMod = m => m.HasImplementation && m.UserPlayable && value.Invoke(m); + } + + public FreeModSelectScreen() + { + IsValidMod = _ => true; + } + + protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys); + } +} From 20c17b8c98448f7ad1a93b53ebb259b8961765db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 19:42:52 +0200 Subject: [PATCH 0359/1276] Make base mod select screen abstract --- .../Visual/UserInterface/TestSceneModSelectScreen.cs | 4 ++-- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 34d9ddcc4e..4a738cb29d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Resolved] private RulesetStore rulesetStore { get; set; } - private ModSelectScreen modSelectScreen; + private UserModSelectScreen modSelectScreen; [SetUpSteps] public void SetUpSteps() @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void createScreen() { - AddStep("create screen", () => Child = modSelectScreen = new ModSelectScreen + AddStep("create screen", () => Child = modSelectScreen = new UserModSelectScreen { RelativeSizeAxes = Axes.Both, State = { Value = Visibility.Visible }, diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index d174474076..ad111cd5b9 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -23,7 +23,7 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public class ModSelectScreen : OsuFocusedOverlayContainer + public abstract class ModSelectScreen : OsuFocusedOverlayContainer { [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); From 9942b0a9467cf51fb6284d157fbc67ea78107af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 19:53:05 +0200 Subject: [PATCH 0360/1276] Add test scene for free mod select screen --- .../TestSceneFreeModSelectScreen.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs new file mode 100644 index 0000000000..974cd710f7 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Game.Screens.OnlinePlay; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneFreeModSelectScreen : MultiplayerTestScene + { + [Test] + public void TestFreeModSelect() + { + FreeModSelectScreen freeModSelectScreen = null; + + AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen + { + State = { Value = Visibility.Visible } + }); + AddToggleStep("toggle visibility", visible => + { + if (freeModSelectScreen != null) + freeModSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden; + }); + } + } +} From 4b6d42c7e890bcd797535fc6c5203e4fc70112aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 16:54:30 +0200 Subject: [PATCH 0361/1276] Add assertion covering free mod selection mod validity filter --- .../Visual/Multiplayer/TestSceneFreeModSelectScreen.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs index 974cd710f7..0db05e3a6a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -1,8 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Overlays.Mods; using osu.Game.Screens.OnlinePlay; namespace osu.Game.Tests.Visual.Multiplayer @@ -18,6 +21,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { State = { Value = Visibility.Visible } }); + + AddAssert("all visible mods are playable", + () => this.ChildrenOfType() + .Where(panel => panel.IsPresent) + .All(panel => panel.Mod.HasImplementation && panel.Mod.UserPlayable)); + AddToggleStep("toggle visibility", visible => { if (freeModSelectScreen != null) From ffb5c1e86c71003c6d6f8cda6e173948936e64f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 16:58:02 +0200 Subject: [PATCH 0362/1276] Tweak colours on incompatibility displaying mod panel --- osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs index 4b6759c209..6c6c36eec9 100644 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs @@ -46,8 +46,8 @@ namespace osu.Game.Overlays.Mods if (incompatible.Value) { - Colour4 backgroundColour = ColourProvider.Background5; - Colour4 textBackgroundColour = ColourProvider.Background4; + Colour4 backgroundColour = ColourProvider.Background6; + Colour4 textBackgroundColour = ColourProvider.Background5; Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(backgroundColour, textBackgroundColour), TRANSITION_DURATION, Easing.OutQuint); Background.FadeColour(backgroundColour, TRANSITION_DURATION, Easing.OutQuint); From 8af865a1c5766d850743f2654b48a50a63aa3316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 21:20:16 +0200 Subject: [PATCH 0363/1276] Fix incompatibility panel using reference equality --- osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs index 6c6c36eec9..38781455fa 100644 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs @@ -37,7 +37,9 @@ namespace osu.Game.Overlays.Mods private void updateIncompatibility() { - incompatible.Value = selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(Mod) && !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod)); + incompatible.Value = selectedMods.Value.Count > 0 + && selectedMods.Value.All(selected => selected.GetType() != Mod.GetType()) + && !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod)); } protected override void UpdateState() From 881df7663d8ee8fab93759c57651e78c4ddfcab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 22:30:02 +0200 Subject: [PATCH 0364/1276] Fix filter not taking effect if applied before panel load completion --- osu.Game/Overlays/Mods/ModPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index 312171cf74..c9aaa1c6c0 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -159,7 +159,7 @@ namespace osu.Game.Overlays.Mods playStateChangeSamples(); UpdateState(); }); - Filtered.BindValueChanged(_ => updateFilterState()); + Filtered.BindValueChanged(_ => updateFilterState(), true); UpdateState(); FinishTransforms(true); From 0d5ce336f49cfe4a475c6b587fc9c1e3dc52f3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 23:26:25 +0200 Subject: [PATCH 0365/1276] Hide mod customisation toggle if customisation not permitted --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index ad111cd5b9..237af2a71d 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -180,14 +180,6 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.BottomCentre, Colour = colourProvider.Background5 }, - new ShearedToggleButton(200) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = 14, Left = 70 }, - Text = "Mod Customisation", - Active = { BindTarget = customisationVisible } - } } }, new ClickToReturnContainer @@ -206,6 +198,18 @@ namespace osu.Game.Overlays.Mods } }; + if (AllowConfiguration) + { + footer.Add(new ShearedToggleButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Vertical = 14, Left = 70 }, + Text = "Mod Customisation", + Active = { BindTarget = customisationVisible } + }); + } + if (ShowTotalMultiplier) { aboveColumnsContainer.Add(multiplierDisplay = new DifficultyMultiplierDisplay From 338d94626e5cb5f86ce924c76e0cea3b16970a1f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 06:42:06 +0300 Subject: [PATCH 0366/1276] Expose underlying skin of `EditorBeatmapSkin` --- osu.Game/Screens/Edit/EditorBeatmapSkin.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs index 429df85904..243a994a7f 100644 --- a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs +++ b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs @@ -21,13 +21,18 @@ namespace osu.Game.Screens.Edit { public event Action BeatmapSkinChanged; + /// + /// The underlying beatmap skin. + /// + protected internal ISkin Skin => skin; + + private readonly Skin skin; + /// /// The combo colours of this skin. /// If empty, the default combo colours will be used. /// - public readonly BindableList ComboColours; - - private readonly Skin skin; + public BindableList ComboColours { get; } public EditorBeatmapSkin(Skin skin) { From 80cce7c3cb1d5c840ec6bbb599a3bcdf3baf1e17 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 06:46:06 +0300 Subject: [PATCH 0367/1276] Add failing test case --- .../Visual/Editing/TestSceneComposeScreen.cs | 63 +++++++++++++------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs index d100fba8d6..30c8539d85 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs @@ -1,44 +1,71 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose; +using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneComposeScreen : EditorClockTestScene { - [Cached(typeof(EditorBeatmap))] - [Cached(typeof(IBeatSnapProvider))] - private readonly EditorBeatmap editorBeatmap = - new EditorBeatmap(new OsuBeatmap - { - BeatmapInfo = - { - Ruleset = new OsuRuleset().RulesetInfo - } - }); + private EditorBeatmap editorBeatmap; [Cached] private EditorClipboard clipboard = new EditorClipboard(); - protected override void LoadComplete() + [SetUpSteps] + public void SetUpSteps() { - base.LoadComplete(); - - Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); - - Child = new ComposeScreen + AddStep("setup compose screen", () => { - State = { Value = Visibility.Visible }, - }; + var beatmap = new OsuBeatmap + { + BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } + }; + + editorBeatmap = new EditorBeatmap(beatmap, new LegacyBeatmapSkin(beatmap.BeatmapInfo, null)); + + Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + + Child = new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(EditorBeatmap), editorBeatmap), + (typeof(IBeatSnapProvider), editorBeatmap), + }, + Child = new ComposeScreen { State = { Value = Visibility.Visible } }, + }; + }); + + AddUntilStep("wait for composer", () => this.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + } + + /// + /// Ensures that the skin of the edited beatmap is properly wrapped in a . + /// + [Test] + public void TestLegacyBeatmapSkinHasTransformer() + { + AddAssert("legacy beatmap skin has transformer", () => + { + var sources = this.ChildrenOfType().First().AllSources; + return sources.OfType().Count(t => t.Skin == editorBeatmap.BeatmapSkin.AsNonNull().Skin) == 1; + }); } } } From 2cb217e06c04d63de4bc4151c7631b9abc448062 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 06:59:44 +0300 Subject: [PATCH 0368/1276] Fix editor legacy beatmap skins not receiving transformer --- osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs b/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs index decfa879a8..694d0253e0 100644 --- a/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs +++ b/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.Edit private readonly EditorBeatmapSkin? beatmapSkin; public EditorSkinProvidingContainer(EditorBeatmap editorBeatmap) - : base(editorBeatmap.PlayableBeatmap.BeatmapInfo.Ruleset.CreateInstance(), editorBeatmap.PlayableBeatmap, editorBeatmap.BeatmapSkin) + : base(editorBeatmap.PlayableBeatmap.BeatmapInfo.Ruleset.CreateInstance(), editorBeatmap.PlayableBeatmap, editorBeatmap.BeatmapSkin?.Skin) { beatmapSkin = editorBeatmap.BeatmapSkin; } From 90093c1d9d3fab7757218cdb021f08c64cdd1fe7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 13:32:37 +0900 Subject: [PATCH 0369/1276] Combine `private` skin variable into exposed one --- osu.Game/Screens/Edit/EditorBeatmapSkin.cs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs index 243a994a7f..f650ffa5a3 100644 --- a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs +++ b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs @@ -24,9 +24,7 @@ namespace osu.Game.Screens.Edit /// /// The underlying beatmap skin. /// - protected internal ISkin Skin => skin; - - private readonly Skin skin; + protected internal readonly Skin Skin; /// /// The combo colours of this skin. @@ -36,11 +34,11 @@ namespace osu.Game.Screens.Edit public EditorBeatmapSkin(Skin skin) { - this.skin = skin; + Skin = skin; ComboColours = new BindableList(); - if (skin.Configuration.ComboColours != null) - ComboColours.AddRange(skin.Configuration.ComboColours.Select(c => (Colour4)c)); + if (Skin.Configuration.ComboColours != null) + ComboColours.AddRange(Skin.Configuration.ComboColours.Select(c => (Colour4)c)); ComboColours.BindCollectionChanged((_, __) => updateColours()); } @@ -48,16 +46,16 @@ namespace osu.Game.Screens.Edit private void updateColours() { - skin.Configuration.CustomComboColours = ComboColours.Select(c => (Color4)c).ToList(); + Skin.Configuration.CustomComboColours = ComboColours.Select(c => (Color4)c).ToList(); invokeSkinChanged(); } #region Delegated ISkin implementation - public Drawable GetDrawableComponent(ISkinComponent component) => skin.GetDrawableComponent(component); - public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => skin.GetTexture(componentName, wrapModeS, wrapModeT); - public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo); - public IBindable GetConfig(TLookup lookup) => skin.GetConfig(lookup); + public Drawable GetDrawableComponent(ISkinComponent component) => Skin.GetDrawableComponent(component); + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => Skin.GetTexture(componentName, wrapModeS, wrapModeT); + public ISample GetSample(ISampleInfo sampleInfo) => Skin.GetSample(sampleInfo); + public IBindable GetConfig(TLookup lookup) => Skin.GetConfig(lookup); #endregion } From fcb48c69cf8e30b4ae9a42bd7f2484b8ecdba175 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 07:57:13 +0300 Subject: [PATCH 0370/1276] Calculate sample playback position using screen-space drawable rectangles --- .../Objects/Drawables/DrawableOsuHitObject.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index a1c8879105..ff87f441d6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -6,10 +6,10 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Graphics.Containers; -using osu.Game.Rulesets.Osu.UI; using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -24,7 +24,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // Must be set to update IsHovered as it's used in relax mod to detect osu hit objects. public override bool HandlePositionalInput => true; - protected override float SamplePlaybackPosition => HitObject.X / OsuPlayfield.BASE_SIZE.X; + protected override float SamplePlaybackPosition => (ScreenSpaceDrawQuad.Centre.X - parentScreenSpaceRectangle.X) / parentScreenSpaceRectangle.Width; + + private RectangleF parentScreenSpaceRectangle => ((DrawableOsuHitObject)ParentHitObject)?.parentScreenSpaceRectangle ?? Parent.ScreenSpaceDrawQuad.AABBFloat; /// /// Whether this can be hit, given a time value. From 3a00931e3898fc4d7e8fc30500e2d3526378d699 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 09:18:56 +0300 Subject: [PATCH 0371/1276] Update slider sliding samples balance calculation to use screen-space as well --- .../Objects/Drawables/DrawableOsuHitObject.cs | 12 +++++++++--- .../Objects/Drawables/DrawableSlider.cs | 3 +-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index ff87f441d6..fa2d2ba38c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -24,9 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // Must be set to update IsHovered as it's used in relax mod to detect osu hit objects. public override bool HandlePositionalInput => true; - protected override float SamplePlaybackPosition => (ScreenSpaceDrawQuad.Centre.X - parentScreenSpaceRectangle.X) / parentScreenSpaceRectangle.Width; - - private RectangleF parentScreenSpaceRectangle => ((DrawableOsuHitObject)ParentHitObject)?.parentScreenSpaceRectangle ?? Parent.ScreenSpaceDrawQuad.AABBFloat; + protected override float SamplePlaybackPosition => CalculateDrawableRelativePosition(this); /// /// Whether this can be hit, given a time value. @@ -91,6 +89,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult); + private RectangleF parentScreenSpaceRectangle => ((DrawableOsuHitObject)ParentHitObject)?.parentScreenSpaceRectangle ?? Parent.ScreenSpaceDrawQuad.AABBFloat; + + /// + /// Calculates the position of the given relative to the playfield area. + /// + /// The drawable to calculate its relative position. + protected float CalculateDrawableRelativePosition(Drawable drawable) => (drawable.ScreenSpaceDrawQuad.Centre.X - parentScreenSpaceRectangle.X) / parentScreenSpaceRectangle.Width; + protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 1447f131c6..c48ab998ba 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -13,7 +13,6 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning.Default; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osuTK; @@ -208,7 +207,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (Tracking.Value && slidingSample != null) // keep the sliding sample playing at the current tracking position - slidingSample.Balance.Value = CalculateSamplePlaybackBalance(Ball.X / OsuPlayfield.BASE_SIZE.X); + slidingSample.Balance.Value = CalculateSamplePlaybackBalance(CalculateDrawableRelativePosition(Ball)); double completionProgress = Math.Clamp((Time.Current - HitObject.StartTime) / HitObject.Duration, 0, 1); From 2202863e1a1caf323aa341dec967fdaaefd4a237 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 17:49:28 +0900 Subject: [PATCH 0372/1276] Split out `IPerformFromScreenRunner` to allow for easier testing --- osu.Game/OsuGame.cs | 8 +----- .../Dashboard/CurrentlyPlayingDisplay.cs | 5 ++-- .../Sections/DebugSettings/GeneralSettings.cs | 5 ++-- .../StableDirectoryLocationDialog.cs | 7 ++--- osu.Game/Screens/IPerformFromScreenRunner.cs | 26 +++++++++++++++++++ osu.Game/Screens/Menu/MainMenu.cs | 4 +-- .../Skinning/Editor/SkinEditorSceneLibrary.cs | 7 ++--- 7 files changed, 43 insertions(+), 19 deletions(-) create mode 100644 osu.Game/Screens/IPerformFromScreenRunner.cs diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 73121f6e7d..76ccbe0a08 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -63,7 +63,7 @@ namespace osu.Game /// The full osu! experience. Builds on top of to add menus and binding logic /// for initial components that are generally retrieved via DI. /// - public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo + public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo, IPerformFromScreenRunner { /// /// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications). @@ -586,12 +586,6 @@ namespace osu.Game private PerformFromMenuRunner performFromMainMenuTask; - /// - /// Perform an action only after returning to a specific screen as indicated by . - /// Eagerly tries to exit the current screen until it succeeds. - /// - /// The action to perform once we are in the correct state. - /// An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. is used if not specified. public void PerformFromScreen(Action action, IEnumerable validScreens = null) { performFromMainMenuTask?.Cancel(); diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 117de88166..786401b7a8 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -14,6 +14,7 @@ using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Spectator; +using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; using osu.Game.Users; @@ -106,7 +107,7 @@ namespace osu.Game.Overlays.Dashboard public readonly APIUser User; [Resolved(canBeNull: true)] - private OsuGame game { get; set; } + private IPerformFromScreenRunner performer { get; set; } public PlayingUserPanel(APIUser user) { @@ -140,7 +141,7 @@ namespace osu.Game.Overlays.Dashboard Text = "Watch", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Action = () => game?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), + Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), Enabled = { Value = User.Id != api.LocalUser.Value.Id } } } diff --git a/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs index 60540a089e..8833420523 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Localisation; +using osu.Game.Screens; using osu.Game.Screens.Import; namespace osu.Game.Overlays.Settings.Sections.DebugSettings @@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings protected override LocalisableString Header => DebugSettingsStrings.GeneralHeader; [BackgroundDependencyLoader(true)] - private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, OsuGame game) + private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, IPerformFromScreenRunner performer) { Children = new Drawable[] { @@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings Add(new SettingsButton { Text = DebugSettingsStrings.ImportFiles, - Action = () => game?.PerformFromScreen(menu => menu.Push(new FileImportScreen())) + Action = () => performer?.PerformFromScreen(menu => menu.Push(new FileImportScreen())) }); } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 904c9deaae..4e81cd63b8 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -6,13 +6,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Game.Overlays.Dialog; +using osu.Game.Screens; namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class StableDirectoryLocationDialog : PopupDialog { - [Resolved] - private OsuGame game { get; set; } + [Resolved(canBeNull: true)] + private IPerformFromScreenRunner performer { get; set; } public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) { @@ -25,7 +26,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance new PopupDialogOkButton { Text = "Sure! I know where it is located!", - Action = () => Schedule(() => game.PerformFromScreen(screen => screen.Push(new StableDirectorySelectScreen(taskCompletionSource)))) + Action = () => Schedule(() => performer.PerformFromScreen(screen => screen.Push(new StableDirectorySelectScreen(taskCompletionSource)))) }, new PopupDialogCancelButton { diff --git a/osu.Game/Screens/IPerformFromScreenRunner.cs b/osu.Game/Screens/IPerformFromScreenRunner.cs new file mode 100644 index 0000000000..655bebdeb0 --- /dev/null +++ b/osu.Game/Screens/IPerformFromScreenRunner.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Screens; +using osu.Game.Screens.Menu; + +namespace osu.Game.Screens +{ + /// + /// Manages a global screen stack to allow nested components a guarantee of where work is executed. + /// + [Cached] + public interface IPerformFromScreenRunner + { + /// + /// Perform an action only after returning to a specific screen as indicated by . + /// Eagerly tries to exit the current screen until it succeeds. + /// + /// The action to perform once we are in the correct state. + /// An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. is used if not specified. + void PerformFromScreen(Action action, IEnumerable validScreens = null); + } +} diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e2d79b4015..442a536cf9 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -148,14 +148,14 @@ namespace osu.Game.Screens.Menu } [Resolved(canBeNull: true)] - private OsuGame game { get; set; } + private IPerformFromScreenRunner performer { get; set; } private void confirmAndExit() { if (exitConfirmed) return; exitConfirmed = true; - game?.PerformFromScreen(menu => menu.Exit()); + performer?.PerformFromScreen(menu => menu.Exit()); } private void preloadSongSelect() diff --git a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs index 0808cd157f..4507526806 100644 --- a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs +++ b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs @@ -15,6 +15,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Screens; using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osuTK; @@ -28,7 +29,7 @@ namespace osu.Game.Skinning.Editor private const float padding = 10; [Resolved(canBeNull: true)] - private OsuGame game { get; set; } + private IPerformFromScreenRunner performer { get; set; } [Resolved] private IBindable ruleset { get; set; } @@ -75,7 +76,7 @@ namespace osu.Game.Skinning.Editor Text = "Song Select", Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Action = () => game?.PerformFromScreen(screen => + Action = () => performer?.PerformFromScreen(screen => { if (screen is SongSelect) return; @@ -88,7 +89,7 @@ namespace osu.Game.Skinning.Editor Text = "Gameplay", Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Action = () => game?.PerformFromScreen(screen => + Action = () => performer?.PerformFromScreen(screen => { if (screen is Player) return; From e3153132666a911dc2ac47d759d6a7654b128f59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 18:09:14 +0900 Subject: [PATCH 0373/1276] Split out `IDialogOverlay` to allow for easier testing --- .../TestSceneManageCollectionsDialog.cs | 2 +- .../Visual/Navigation/TestSceneOsuGame.cs | 2 +- .../Navigation/TestScenePerformFromScreen.cs | 11 ++++--- .../Navigation/TestSceneScreenNavigation.cs | 13 ++++---- .../TestSceneDeleteLocalScore.cs | 2 +- .../UserInterface/TestSceneDialogOverlay.cs | 2 +- .../Collections/DrawableCollectionListItem.cs | 2 +- osu.Game/Database/LegacyImportManager.cs | 2 +- osu.Game/Online/Chat/ExternalLinkOpener.cs | 2 +- .../Online/Leaderboards/LeaderboardScore.cs | 2 +- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 +- osu.Game/Overlays/DialogOverlay.cs | 2 +- osu.Game/Overlays/IDialogOverlay.cs | 31 +++++++++++++++++++ .../Sections/Maintenance/GeneralSettings.cs | 2 +- .../Maintenance/MigrationSelectScreen.cs | 2 +- osu.Game/PerformFromMenuRunner.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Menu/MainMenu.cs | 2 +- osu.Game/Screens/Menu/StorageErrorDialog.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 4 +-- osu.Game/Tests/Visual/EditorTestScene.cs | 2 +- osu.Game/Tests/Visual/ScreenTestScene.cs | 2 +- 24 files changed, 66 insertions(+), 33 deletions(-) create mode 100644 osu.Game/Overlays/IDialogOverlay.cs diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index e40dd58663..51ca55f37f 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Collections }); Dependencies.Cache(manager); - Dependencies.Cache(dialogOverlay); + Dependencies.CacheAs(dialogOverlay); } [SetUp] diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index b8d1636ea0..921d088fc7 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Navigation typeof(LoginOverlay), typeof(MusicController), typeof(AccountCreationOverlay), - typeof(DialogOverlay), + typeof(IDialogOverlay), typeof(ScreenshotManager) }; diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index f045e3a3e8..c4099b55c7 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; @@ -113,12 +114,12 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("did not perform", () => !actionPerformed); AddAssert("only one exit attempt", () => blocker.ExitAttempts == 1); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); if (confirmed) { AddStep("accept dialog", () => InputManager.Key(Key.Number1)); - AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); + AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); AddUntilStep("did perform", () => actionPerformed); } else @@ -145,7 +146,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == blocker2); AddAssert("did not perform", () => !actionPerformed); @@ -187,7 +188,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); @@ -221,7 +222,7 @@ namespace osu.Game.Tests.Visual.Navigation public class DialogBlockingScreen : OsuScreen { [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private int dialogDisplayCount; diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 394976eb43..0b7242eee0 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; @@ -200,10 +201,10 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("choose clear all scores", () => InputManager.Key(Key.Number4)); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); - AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); AddStep("confirm deletion", () => InputManager.Key(Key.Number1)); - AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); + AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find(score.ID)?.DeletePending == true)); @@ -246,10 +247,10 @@ namespace osu.Game.Tests.Visual.Navigation InputManager.Click(MouseButton.Left); }); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); - AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); AddStep("confirm deletion", () => InputManager.Key(Key.Number1)); - AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); + AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find(score.ID)?.DeletePending == true)); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 72dfc53c01..1350052ae6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.UserInterface private BeatmapInfo beatmapInfo; - [Cached] + [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay; public TestSceneDeleteLocalScore() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index 405461eec8..17234f4022 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneDialogOverlay : OsuTestScene + public class TestSceneIDialogOverlay : OsuTestScene { private DialogOverlay overlay; diff --git a/osu.Game/Collections/DrawableCollectionListItem.cs b/osu.Game/Collections/DrawableCollectionListItem.cs index c4cb040b52..5a20b7e7bd 100644 --- a/osu.Game/Collections/DrawableCollectionListItem.cs +++ b/osu.Game/Collections/DrawableCollectionListItem.cs @@ -158,7 +158,7 @@ namespace osu.Game.Collections public Func IsTextBoxHovered; [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private CollectionManager collectionManager { get; set; } diff --git a/osu.Game/Database/LegacyImportManager.cs b/osu.Game/Database/LegacyImportManager.cs index 4dc26b18bb..59394c2952 100644 --- a/osu.Game/Database/LegacyImportManager.cs +++ b/osu.Game/Database/LegacyImportManager.cs @@ -40,7 +40,7 @@ namespace osu.Game.Database private OsuGame game { get; set; } [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private DesktopGameHost desktopGameHost { get; set; } diff --git a/osu.Game/Online/Chat/ExternalLinkOpener.cs b/osu.Game/Online/Chat/ExternalLinkOpener.cs index 328b43c4e8..20d8459132 100644 --- a/osu.Game/Online/Chat/ExternalLinkOpener.cs +++ b/osu.Game/Online/Chat/ExternalLinkOpener.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online.Chat private GameHost host { get; set; } [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private Bindable externalLinkWarning; diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index ddd9d9a2b2..eecb7ff6b3 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -64,7 +64,7 @@ namespace osu.Game.Online.Leaderboards private List statisticsLabels; [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private SongSelect songSelect { get; set; } diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 6159ab59f0..9fd1a32e84 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -216,7 +216,7 @@ namespace osu.Game.Overlays.Dialog }; // It's important we start in a visible state so our state fires on hide, even before load. - // This is used by the DialogOverlay to know when the dialog was dismissed. + // This is used by the IDialogOverlay to know when the dialog was dismissed. Show(); } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 9dea1ca00a..15d89a561a 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -14,7 +14,7 @@ using osu.Game.Audio.Effects; namespace osu.Game.Overlays { - public class DialogOverlay : OsuFocusedOverlayContainer + public class DialogOverlay : OsuFocusedOverlayContainer, IDialogOverlay { private readonly Container dialogContainer; diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs new file mode 100644 index 0000000000..07f28a7204 --- /dev/null +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -0,0 +1,31 @@ +#nullable enable +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Overlays +{ + /// + /// A global overlay that can show popup dialogs. + /// + [Cached] + public interface IDialogOverlay + { + /// + /// Push a new dialog for display. + /// + /// + /// This will immediate dismiss any already displayed dialog (cancelling the action). + /// If the dialog instance provided is already displayed, it will be a noop. + /// + /// The dialog to be presented. + void Push(PopupDialog dialog); + + /// + /// The currently displayed dialog, if any. + /// + PopupDialog? CurrentDialog { get; } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index aa02d086f4..be4b0decd9 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private SettingsButton undeleteButton; [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] LegacyImportManager legacyImportManager, DialogOverlay dialogOverlay) + private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] LegacyImportManager legacyImportManager, IDialogOverlay dialogOverlay) { if (legacyImportManager?.SupportsImportFromStable == true) { diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs index 0304a4291a..3cb5521e51 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private OsuGameBase game { get; set; } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } protected override DirectoryInfo InitialPath => new DirectoryInfo(storage.GetFullPath(string.Empty)).Parent; diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 528470768c..7039d5379c 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -26,7 +26,7 @@ namespace osu.Game private NotificationOverlay notifications { get; set; } [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(canBeNull: true)] private OsuGame game { get; set; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 57f7429e06..255571dfdb 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -84,7 +84,7 @@ namespace osu.Game.Screens.Edit private Storage storage { get; set; } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(canBeNull: true)] private NotificationOverlay notifications { get; set; } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e2d79b4015..9e6af860fc 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Menu private IAPIProvider api { get; set; } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private BackgroundScreenDefault background; diff --git a/osu.Game/Screens/Menu/StorageErrorDialog.cs b/osu.Game/Screens/Menu/StorageErrorDialog.cs index 250623ec68..f4c77d5d8f 100644 --- a/osu.Game/Screens/Menu/StorageErrorDialog.cs +++ b/osu.Game/Screens/Menu/StorageErrorDialog.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Menu public class StorageErrorDialog : PopupDialog { [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } public StorageErrorDialog(OsuStorage storage, OsuStorageError error) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 7ef0bb5923..0dbf73fc5b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -238,7 +238,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private bool exitConfirmed; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 618c5cf5ec..2d70b1aecb 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select.Carousel private Action viewDetails; [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private CollectionManager collectionManager { get; set; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 2d1a2bce4e..dc505063d1 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Select protected Container LeftArea { get; private set; } private BeatmapInfoWedge beatmapInfoWedge; - private DialogOverlay dialogOverlay; + private IDialogOverlay dialogOverlay; [Resolved] private BeatmapManager beatmaps { get; set; } @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Select private MusicController music { get; set; } [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) + private void load(AudioManager audio, IDialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) { // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 51221cb8fe..ca12bc73fe 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual { [Resolved(canBeNull: true)] [CanBeNull] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } public new void Undo() => base.Undo(); diff --git a/osu.Game/Tests/Visual/ScreenTestScene.cs b/osu.Game/Tests/Visual/ScreenTestScene.cs index b6f6ca6daa..e9069d8c9c 100644 --- a/osu.Game/Tests/Visual/ScreenTestScene.cs +++ b/osu.Game/Tests/Visual/ScreenTestScene.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual protected override Container Content => content; - [Cached] + [Cached(typeof(IDialogOverlay))] protected DialogOverlay DialogOverlay { get; private set; } protected ScreenTestScene() From 25f1f772f3d6cad2a0bb9728057e106588555bd0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 18:36:22 +0900 Subject: [PATCH 0374/1276] Allow caching via `loadComponentSingleFile` with interface specification --- osu.Game/OsuGame.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 73121f6e7d..f8bc32431c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -825,7 +825,7 @@ namespace osu.Game }, rightFloatingOverlayContent.Add, true); loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); - loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); + loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(CreateHighPerformanceSession(), Add); @@ -982,12 +982,15 @@ namespace osu.Game /// The component to load. /// An action to invoke on load completion (generally to add the component to the hierarchy). /// Whether to cache the component as type into the game dependencies before any scheduling. - private T loadComponentSingleFile(T component, Action loadCompleteAction, bool cache = false) - where T : Drawable + private T loadComponentSingleFile(T component, Action loadCompleteAction, bool cache = false) + where T : class { if (cache) dependencies.CacheAs(component); + var drawableComponent = component as Drawable; + Debug.Assert(drawableComponent != null); + if (component is OsuFocusedOverlayContainer overlay) focusedOverlays.Add(overlay); @@ -1011,7 +1014,7 @@ namespace osu.Game // Since this is running in a separate thread, it is possible for OsuGame to be disposed after LoadComponentAsync has been called // throwing an exception. To avoid this, the call is scheduled on the update thread, which does not run if IsDisposed = true Task task = null; - var del = new ScheduledDelegate(() => task = LoadComponentAsync(component, loadCompleteAction)); + var del = new ScheduledDelegate(() => task = LoadComponentAsync(drawableComponent, loadCompleteAction)); Scheduler.Add(del); // The delegate won't complete if OsuGame has been disposed in the meantime From fea4632e839207ae1b183878aff42a437e271fa6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 20:04:19 +0900 Subject: [PATCH 0375/1276] Convert assert to hard throw --- osu.Game/OsuGame.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f8bc32431c..e10da39055 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -988,8 +988,7 @@ namespace osu.Game if (cache) dependencies.CacheAs(component); - var drawableComponent = component as Drawable; - Debug.Assert(drawableComponent != null); + var drawableComponent = component as Drawable ?? throw new ArgumentException($"Component must be a {nameof(Drawable)}", nameof(component)); if (component is OsuFocusedOverlayContainer overlay) focusedOverlays.Add(overlay); From d86e3abf2c5d4857062cf23d680fa40f94006a0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 20:04:38 +0900 Subject: [PATCH 0376/1276] Fix invalid rename --- osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index 17234f4022..405461eec8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneIDialogOverlay : OsuTestScene + public class TestSceneDialogOverlay : OsuTestScene { private DialogOverlay overlay; From 72a33c0926dde589b5dc1c2ffabdcf5184bdb474 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 20:06:08 +0900 Subject: [PATCH 0377/1276] Split out step for dialog overlay load wait --- .../Visual/Navigation/TestScenePerformFromScreen.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index c4099b55c7..559e6ddd89 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("did not perform", () => !actionPerformed); AddAssert("only one exit attempt", () => blocker.ExitAttempts == 1); - AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + waitForDialogOverlayLoad(); if (confirmed) { @@ -146,7 +146,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + waitForDialogOverlayLoad(); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == blocker2); AddAssert("did not perform", () => !actionPerformed); @@ -188,7 +188,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + waitForDialogOverlayLoad(); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); @@ -212,6 +212,8 @@ namespace osu.Game.Tests.Visual.Navigation } } + private void waitForDialogOverlayLoad() => AddUntilStep("wait for dialog overlay loaded", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + private void importAndWaitForSongSelect() { AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); From 043599081bc29f81fa6888d48d214d1cc505edb3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:59:57 +0900 Subject: [PATCH 0378/1276] Split out `INotificationOverlay` to allow for easier testing --- .../Security/ElevatedPrivilegesChecker.cs | 2 +- osu.Desktop/Updater/SquirrelUpdateManager.cs | 2 +- osu.Game.Tests/Visual/Menus/IntroTestScene.cs | 2 +- .../Visual/Navigation/TestSceneOsuGame.cs | 2 +- .../Visual/Online/TestSceneMessageNotifier.cs | 2 +- osu.Game/Database/EFToRealmMigrator.cs | 2 +- osu.Game/Graphics/ScreenshotManager.cs | 2 +- osu.Game/Online/Chat/MessageNotifier.cs | 4 +-- osu.Game/OsuGame.cs | 2 +- .../BeatmapSet/Buttons/FavouriteButton.cs | 2 +- osu.Game/Overlays/INotificationOverlay.cs | 26 +++++++++++++++++++ osu.Game/Overlays/NotificationOverlay.cs | 6 +---- .../Sections/General/UpdateSettings.cs | 2 +- .../Maintenance/MigrationRunScreen.cs | 2 +- osu.Game/PerformFromMenuRunner.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 4 +-- osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- osu.Game/Updater/UpdateManager.cs | 4 +-- 22 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 osu.Game/Overlays/INotificationOverlay.cs diff --git a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs index 8f3ad853dc..ba37a14442 100644 --- a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs +++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs @@ -19,7 +19,7 @@ namespace osu.Desktop.Security public class ElevatedPrivilegesChecker : Component { [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } private bool elevated; diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index b307146b10..bae850fce1 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -25,7 +25,7 @@ namespace osu.Desktop.Updater public class SquirrelUpdateManager : osu.Game.Updater.UpdateManager { private UpdateManager updateManager; - private NotificationOverlay notificationOverlay; + private INotificationOverlay notificationOverlay; public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited(); diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs index 82accceb23..c68cd39c65 100644 --- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs +++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Menus private IntroScreen intro; - [Cached] + [Cached(typeof(INotificationOverlay))] private NotificationOverlay notifications; private ScheduledDelegate trackResetDelegate; diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index 921d088fc7..0f8337deb6 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Navigation typeof(OsuLogo), typeof(IdleTracker), typeof(OnScreenDisplay), - typeof(NotificationOverlay), + typeof(INotificationOverlay), typeof(BeatmapListingOverlay), typeof(DashboardOverlay), typeof(NewsOverlay), diff --git a/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs b/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs index 2c253650d5..79f62a16e3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs @@ -200,7 +200,7 @@ namespace osu.Game.Tests.Visual.Online [Cached] public ChannelManager ChannelManager { get; } = new ChannelManager(); - [Cached] + [Cached(typeof(INotificationOverlay))] public NotificationOverlay NotificationOverlay { get; } = new NotificationOverlay { Anchor = Anchor.TopRight, diff --git a/osu.Game/Database/EFToRealmMigrator.cs b/osu.Game/Database/EFToRealmMigrator.cs index ae73e13b77..4e98b7d3d2 100644 --- a/osu.Game/Database/EFToRealmMigrator.cs +++ b/osu.Game/Database/EFToRealmMigrator.cs @@ -52,7 +52,7 @@ namespace osu.Game.Database private OsuConfigManager config { get; set; } = null!; [Resolved] - private NotificationOverlay notificationOverlay { get; set; } = null!; + private INotificationOverlay notificationOverlay { get; set; } = null!; [Resolved] private OsuGame game { get; set; } = null!; diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index b0f20de685..a2f1a3d7b9 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -43,7 +43,7 @@ namespace osu.Game.Graphics private Storage storage; [Resolved] - private NotificationOverlay notificationOverlay { get; set; } + private INotificationOverlay notificationOverlay { get; set; } private Sample shutter; diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index 20c33db61f..ca6082e19b 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -24,7 +24,7 @@ namespace osu.Game.Online.Chat public class MessageNotifier : Component { [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved] private ChatOverlay chatOverlay { get; set; } @@ -170,7 +170,7 @@ namespace osu.Game.Online.Chat public override bool IsImportant => false; [BackgroundDependencyLoader] - private void load(OsuColour colours, ChatOverlay chatOverlay, NotificationOverlay notificationOverlay) + private void load(OsuColour colours, ChatOverlay chatOverlay, INotificationOverlay notificationOverlay) { IconBackground.Colour = colours.PurpleDark; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e10da39055..45d76cc15b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -778,7 +778,7 @@ namespace osu.Game loadComponentSingleFile(onScreenDisplay, Add, true); - loadComponentSingleFile(Notifications.With(d => + loadComponentSingleFile(Notifications.With(d => { d.Anchor = Anchor.TopRight; d.Origin = Anchor.TopRight; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index 8fe7450873..28100e5fff 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons } [BackgroundDependencyLoader(true)] - private void load(IAPIProvider api, NotificationOverlay notifications) + private void load(IAPIProvider api, INotificationOverlay notifications) { SpriteIcon icon; diff --git a/osu.Game/Overlays/INotificationOverlay.cs b/osu.Game/Overlays/INotificationOverlay.cs new file mode 100644 index 0000000000..432dd2be54 --- /dev/null +++ b/osu.Game/Overlays/INotificationOverlay.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Overlays.Notifications; + +namespace osu.Game.Overlays +{ + /// + /// An overlay which is capable of showing notifications to the user. + /// + [Cached] + public interface INotificationOverlay + { + /// + /// Post a new notification for display. + /// + /// The notification to display. + void Post(Notification notification); + + /// + /// Hide the overlay, if it is currently visible. + /// + void Hide(); + } +} diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index e4e3931048..0e8fbc6680 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -19,7 +19,7 @@ using osu.Game.Localisation; namespace osu.Game.Overlays { - public class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent + public class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent, INotificationOverlay { public string IconTexture => "Icons/Hexacons/notification"; public LocalisableString Title => NotificationsStrings.HeaderTitle; @@ -111,10 +111,6 @@ namespace osu.Game.Overlays private double? lastSamplePlayback; - /// - /// Post a new notification for display. - /// - /// The notification to display. public void Post(Notification notification) => postScheduler.Add(() => { ++runningDepth; diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 0b4eca6379..5bc88b8692 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.General private SettingsButton checkForUpdatesButton; [Resolved(CanBeNull = true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [BackgroundDependencyLoader(true)] private void load(Storage storage, OsuConfigManager config, OsuGame game) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs index fb7ff0dbd1..10d6f0aef5 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private OsuGame game { get; set; } [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved] private Storage storage { get; set; } diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 7039d5379c..ae9879fb5a 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -23,7 +23,7 @@ namespace osu.Game private readonly Func getCurrentScreen; [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved] private IDialogOverlay dialogOverlay { get; set; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 255571dfdb..602b7563ac 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Edit private IDialogOverlay dialogOverlay { get; set; } [Resolved(canBeNull: true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } public readonly Bindable Mode = new Bindable(); diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 8eeb90a3fd..598c441042 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -118,7 +118,7 @@ namespace osu.Game.Screens.Menu private IAPIProvider api { get; set; } [Resolved(CanBeNull = true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved(CanBeNull = true)] private LoginOverlay loginOverlay { get; set; } diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index e569b0e517..5575d3681f 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -171,7 +171,7 @@ namespace osu.Game.Screens.Menu } [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } private void ensureEventuallyArrivingAtMenu() { diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 628452fbc8..676a08b434 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader(true)] - private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) + private void load(OsuConfigManager config, INotificationOverlay notificationOverlay) { if (drawableRuleset != null) { diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index ba720af2a1..b72ec373d7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -515,7 +515,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audioManager, NotificationOverlay notificationOverlay, VolumeOverlay volumeOverlay) + private void load(OsuColour colours, AudioManager audioManager, INotificationOverlay notificationOverlay, VolumeOverlay volumeOverlay) { Icon = FontAwesome.Solid.VolumeMute; IconBackground.Colour = colours.RedDark; @@ -567,7 +567,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(OsuColour colours, NotificationOverlay notificationOverlay) + private void load(OsuColour colours, INotificationOverlay notificationOverlay) { Icon = FontAwesome.Solid.BatteryQuarter; IconBackground.Colour = colours.RedDark; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 593436bbb7..4a991ffc66 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Select private OsuScreen playerLoader; [Resolved(CanBeNull = true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } public override bool AllowExternalScreenChange => true; diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 9b9f354d23..c17d8304b9 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -31,7 +31,7 @@ namespace osu.Game.Updater private OsuGameBase game { get; set; } [Resolved] - protected NotificationOverlay Notifications { get; private set; } + protected INotificationOverlay Notifications { get; private set; } protected override void LoadComplete() { @@ -94,7 +94,7 @@ namespace osu.Game.Updater } [BackgroundDependencyLoader] - private void load(OsuColour colours, ChangelogOverlay changelog, NotificationOverlay notificationOverlay) + private void load(OsuColour colours, ChangelogOverlay changelog, INotificationOverlay notificationOverlay) { Icon = FontAwesome.Solid.CheckSquare; IconBackground.Colour = colours.BlueDark; From d29312e0709eb11e63effe51fb6c806966e3689d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 21:47:47 +0900 Subject: [PATCH 0379/1276] Fix rider incompetency --- osu.Game/Overlays/IDialogOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs index 07f28a7204..dedc164510 100644 --- a/osu.Game/Overlays/IDialogOverlay.cs +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -1,7 +1,8 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using osu.Framework.Allocation; using osu.Game.Overlays.Dialog; From aaf5577e6a035606f19e3fd249b7a4f464f35a13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 21:48:46 +0900 Subject: [PATCH 0380/1276] Remove unnecessary `canBeNull` specification --- .../Sections/Maintenance/StableDirectoryLocationDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 4e81cd63b8..b16fd9a5a1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class StableDirectoryLocationDialog : PopupDialog { - [Resolved(canBeNull: true)] + [Resolved] private IPerformFromScreenRunner performer { get; set; } public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) From 5d5e46ede71bd05ac9846da73eb03119f96cad7b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 21:47:47 +0900 Subject: [PATCH 0381/1276] Fix rider incompetency --- osu.Game/Overlays/IDialogOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs index 07f28a7204..dedc164510 100644 --- a/osu.Game/Overlays/IDialogOverlay.cs +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -1,7 +1,8 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using osu.Framework.Allocation; using osu.Game.Overlays.Dialog; From 3cbc6cd29767a7d47ba624dc1680c22f4a02cedd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 21:04:22 +0300 Subject: [PATCH 0382/1276] Update further tests to cache using `IDialogOverlay` --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 2 +- osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs | 2 +- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs | 2 +- .../Visual/SongSelect/TestSceneUserTopScoreContainer.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index d077868175..6818147da4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online Dependencies.Cache(chatManager); Dependencies.Cache(new ChatOverlay()); - Dependencies.Cache(dialogOverlay); + Dependencies.CacheAs(dialogOverlay); } [SetUp] diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index f9c9b2a68b..2106043544 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Settings Depth = -1 }); - Dependencies.Cache(dialogOverlay); + Dependencies.CacheAs(dialogOverlay); } } } diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 1ed6648131..3b15ee9c45 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.SongSelect { private readonly FailableLeaderboard leaderboard; - [Cached] + [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay; private ScoreManager scoreManager; diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs index dd7f9951bf..c71e54e9a8 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneUserTopScoreContainer : OsuTestScene { - [Cached] + [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay; public TestSceneUserTopScoreContainer() From 16a0efc5621ba8a751f874bb8fef91218766efa2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 21:06:27 +0300 Subject: [PATCH 0383/1276] Cache `IDialogOverlay` as its type in interface --- osu.Game/Overlays/IDialogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs index dedc164510..1c6a84cd64 100644 --- a/osu.Game/Overlays/IDialogOverlay.cs +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays /// /// A global overlay that can show popup dialogs. /// - [Cached] + [Cached(typeof(IDialogOverlay))] public interface IDialogOverlay { /// From 1fc28552b54400ca6e49f40ad5e40f4c88d3bd6e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 21:07:20 +0300 Subject: [PATCH 0384/1276] Minor reword inline comment --- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 9fd1a32e84..d08b6b7beb 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -216,7 +216,7 @@ namespace osu.Game.Overlays.Dialog }; // It's important we start in a visible state so our state fires on hide, even before load. - // This is used by the IDialogOverlay to know when the dialog was dismissed. + // This is used by the dialog overlay to know when the dialog was dismissed. Show(); } From 9d59cd408fda8b931d77f9001ac38a31a4a91fb7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 22:59:49 +0300 Subject: [PATCH 0385/1276] Add concept of "initial items count" in paginated API requests --- .../Online/API/Requests/GetUserBeatmapsRequest.cs | 4 ++-- .../API/Requests/GetUserKudosuHistoryRequest.cs | 4 ++-- .../Requests/GetUserMostPlayedBeatmapsRequest.cs | 4 ++-- .../API/Requests/GetUserRecentActivitiesRequest.cs | 4 ++-- .../Online/API/Requests/GetUserScoresRequest.cs | 4 ++-- osu.Game/Online/API/Requests/PaginatedAPIRequest.cs | 13 ++++++++++--- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index e2c0ed4301..b7681cb71b 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -13,8 +13,8 @@ namespace osu.Game.Online.API.Requests private readonly BeatmapSetType type; - public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int page = 0, int itemsPerPage = 6) - : base(page, itemsPerPage) + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int page, int itemsPerPage, int initialItems) + : base(page, itemsPerPage, initialItems) { this.userId = userId; this.type = type; diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs index e90e297672..d2102d7697 100644 --- a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserKudosuHistoryRequest(long userId, int page = 0, int itemsPerPage = 5) - : base(page, itemsPerPage) + public GetUserKudosuHistoryRequest(long userId, int page, int itemsPerPage, int initialItems) + : base(page, itemsPerPage, initialItems) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index 9f094e51c4..a9b6dd7edc 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserMostPlayedBeatmapsRequest(long userId, int page = 0, int itemsPerPage = 5) - : base(page, itemsPerPage) + public GetUserMostPlayedBeatmapsRequest(long userId, int page, int itemsPerPage, int initialItems) + : base(page, itemsPerPage, initialItems) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs index f2fa51bde7..60acc27182 100644 --- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserRecentActivitiesRequest(long userId, int page = 0, int itemsPerPage = 5) - : base(page, itemsPerPage) + public GetUserRecentActivitiesRequest(long userId, int page, int itemsPerPage, int initialItems) + : base(page, itemsPerPage, initialItems) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index 5d39799f6b..85b58fc596 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -14,8 +14,8 @@ namespace osu.Game.Online.API.Requests private readonly ScoreType type; private readonly RulesetInfo ruleset; - public GetUserScoresRequest(long userId, ScoreType type, int page = 0, int itemsPerPage = 5, RulesetInfo ruleset = null) - : base(page, itemsPerPage) + public GetUserScoresRequest(long userId, ScoreType type, int page, int itemsPerPage, int initialItems, RulesetInfo ruleset = null) + : base(page, itemsPerPage, initialItems) { this.userId = userId; this.type = type; diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs index bddc34a0dc..ea59fcfc25 100644 --- a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs +++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs @@ -9,11 +9,13 @@ namespace osu.Game.Online.API.Requests public abstract class PaginatedAPIRequest : APIRequest where T : class { private readonly int page; + private readonly int initialItems; private readonly int itemsPerPage; - protected PaginatedAPIRequest(int page, int itemsPerPage) + protected PaginatedAPIRequest(int page, int itemsPerPage, int initialItems) { this.page = page; + this.initialItems = initialItems; this.itemsPerPage = itemsPerPage; } @@ -21,8 +23,13 @@ namespace osu.Game.Online.API.Requests { var req = base.CreateWebRequest(); - req.AddParameter("offset", (page * itemsPerPage).ToString(CultureInfo.InvariantCulture)); - req.AddParameter("limit", itemsPerPage.ToString(CultureInfo.InvariantCulture)); + if (page == 0) + req.AddParameter("limit", initialItems.ToString(CultureInfo.InvariantCulture)); + else + { + req.AddParameter("offset", (initialItems + (page - 1) * itemsPerPage).ToString(CultureInfo.InvariantCulture)); + req.AddParameter("limit", itemsPerPage.ToString(CultureInfo.InvariantCulture)); + } return req; } From f08449e432d11625ec3b3fa3ad5d95df1ef091c8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 23:04:21 +0300 Subject: [PATCH 0386/1276] Update paginated profile subsections to display items inline with web --- .../Beatmaps/PaginatedBeatmapContainer.cs | 7 ++++--- .../PaginatedMostPlayedBeatmapContainer.cs | 5 ++--- .../Kudosu/PaginatedKudosuHistoryContainer.cs | 5 ++--- .../Sections/PaginatedProfileSubsection.cs | 19 +++++++++++++++---- .../Sections/Ranks/PaginatedScoreContainer.cs | 6 ++---- .../PaginatedRecentActivityContainer.cs | 5 ++--- 6 files changed, 27 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index d39074bd49..deeb63b5bf 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -20,11 +20,12 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps private const float panel_padding = 10f; private readonly BeatmapSetType type; + protected override int InitialItemsCount => type == BeatmapSetType.Graveyard ? 2 : 6; + public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, LocalisableString headerText) : base(user, headerText) { this.type = type; - ItemsPerPage = 6; } [BackgroundDependencyLoader] @@ -57,8 +58,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps } } - protected override APIRequest> CreateRequest() => - new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); + protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => + new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, itemsPerPage, initialItems); protected override Drawable CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0 ? new BeatmapCardNormal(model) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index ad1192a13a..ccd60fcd89 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -19,7 +19,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical public PaginatedMostPlayedBeatmapContainer(Bindable user) : base(user, UsersStrings.ShowExtraHistoricalMostPlayedTitle) { - ItemsPerPage = 5; } [BackgroundDependencyLoader] @@ -30,8 +29,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override int GetCount(APIUser user) => user.BeatmapPlayCountsCount; - protected override APIRequest> CreateRequest() => - new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); + protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => + new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap mostPlayed) => new DrawableMostPlayedBeatmap(mostPlayed); diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs index c4837cc0e2..311a64a10d 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -17,11 +17,10 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu public PaginatedKudosuHistoryContainer(Bindable user) : base(user, missingText: UsersStrings.ShowExtraKudosuEntryEmpty) { - ItemsPerPage = 5; } - protected override APIRequest> CreateRequest() - => new GetUserKudosuHistoryRequest(User.Value.Id, VisiblePages++, ItemsPerPage); + protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) + => new GetUserKudosuHistoryRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); protected override Drawable CreateDrawableItem(APIKudosuHistory item) => new DrawableKudosuHistoryItem(item); } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index 9dcbf6142d..646c16fbd7 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -21,11 +21,20 @@ namespace osu.Game.Overlays.Profile.Sections { public abstract class PaginatedProfileSubsection : ProfileSubsection { + /// + /// The number of items displayed per page. + /// + protected virtual int ItemsPerPage => 50; + + /// + /// The number of items displayed initially. + /// + protected virtual int InitialItemsCount => 5; + [Resolved] private IAPIProvider api { get; set; } protected int VisiblePages; - protected int ItemsPerPage; protected ReverseChildIDFillFlowContainer ItemsContainer { get; private set; } @@ -101,7 +110,7 @@ namespace osu.Game.Overlays.Profile.Sections { loadCancellation = new CancellationTokenSource(); - retrievalRequest = CreateRequest(); + retrievalRequest = CreateRequest(ItemsPerPage, InitialItemsCount); retrievalRequest.Success += UpdateItems; api.Queue(retrievalRequest); @@ -125,7 +134,9 @@ namespace osu.Game.Overlays.Profile.Sections LoadComponentsAsync(items.Select(CreateDrawableItem).Where(d => d != null), drawables => { missing.Hide(); - moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); + + int maxCount = VisiblePages == 1 ? InitialItemsCount : ItemsPerPage; + moreButton.FadeTo(items.Count == maxCount ? 1 : 0); moreButton.IsLoading = false; ItemsContainer.AddRange(drawables); @@ -138,7 +149,7 @@ namespace osu.Game.Overlays.Profile.Sections { } - protected abstract APIRequest> CreateRequest(); + protected abstract APIRequest> CreateRequest(int itemsPerPage, int initialItems); protected abstract Drawable CreateDrawableItem(TModel model); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 5c67da1911..cc5952017d 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -23,8 +23,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks : base(user, headerText) { this.type = type; - - ItemsPerPage = 5; } [BackgroundDependencyLoader] @@ -62,8 +60,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks base.OnItemsReceived(items); } - protected override APIRequest> CreateRequest() => - new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); + protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => + new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, itemsPerPage, initialItems); private int drawableItemIndex; diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index c5ff896654..60cba95fda 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -19,7 +19,6 @@ namespace osu.Game.Overlays.Profile.Sections.Recent public PaginatedRecentActivityContainer(Bindable user) : base(user, missingText: EventsStrings.Empty) { - ItemsPerPage = 10; } [BackgroundDependencyLoader] @@ -28,8 +27,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent ItemsContainer.Spacing = new Vector2(0, 8); } - protected override APIRequest> CreateRequest() => - new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, ItemsPerPage); + protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => + new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); protected override Drawable CreateDrawableItem(APIRecentActivity model) => new DrawableRecentActivity(model); } From c4d89b8c3c446d6440b023c2fb5508da4c99add6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 05:57:03 +0900 Subject: [PATCH 0387/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index a8952df329..8b22df418f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3246255815..0a102510e7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 668d23f82e..2f0facea18 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 94335c293848a5475aa0df3f8c71f6726a544ec8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 00:18:10 +0300 Subject: [PATCH 0388/1276] Update further usages to cache/resolve via interface --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 4 ++-- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index bae850fce1..c09cce1235 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -39,9 +39,9 @@ namespace osu.Desktop.Updater private readonly SquirrelLogger squirrelLogger = new SquirrelLogger(); [BackgroundDependencyLoader] - private void load(NotificationOverlay notification) + private void load(INotificationOverlay notifications) { - notificationOverlay = notification; + notificationOverlay = notifications; SquirrelLocator.CurrentMutable.Register(() => squirrelLogger, typeof(ILogger)); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 958d617d63..950c755cc1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private SessionStatics sessionStatics { get; set; } - [Cached] + [Cached(typeof(INotificationOverlay))] private readonly NotificationOverlay notificationOverlay; [Cached] diff --git a/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs b/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs index a68090504d..ac0956502e 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Settings { public class TestSceneMigrationScreens : ScreenTestScene { - [Cached] + [Cached(typeof(INotificationOverlay))] private readonly NotificationOverlay notifications; public TestSceneMigrationScreens() diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index b72ec373d7..aea39bc8ff 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Play private EpilepsyWarning? epilepsyWarning; [Resolved(CanBeNull = true)] - private NotificationOverlay? notificationOverlay { get; set; } + private INotificationOverlay? notificationOverlay { get; set; } [Resolved(CanBeNull = true)] private VolumeOverlay? volumeOverlay { get; set; } From 96d4369cc823d71370f1c2fa9f8faaecba66f828 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 02:04:23 +0300 Subject: [PATCH 0389/1276] Introduce `Pagination` and simplify paginated API requests --- .../API/Requests/GetUserBeatmapsRequest.cs | 4 +- .../Requests/GetUserKudosuHistoryRequest.cs | 4 +- .../GetUserMostPlayedBeatmapsRequest.cs | 4 +- .../GetUserRecentActivitiesRequest.cs | 4 +- .../API/Requests/GetUserScoresRequest.cs | 4 +- .../API/Requests/PaginatedAPIRequest.cs | 19 +++------- osu.Game/Online/API/Requests/Pagination.cs | 38 +++++++++++++++++++ .../Beatmaps/PaginatedBeatmapContainer.cs | 4 +- .../PaginatedMostPlayedBeatmapContainer.cs | 4 +- .../Kudosu/PaginatedKudosuHistoryContainer.cs | 4 +- .../Sections/PaginatedProfileSubsection.cs | 16 ++++---- .../Sections/Ranks/PaginatedScoreContainer.cs | 6 +-- .../PaginatedRecentActivityContainer.cs | 4 +- 13 files changed, 73 insertions(+), 42 deletions(-) create mode 100644 osu.Game/Online/API/Requests/Pagination.cs diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index b7681cb71b..019c805c9a 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -13,8 +13,8 @@ namespace osu.Game.Online.API.Requests private readonly BeatmapSetType type; - public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int page, int itemsPerPage, int initialItems) - : base(page, itemsPerPage, initialItems) + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, Pagination pagination) + : base(pagination) { this.userId = userId; this.type = type; diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs index d2102d7697..3759c59722 100644 --- a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserKudosuHistoryRequest(long userId, int page, int itemsPerPage, int initialItems) - : base(page, itemsPerPage, initialItems) + public GetUserKudosuHistoryRequest(long userId, Pagination pagination) + : base(pagination) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index a9b6dd7edc..37359e2cd0 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserMostPlayedBeatmapsRequest(long userId, int page, int itemsPerPage, int initialItems) - : base(page, itemsPerPage, initialItems) + public GetUserMostPlayedBeatmapsRequest(long userId, Pagination pagination) + : base(pagination) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs index 60acc27182..c16633c17a 100644 --- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserRecentActivitiesRequest(long userId, int page, int itemsPerPage, int initialItems) - : base(page, itemsPerPage, initialItems) + public GetUserRecentActivitiesRequest(long userId, Pagination pagination) + : base(pagination) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index 85b58fc596..af01969a1a 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -14,8 +14,8 @@ namespace osu.Game.Online.API.Requests private readonly ScoreType type; private readonly RulesetInfo ruleset; - public GetUserScoresRequest(long userId, ScoreType type, int page, int itemsPerPage, int initialItems, RulesetInfo ruleset = null) - : base(page, itemsPerPage, initialItems) + public GetUserScoresRequest(long userId, ScoreType type, Pagination pagination, RulesetInfo ruleset = null) + : base(pagination) { this.userId = userId; this.type = type; diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs index ea59fcfc25..ddc453bf17 100644 --- a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs +++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs @@ -8,28 +8,19 @@ namespace osu.Game.Online.API.Requests { public abstract class PaginatedAPIRequest : APIRequest where T : class { - private readonly int page; - private readonly int initialItems; - private readonly int itemsPerPage; + private readonly Pagination pagination; - protected PaginatedAPIRequest(int page, int itemsPerPage, int initialItems) + protected PaginatedAPIRequest(Pagination pagination) { - this.page = page; - this.initialItems = initialItems; - this.itemsPerPage = itemsPerPage; + this.pagination = pagination; } protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); - if (page == 0) - req.AddParameter("limit", initialItems.ToString(CultureInfo.InvariantCulture)); - else - { - req.AddParameter("offset", (initialItems + (page - 1) * itemsPerPage).ToString(CultureInfo.InvariantCulture)); - req.AddParameter("limit", itemsPerPage.ToString(CultureInfo.InvariantCulture)); - } + req.AddParameter("offset", pagination.Offset.ToString(CultureInfo.InvariantCulture)); + req.AddParameter("limit", pagination.Limit.ToString(CultureInfo.InvariantCulture)); return req; } diff --git a/osu.Game/Online/API/Requests/Pagination.cs b/osu.Game/Online/API/Requests/Pagination.cs new file mode 100644 index 0000000000..a1caac7592 --- /dev/null +++ b/osu.Game/Online/API/Requests/Pagination.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online.API.Requests +{ + /// + /// Represents a pagination data used for . + /// + public readonly struct Pagination + { + /// + /// The starting point of the request. + /// + public int Offset { get; } + + /// + /// The maximum number of items to return in this request. + /// + public int Limit { get; } + + public Pagination(int offset, int limit) + { + Offset = offset; + Limit = limit; + } + + public Pagination(int limit) + : this(0, limit) + { + } + + /// + /// Returns a of the next number of items defined by after this. + /// + /// The limit of the next pagination. + public Pagination TakeNext(int limit) => new Pagination(Offset + Limit, limit); + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index deeb63b5bf..f1c2b4bf7a 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -58,8 +58,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps } } - protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => - new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, itemsPerPage, initialItems); + protected override APIRequest> CreateRequest(Pagination pagination) => + new GetUserBeatmapsRequest(User.Value.Id, type, pagination); protected override Drawable CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0 ? new BeatmapCardNormal(model) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index ccd60fcd89..281bcc3a03 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -29,8 +29,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override int GetCount(APIUser user) => user.BeatmapPlayCountsCount; - protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => - new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); + protected override APIRequest> CreateRequest(Pagination pagination) => + new GetUserMostPlayedBeatmapsRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap mostPlayed) => new DrawableMostPlayedBeatmap(mostPlayed); diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs index 311a64a10d..5aada2b655 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -19,8 +19,8 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { } - protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) - => new GetUserKudosuHistoryRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); + protected override APIRequest> CreateRequest(Pagination pagination) + => new GetUserKudosuHistoryRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIKudosuHistory item) => new DrawableKudosuHistoryItem(item); } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index 646c16fbd7..a1f9e7546a 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Graphics.Containers; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osuTK; @@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Profile.Sections [Resolved] private IAPIProvider api { get; set; } - protected int VisiblePages; + protected Pagination? CurrentPage { get; private set; } protected ReverseChildIDFillFlowContainer ItemsContainer { get; private set; } @@ -96,7 +97,7 @@ namespace osu.Game.Overlays.Profile.Sections loadCancellation?.Cancel(); retrievalRequest?.Cancel(); - VisiblePages = 0; + CurrentPage = null; ItemsContainer.Clear(); if (e.NewValue != null) @@ -110,7 +111,9 @@ namespace osu.Game.Overlays.Profile.Sections { loadCancellation = new CancellationTokenSource(); - retrievalRequest = CreateRequest(ItemsPerPage, InitialItemsCount); + CurrentPage = CurrentPage?.TakeNext(ItemsPerPage) ?? new Pagination(InitialItemsCount); + + retrievalRequest = CreateRequest(CurrentPage.Value); retrievalRequest.Success += UpdateItems; api.Queue(retrievalRequest); @@ -120,7 +123,7 @@ namespace osu.Game.Overlays.Profile.Sections { OnItemsReceived(items); - if (!items.Any() && VisiblePages == 1) + if (!items.Any() && CurrentPage?.Offset == 0) { moreButton.Hide(); moreButton.IsLoading = false; @@ -135,8 +138,7 @@ namespace osu.Game.Overlays.Profile.Sections { missing.Hide(); - int maxCount = VisiblePages == 1 ? InitialItemsCount : ItemsPerPage; - moreButton.FadeTo(items.Count == maxCount ? 1 : 0); + moreButton.FadeTo(items.Count == CurrentPage?.Limit ? 1 : 0); moreButton.IsLoading = false; ItemsContainer.AddRange(drawables); @@ -149,7 +151,7 @@ namespace osu.Game.Overlays.Profile.Sections { } - protected abstract APIRequest> CreateRequest(int itemsPerPage, int initialItems); + protected abstract APIRequest> CreateRequest(Pagination pagination); protected abstract Drawable CreateDrawableItem(TModel model); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index cc5952017d..c3a5e3f8b8 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -54,14 +54,14 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks protected override void OnItemsReceived(List items) { - if (VisiblePages == 0) + if (CurrentPage == null || CurrentPage?.Offset == 0) drawableItemIndex = 0; base.OnItemsReceived(items); } - protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => - new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, itemsPerPage, initialItems); + protected override APIRequest> CreateRequest(Pagination pagination) => + new GetUserScoresRequest(User.Value.Id, type, pagination); private int drawableItemIndex; diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index 60cba95fda..e5118094ce 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -27,8 +27,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent ItemsContainer.Spacing = new Vector2(0, 8); } - protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => - new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); + protected override APIRequest> CreateRequest(Pagination pagination) => + new GetUserRecentActivitiesRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIRecentActivity model) => new DrawableRecentActivity(model); } From c858ec24833b2020a434180d9f63a8a472ec7173 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 02:48:34 +0300 Subject: [PATCH 0390/1276] `Pagination` -> `PaginationParameters --- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 2 +- .../Online/API/Requests/GetUserKudosuHistoryRequest.cs | 2 +- .../API/Requests/GetUserMostPlayedBeatmapsRequest.cs | 2 +- .../API/Requests/GetUserRecentActivitiesRequest.cs | 2 +- osu.Game/Online/API/Requests/GetUserScoresRequest.cs | 2 +- osu.Game/Online/API/Requests/PaginatedAPIRequest.cs | 4 ++-- .../{Pagination.cs => PaginationParameters.cs} | 10 +++++----- .../Sections/Beatmaps/PaginatedBeatmapContainer.cs | 2 +- .../Historical/PaginatedMostPlayedBeatmapContainer.cs | 2 +- .../Sections/Kudosu/PaginatedKudosuHistoryContainer.cs | 2 +- .../Profile/Sections/PaginatedProfileSubsection.cs | 6 +++--- .../Profile/Sections/Ranks/PaginatedScoreContainer.cs | 2 +- .../Recent/PaginatedRecentActivityContainer.cs | 2 +- 13 files changed, 20 insertions(+), 20 deletions(-) rename osu.Game/Online/API/Requests/{Pagination.cs => PaginationParameters.cs} (68%) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index 019c805c9a..ffb3a3be9b 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -13,7 +13,7 @@ namespace osu.Game.Online.API.Requests private readonly BeatmapSetType type; - public GetUserBeatmapsRequest(long userId, BeatmapSetType type, Pagination pagination) + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, PaginationParameters pagination) : base(pagination) { this.userId = userId; diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs index 3759c59722..67d3ad26b0 100644 --- a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserKudosuHistoryRequest(long userId, Pagination pagination) + public GetUserKudosuHistoryRequest(long userId, PaginationParameters pagination) : base(pagination) { this.userId = userId; diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index 37359e2cd0..bef3df42fb 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserMostPlayedBeatmapsRequest(long userId, Pagination pagination) + public GetUserMostPlayedBeatmapsRequest(long userId, PaginationParameters pagination) : base(pagination) { this.userId = userId; diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs index c16633c17a..79f0549d4a 100644 --- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserRecentActivitiesRequest(long userId, Pagination pagination) + public GetUserRecentActivitiesRequest(long userId, PaginationParameters pagination) : base(pagination) { this.userId = userId; diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index af01969a1a..7250929f11 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.API.Requests private readonly ScoreType type; private readonly RulesetInfo ruleset; - public GetUserScoresRequest(long userId, ScoreType type, Pagination pagination, RulesetInfo ruleset = null) + public GetUserScoresRequest(long userId, ScoreType type, PaginationParameters pagination, RulesetInfo ruleset = null) : base(pagination) { this.userId = userId; diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs index ddc453bf17..3d719de958 100644 --- a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs +++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs @@ -8,9 +8,9 @@ namespace osu.Game.Online.API.Requests { public abstract class PaginatedAPIRequest : APIRequest where T : class { - private readonly Pagination pagination; + private readonly PaginationParameters pagination; - protected PaginatedAPIRequest(Pagination pagination) + protected PaginatedAPIRequest(PaginationParameters pagination) { this.pagination = pagination; } diff --git a/osu.Game/Online/API/Requests/Pagination.cs b/osu.Game/Online/API/Requests/PaginationParameters.cs similarity index 68% rename from osu.Game/Online/API/Requests/Pagination.cs rename to osu.Game/Online/API/Requests/PaginationParameters.cs index a1caac7592..3593a4fe83 100644 --- a/osu.Game/Online/API/Requests/Pagination.cs +++ b/osu.Game/Online/API/Requests/PaginationParameters.cs @@ -6,7 +6,7 @@ namespace osu.Game.Online.API.Requests /// /// Represents a pagination data used for . /// - public readonly struct Pagination + public readonly struct PaginationParameters { /// /// The starting point of the request. @@ -18,21 +18,21 @@ namespace osu.Game.Online.API.Requests /// public int Limit { get; } - public Pagination(int offset, int limit) + public PaginationParameters(int offset, int limit) { Offset = offset; Limit = limit; } - public Pagination(int limit) + public PaginationParameters(int limit) : this(0, limit) { } /// - /// Returns a of the next number of items defined by after this. + /// Returns a of the next number of items defined by after this. /// /// The limit of the next pagination. - public Pagination TakeNext(int limit) => new Pagination(Offset + Limit, limit); + public PaginationParameters TakeNext(int limit) => new PaginationParameters(Offset + Limit, limit); } } diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index f1c2b4bf7a..0424fe8bd9 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps } } - protected override APIRequest> CreateRequest(Pagination pagination) => + protected override APIRequest> CreateRequest(PaginationParameters pagination) => new GetUserBeatmapsRequest(User.Value.Id, type, pagination); protected override Drawable CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0 diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 281bcc3a03..06de0f62dc 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override int GetCount(APIUser user) => user.BeatmapPlayCountsCount; - protected override APIRequest> CreateRequest(Pagination pagination) => + protected override APIRequest> CreateRequest(PaginationParameters pagination) => new GetUserMostPlayedBeatmapsRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap mostPlayed) => diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs index 5aada2b655..9af854e6b9 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { } - protected override APIRequest> CreateRequest(Pagination pagination) + protected override APIRequest> CreateRequest(PaginationParameters pagination) => new GetUserKudosuHistoryRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIKudosuHistory item) => new DrawableKudosuHistoryItem(item); diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index a1f9e7546a..33bd155d71 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Profile.Sections [Resolved] private IAPIProvider api { get; set; } - protected Pagination? CurrentPage { get; private set; } + protected PaginationParameters? CurrentPage { get; private set; } protected ReverseChildIDFillFlowContainer ItemsContainer { get; private set; } @@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Profile.Sections { loadCancellation = new CancellationTokenSource(); - CurrentPage = CurrentPage?.TakeNext(ItemsPerPage) ?? new Pagination(InitialItemsCount); + CurrentPage = CurrentPage?.TakeNext(ItemsPerPage) ?? new PaginationParameters(InitialItemsCount); retrievalRequest = CreateRequest(CurrentPage.Value); retrievalRequest.Success += UpdateItems; @@ -151,7 +151,7 @@ namespace osu.Game.Overlays.Profile.Sections { } - protected abstract APIRequest> CreateRequest(Pagination pagination); + protected abstract APIRequest> CreateRequest(PaginationParameters pagination); protected abstract Drawable CreateDrawableItem(TModel model); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index c3a5e3f8b8..ef9f4b5ff3 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks base.OnItemsReceived(items); } - protected override APIRequest> CreateRequest(Pagination pagination) => + protected override APIRequest> CreateRequest(PaginationParameters pagination) => new GetUserScoresRequest(User.Value.Id, type, pagination); private int drawableItemIndex; diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index e5118094ce..77008d5f34 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent ItemsContainer.Spacing = new Vector2(0, 8); } - protected override APIRequest> CreateRequest(Pagination pagination) => + protected override APIRequest> CreateRequest(PaginationParameters pagination) => new GetUserRecentActivitiesRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIRecentActivity model) => new DrawableRecentActivity(model); From 83e89b3e80c737b626fe1962d2652a64a381058e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 11:10:11 +0900 Subject: [PATCH 0391/1276] Add unread count to `INotificationOverlay` and fix toolbar button DI logic --- .../Visual/Menus/TestSceneToolbar.cs | 18 +++++++++++++----- osu.Game/Overlays/INotificationOverlay.cs | 6 ++++++ osu.Game/Overlays/NotificationOverlay.cs | 6 ++++-- .../Toolbar/ToolbarNotificationButton.cs | 8 ++++---- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs index c65595d82e..dbc7e54b5e 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using Moq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -22,6 +23,17 @@ namespace osu.Game.Tests.Visual.Menus [Resolved] private IRulesetStore rulesets { get; set; } + private readonly Mock notifications = new Mock(); + + private readonly BindableInt unreadNotificationCount = new BindableInt(); + + [BackgroundDependencyLoader] + private void load() + { + Dependencies.CacheAs(notifications.Object); + notifications.SetupGet(n => n.UnreadCount).Returns(unreadNotificationCount); + } + [SetUp] public void SetUp() => Schedule(() => { @@ -31,10 +43,6 @@ namespace osu.Game.Tests.Visual.Menus [Test] public void TestNotificationCounter() { - ToolbarNotificationButton notificationButton = null; - - AddStep("retrieve notification button", () => notificationButton = toolbar.ChildrenOfType().Single()); - setNotifications(1); setNotifications(2); setNotifications(3); @@ -43,7 +51,7 @@ namespace osu.Game.Tests.Visual.Menus void setNotifications(int count) => AddStep($"set notification count to {count}", - () => notificationButton.NotificationCount.Value = count); + () => unreadNotificationCount.Value = count); } [TestCase(false)] diff --git a/osu.Game/Overlays/INotificationOverlay.cs b/osu.Game/Overlays/INotificationOverlay.cs index 432dd2be54..1d8e33ea3a 100644 --- a/osu.Game/Overlays/INotificationOverlay.cs +++ b/osu.Game/Overlays/INotificationOverlay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Game.Overlays.Notifications; namespace osu.Game.Overlays @@ -22,5 +23,10 @@ namespace osu.Game.Overlays /// Hide the overlay, if it is currently visible. /// void Hide(); + + /// + /// Current number of unread notifications. + /// + IBindable UnreadCount { get; } } } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 0e8fbc6680..2b5909f45a 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -99,7 +99,9 @@ namespace osu.Game.Overlays OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true); } - public readonly BindableInt UnreadCount = new BindableInt(); + public IBindable UnreadCount => unreadCount; + + private readonly BindableInt unreadCount = new BindableInt(); private int runningDepth; @@ -180,7 +182,7 @@ namespace osu.Game.Overlays private void updateCounts() { - UnreadCount.Value = sections.Select(c => c.UnreadCount).Sum(); + unreadCount.Value = sections.Select(c => c.UnreadCount).Sum(); } private void markAllRead() diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs index 79d0fc74c1..313a2bc3f4 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Toolbar { protected override Anchor TooltipAnchor => Anchor.TopRight; - public BindableInt NotificationCount = new BindableInt(); + public IBindable NotificationCount = new BindableInt(); private readonly CountCircle countDisplay; @@ -36,10 +36,10 @@ namespace osu.Game.Overlays.Toolbar }); } - [BackgroundDependencyLoader(true)] - private void load(NotificationOverlay notificationOverlay) + [BackgroundDependencyLoader] + private void load(INotificationOverlay notificationOverlay) { - StateContainer = notificationOverlay; + StateContainer = notificationOverlay as NotificationOverlay; if (notificationOverlay != null) NotificationCount.BindTo(notificationOverlay.UnreadCount); From 8d0dd3961e056b2670f67412a48cfdf000ec1694 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 03:25:18 +0300 Subject: [PATCH 0392/1276] Add failing test cases --- .../LegacyMainCirclePieceTest.cs | 133 ++++++++++++++++++ .../Skinning/Legacy/LegacyMainCirclePiece.cs | 19 ++- 2 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs diff --git a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs new file mode 100644 index 0000000000..200dd2f593 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs @@ -0,0 +1,133 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Testing; +using osu.Game.Audio; +using osu.Game.Rulesets.Osu.Skinning.Legacy; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; + +#nullable enable + +namespace osu.Game.Rulesets.Osu.Tests +{ + [HeadlessTest] + public class LegacyMainCirclePieceTest : OsuTestScene + { + private static readonly object?[][] texture_priority_cases = + { + // default priority lookup + new object?[] + { + // available textures + new[] { @"hitcircle", @"hitcircleoverlay" }, + // priority lookup + @"", + // expected circle and overlay + @"hitcircle", @"hitcircleoverlay", + }, + // custom priority lookup + new object?[] + { + new[] { @"hitcircle", @"hitcircleoverlay", @"sliderstartcircle", @"sliderstartcircleoverlay" }, + @"sliderstartcircle", + @"sliderstartcircle", @"sliderstartcircleoverlay", + }, + // when no sprites are available for the specified prefix, fall back to "hitcircle"/"hitcircleoverlay". + new object?[] + { + new[] { @"hitcircle", @"hitcircleoverlay" }, + @"sliderstartcircle", + @"hitcircle", @"hitcircleoverlay", + }, + // when a circle is available for the specified prefix but no overlay exists, no overlay is displayed. + new object?[] + { + new[] { @"hitcircle", @"hitcircleoverlay", @"sliderstartcircle" }, + @"sliderstartcircle", + @"sliderstartcircle", null + }, + // when no circle is available for the specified prefix but an overlay exists, the overlay is ignored. + new object?[] + { + new[] { @"hitcircle", @"hitcircleoverlay", @"sliderstartcircleoverlay" }, + @"sliderstartcircle", + @"hitcircle", @"hitcircleoverlay", + } + }; + + [TestCaseSource(nameof(texture_priority_cases))] + public void TestTexturePriorities(string[] textureFilenames, string priorityLookup, string? expectedCircle, string? expectedOverlay) + { + Sprite? circleSprite = null; + Sprite? overlaySprite = null; + + AddStep("load circle piece", () => + { + Child = new DependencyProvidingContainer + { + CachedDependencies = new (Type, object)[] + { + (typeof(ISkinSource), new TestSkin(textureFilenames)) + }, + Child = new LegacyMainCirclePiece(priorityLookup, false), + }; + + var sprites = this.ChildrenOfType().Where(s => s.Texture.AssetName != null).DistinctBy(s => s.Texture.AssetName).ToArray(); + Debug.Assert(sprites.Length <= 2); + + circleSprite = sprites.ElementAtOrDefault(0); + overlaySprite = sprites.ElementAtOrDefault(1); + }); + + AddAssert("check circle sprite", () => circleSprite?.Texture?.AssetName == expectedCircle); + AddAssert("check overlay sprite", () => overlaySprite?.Texture?.AssetName == expectedOverlay); + } + + private class TestSkin : ISkinSource + { + private readonly string[] textureFilenames; + + public TestSkin(string[] textureFilenames) + { + this.textureFilenames = textureFilenames; + } + + public Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) + { + if (textureFilenames.Contains(componentName)) + return new Texture(1, 1) { AssetName = componentName }; + + return null; + } + + public event Action SourceChanged + { + add { } + remove { } + } + + public IEnumerable AllSources { get; } = Enumerable.Empty(); + public Drawable? GetDrawableComponent(ISkinComponent component) => null; + public ISample? GetSample(ISampleInfo sampleInfo) => null; + + public IBindable? GetConfig(TLookup lookup) + where TLookup : notnull + where TValue : notnull + => null; + + public ISkin? FindProvider(Func lookupFunction) => lookupFunction(this) ? this : null; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index c6007885be..829dbb02f1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -43,7 +44,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly Bindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); - [Resolved] + [Resolved(canBeNull: true)] + [CanBeNull] private DrawableHitObject drawableObject { get; set; } [Resolved] @@ -107,8 +109,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (overlayAboveNumber) OverlayLayer.ChangeChildDepth(hitCircleOverlay, float.MinValue); - accentColour.BindTo(drawableObject.AccentColour); - indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); + if (drawableOsuObject != null) + { + accentColour.BindTo(drawableOsuObject.AccentColour); + indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); + } Texture getTextureWithFallback(string name) { @@ -149,15 +154,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (hasNumber) indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); - drawableObject.ApplyCustomUpdateState += updateStateTransforms; - updateStateTransforms(drawableObject, drawableObject.State.Value); + if (drawableObject != null) + drawableObject.ApplyCustomUpdateState += updateStateTransforms; + + updateStateTransforms(drawableObject, drawableObject?.State.Value ?? ArmedState.Idle); } private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { const double legacy_fade_duration = 240; - using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) + using (BeginAbsoluteSequence(drawableObject?.HitStateUpdateTime ?? 0)) { switch (state) { From ec7bb876b5f76416255fa04e71876f6f39980465 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 07:12:05 +0300 Subject: [PATCH 0393/1276] Improve legacy circle texture lookup to match 1:1 with stable --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 49 +++++-------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 829dbb02f1..5fe4a4b5df 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -23,9 +23,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public override bool RemoveCompletedTransforms => false; - private readonly string priorityLookup; private readonly bool hasNumber; + private string priorityLookup; + public LegacyMainCirclePiece(string priorityLookup = null, bool hasNumber = true) { this.priorityLookup = priorityLookup; @@ -56,21 +57,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { var drawableOsuObject = (DrawableOsuHitObject)drawableObject; - bool allowFallback = false; - // attempt lookup using priority specification - Texture baseTexture = getTextureWithFallback(string.Empty); + Texture baseTexture = getTexture(string.Empty); - // if the base texture was not found without a fallback, switch on fallback mode and re-perform the lookup. + // if the base texture was not found using the priority specification, nullify the specification and fall back to "hitcircle". if (baseTexture == null) { - allowFallback = true; - baseTexture = getTextureWithFallback(string.Empty); + priorityLookup = null; + baseTexture = getTexture(string.Empty); } // at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it. // the flow above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist. - // expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin). + // expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png. InternalChildren = new[] { @@ -83,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Child = hitCircleOverlay = new KiaiFlashingDrawable(() => getAnimationWithFallback(@"overlay", 1000 / 2d)) + Child = hitCircleOverlay = new KiaiFlashingDrawable(() => getAnimation(@"overlay", 1000 / 2d)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -115,35 +114,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); } - Texture getTextureWithFallback(string name) - { - Texture tex = null; + Texture getTexture(string name) + => skin.GetTexture($"{priorityLookup ?? @"hitcircle"}{name}"); - if (!string.IsNullOrEmpty(priorityLookup)) - { - tex = skin.GetTexture($"{priorityLookup}{name}"); - - if (!allowFallback) - return tex; - } - - return tex ?? skin.GetTexture($"hitcircle{name}"); - } - - Drawable getAnimationWithFallback(string name, double frameLength) - { - Drawable animation = null; - - if (!string.IsNullOrEmpty(priorityLookup)) - { - animation = skin.GetAnimation($"{priorityLookup}{name}", true, true, frameLength: frameLength); - - if (!allowFallback) - return animation; - } - - return animation ?? skin.GetAnimation($"hitcircle{name}", true, true, frameLength: frameLength); - } + Drawable getAnimation(string name, double frameLength) + => skin.GetAnimation($"{priorityLookup ?? @"hitcircle"}{name}", true, true, frameLength: frameLength); } protected override void LoadComplete() From f92a41b6c3758027e315347fafb01abbac7aa678 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 08:03:50 +0300 Subject: [PATCH 0394/1276] Replace local `TestSkin` implementation with simplified Moq setups --- .../LegacyMainCirclePieceTest.cs | 56 ++++--------------- 1 file changed, 10 insertions(+), 46 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs index 200dd2f593..766db25d71 100644 --- a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs @@ -1,25 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Moq; using NUnit.Framework; -using osu.Framework.Audio.Sample; -using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Testing; -using osu.Game.Audio; using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Skinning; using osu.Game.Tests.Visual; -#nullable enable - namespace osu.Game.Rulesets.Osu.Tests { [HeadlessTest] @@ -75,12 +71,15 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("load circle piece", () => { + var skin = new Mock(); + + skin.Setup(s => s.GetTexture(It.IsAny())).CallBase(); + skin.Setup(s => s.GetTexture(It.IsIn(textureFilenames), It.IsAny(), It.IsAny())) + .Returns((string componentName, WrapMode _, WrapMode __) => new Texture(1, 1) { AssetName = componentName }); + Child = new DependencyProvidingContainer { - CachedDependencies = new (Type, object)[] - { - (typeof(ISkinSource), new TestSkin(textureFilenames)) - }, + CachedDependencies = new (Type, object)[] { (typeof(ISkinSource), skin.Object) }, Child = new LegacyMainCirclePiece(priorityLookup, false), }; @@ -94,40 +93,5 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("check circle sprite", () => circleSprite?.Texture?.AssetName == expectedCircle); AddAssert("check overlay sprite", () => overlaySprite?.Texture?.AssetName == expectedOverlay); } - - private class TestSkin : ISkinSource - { - private readonly string[] textureFilenames; - - public TestSkin(string[] textureFilenames) - { - this.textureFilenames = textureFilenames; - } - - public Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) - { - if (textureFilenames.Contains(componentName)) - return new Texture(1, 1) { AssetName = componentName }; - - return null; - } - - public event Action SourceChanged - { - add { } - remove { } - } - - public IEnumerable AllSources { get; } = Enumerable.Empty(); - public Drawable? GetDrawableComponent(ISkinComponent component) => null; - public ISample? GetSample(ISampleInfo sampleInfo) => null; - - public IBindable? GetConfig(TLookup lookup) - where TLookup : notnull - where TValue : notnull - => null; - - public ISkin? FindProvider(Func lookupFunction) => lookupFunction(this) ? this : null; - } } } From fd113953ace591b867081da881821f15dd356511 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 08:06:39 +0300 Subject: [PATCH 0395/1276] Rename `prioritiyLookup` and add xmldoc --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 5fe4a4b5df..82bb4dcf2d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -25,11 +25,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly bool hasNumber; - private string priorityLookup; + /// + /// A prioritised prefix to perform texture lookups with. + /// + /// + /// If the "circle" texture could not be found with this prefix, + /// then it is nullified and the default prefix "hitcircle" is used instead. + /// + private string priorityLookupPrefix; - public LegacyMainCirclePiece(string priorityLookup = null, bool hasNumber = true) + public LegacyMainCirclePiece(string priorityLookupPrefix = null, bool hasNumber = true) { - this.priorityLookup = priorityLookup; + this.priorityLookupPrefix = priorityLookupPrefix; this.hasNumber = hasNumber; Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); @@ -63,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy // if the base texture was not found using the priority specification, nullify the specification and fall back to "hitcircle". if (baseTexture == null) { - priorityLookup = null; + priorityLookupPrefix = null; baseTexture = getTexture(string.Empty); } @@ -115,10 +122,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } Texture getTexture(string name) - => skin.GetTexture($"{priorityLookup ?? @"hitcircle"}{name}"); + => skin.GetTexture($"{priorityLookupPrefix ?? @"hitcircle"}{name}"); Drawable getAnimation(string name, double frameLength) - => skin.GetAnimation($"{priorityLookup ?? @"hitcircle"}{name}", true, true, frameLength: frameLength); + => skin.GetAnimation($"{priorityLookupPrefix ?? @"hitcircle"}{name}", true, true, frameLength: frameLength); } protected override void LoadComplete() From b067924adacb6ecdc38d3d9ccdf52a30b44458f7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 08:08:02 +0300 Subject: [PATCH 0396/1276] Avoid applying state transforms when no object is present --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 82bb4dcf2d..9d14041dd8 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -4,6 +4,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -137,16 +138,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); if (drawableObject != null) + { drawableObject.ApplyCustomUpdateState += updateStateTransforms; - - updateStateTransforms(drawableObject, drawableObject?.State.Value ?? ArmedState.Idle); + updateStateTransforms(drawableObject, drawableObject.State.Value); + } } private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { const double legacy_fade_duration = 240; - using (BeginAbsoluteSequence(drawableObject?.HitStateUpdateTime ?? 0)) + using (BeginAbsoluteSequence(drawableObject.AsNonNull().HitStateUpdateTime)) { switch (state) { From feeff1647697ef1c45156091cce88da20464caee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 14:30:45 +0900 Subject: [PATCH 0397/1276] Add debug language to help with localisation efforts The idea is to allow a developer to immediately see which text on a component or screen has already got localisation support. It can be a bit of a challenge to see this when creating a new component that doesn't yet have any translations populated. Curious to hear thoughts on this. I could see it working very well as a visual tests checkbox (implemented at o!f side), potentially in addition to having this at the game level, or replacing this PR. --- .../Localisation/DebugLocalisationStore.cs | 30 +++++++++++++++++++ osu.Game/Localisation/Language.cs | 7 ++++- osu.Game/OsuGame.cs | 8 +++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Localisation/DebugLocalisationStore.cs diff --git a/osu.Game/Localisation/DebugLocalisationStore.cs b/osu.Game/Localisation/DebugLocalisationStore.cs new file mode 100644 index 0000000000..99dca6265e --- /dev/null +++ b/osu.Game/Localisation/DebugLocalisationStore.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public class DebugLocalisationStore : ILocalisationStore + { + public string Get(string lookup) => $@"[[{lookup.Substring(lookup.LastIndexOf('.') + 1)}]]"; + + public Task GetAsync(string lookup, CancellationToken cancellationToken = default) => Task.FromResult(Get(lookup)); + + public Stream GetStream(string name) => throw new NotImplementedException(); + + public IEnumerable GetAvailableResources() => throw new NotImplementedException(); + + public CultureInfo EffectiveCulture { get; } = new CultureInfo(@"debug"); + + public void Dispose() + { + } + } +} diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index ab96d01c82..c98586457e 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -110,6 +110,11 @@ namespace osu.Game.Localisation // zh_hk, [Description(@"繁體中文(台灣)")] - zh_hant + zh_hant, + +#if DEBUG + [Description(@"Debug (show raw keys)")] + DebugLocalisation +#endif } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index eee86de7ff..b591b09584 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -628,6 +628,14 @@ namespace osu.Game foreach (var language in Enum.GetValues(typeof(Language)).OfType()) { +#if DEBUG + if (language == Language.DebugLocalisation) + { + Localisation.AddLanguage(Language.DebugLocalisation.ToString(), new DebugLocalisationStore()); + continue; + } +#endif + string cultureCode = language.ToCultureCode(); try From ae07b2b512b61ff86bd36632d67c6f01664c8d3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 17:42:10 +0900 Subject: [PATCH 0398/1276] Initial setup --- .../Visual/Navigation/TestFirstRunSetup.cs | 15 ++ osu.Game/OsuGame.cs | 3 + .../FirstRunSetup/FirstRunSetupScreen.cs | 51 +++++++ .../Overlays/FirstRunSetup/ScreenWelcome.cs | 58 ++++++++ osu.Game/Overlays/FirstRunSetupOverlay.cs | 134 ++++++++++++++++++ 5 files changed, 261 insertions(+) create mode 100644 osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs create mode 100644 osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs create mode 100644 osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs create mode 100644 osu.Game/Overlays/FirstRunSetupOverlay.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs b/osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs new file mode 100644 index 0000000000..ab49334af1 --- /dev/null +++ b/osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; + +namespace osu.Game.Tests.Visual.Navigation +{ + public class TestFirstRunSetup : OsuGameTestScene + { + [Test] + public void TestOverlay() + { + } + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5a150b0c49..52aa6b8f07 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -149,6 +149,8 @@ namespace osu.Game protected SettingsOverlay Settings; + private FirstRunSetupOverlay firstRunOverlay; + private VolumeOverlay volume; private OsuLogo osuLogo; @@ -791,6 +793,7 @@ namespace osu.Game loadComponentSingleFile(CreateUpdateManager(), Add, true); // overlay elements + loadComponentSingleFile(firstRunOverlay = new FirstRunSetupOverlay(), overlayContent.Add, true); loadComponentSingleFile(new ManageCollectionsDialog(), overlayContent.Add, true); loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true); diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs new file mode 100644 index 0000000000..d00d21d347 --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -0,0 +1,51 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osu.Framework.Screens; + +namespace osu.Game.Overlays.FirstRunSetup +{ + public abstract class FirstRunSetupScreen : Screen + { + [Resolved] + protected FirstRunSetupOverlay Overlay { get; private set; } + + protected Container Content { get; private set; } + + protected FirstRunSetupScreen() + { + InternalChildren = new Drawable[] + { + Content = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(20), + }, + }; + } + + protected override bool OnClick(ClickEvent e) => true; + + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + this.FadeOut().Delay(200).FadeIn(200); + } + + public override void OnResuming(IScreen last) + { + base.OnResuming(last); + this.FadeIn(200); + } + + public override void OnSuspending(IScreen next) + { + base.OnSuspending(next); + this.FadeOut(200); + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs new file mode 100644 index 0000000000..be21712c95 --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Screens; +using osu.Framework.Utils; +using osu.Game.Graphics.Containers; +using osu.Game.Screens.OnlinePlay.Match.Components; +using osuTK; + +namespace osu.Game.Overlays.FirstRunSetup +{ + public class ScreenWelcome : FirstRunSetupScreen + { + public ScreenWelcome() + { + Content.Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuTextFlowContainer + { + Text = "Welcome to the first-run setup guide!\n\nThis will help you get osu! setup in a way that suits you.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + } + }, + new PurpleTriangleButton + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding(10), + Text = "Get started", + Action = () => this.Push(new ScreenWelcome()), + } + }; + } + + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + Overlay.MoveDisplayTo(new Vector2(RNG.NextSingle(), RNG.NextSingle())); + } + + public override void OnResuming(IScreen last) + { + base.OnResuming(last); + Overlay.MoveDisplayTo(new Vector2(RNG.NextSingle(), RNG.NextSingle())); + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs new file mode 100644 index 0000000000..218e6018e2 --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -0,0 +1,134 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Framework.Screens; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Online.API; +using osu.Game.Overlays.Dialog; +using osu.Game.Overlays.FirstRunSetup; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Menu; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays +{ + [Cached] + public class FirstRunSetupOverlay : OsuFocusedOverlayContainer + { + protected override bool StartHidden => true; + + [Resolved] + private DialogOverlay dialogOverlay { get; set; } + + [Resolved] + private OsuGame osuGame { get; set; } + + private ScreenWelcome welcomeScreen; + + private Container currentDisplayContainer; + + private PlayfieldBorder border; + + public FirstRunSetupOverlay() + { + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api, OsuColour colours) + { + Children = new Drawable[] + { + border = new PlayfieldBorder + { + PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Full }, + Colour = colours.Blue, + }, + currentDisplayContainer = new Container + { + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Both, + Size = new Vector2(400, 300), + Position = new Vector2(0.5f), + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 5, + Colour = Color4.Black.Opacity(0.2f), + }, + Masking = true, + CornerRadius = 10, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.8f, + }, + new ScreenStack(welcomeScreen = new ScreenWelcome()) + { + RelativeSizeAxes = Axes.Both, + }, + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // if we are valid for display, only do so after reaching the main menu. + osuGame.PerformFromScreen(_ => + { + Show(); + }, new[] { typeof(MainMenu) }); + + border + .FadeInFromZero(500) + .Delay(1000) + .FadeOut(500) + .Loop(); + } + + protected override bool OnClick(ClickEvent e) + { + if (dialogOverlay.CurrentDialog == null) + { + dialogOverlay.Push(new ConfirmDialog("Are you sure you want to exit the setup process?", + Hide, + () => { })); + } + + return base.OnClick(e); + } + + protected override void PopIn() + { + base.PopIn(); + this.FadeIn(400, Easing.OutQuint); + + if (welcomeScreen.GetChildScreen() != null) + welcomeScreen.MakeCurrent(); + } + + protected override void PopOut() + { + base.PopOut(); + this.FadeOut(100); + } + + public void MoveDisplayTo(Vector2 position) => + currentDisplayContainer.MoveTo(position, 1000, Easing.OutElasticQuarter); + } +} From c2df3465b281fe8523e51379785780005cb98041 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 18:11:32 +0900 Subject: [PATCH 0399/1276] Add ui scaling setup screen, kind of --- .../FirstRunSetup/ScreenSetupUIScale.cs | 87 +++++++++++++++++++ .../Overlays/FirstRunSetup/ScreenWelcome.cs | 11 +-- osu.Game/Overlays/FirstRunSetupOverlay.cs | 6 +- 3 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs new file mode 100644 index 0000000000..7c703f65bd --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs @@ -0,0 +1,87 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; +using osu.Framework.Screens; +using osu.Game.Configuration; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; +using osu.Game.Overlays.Settings; +using osu.Game.Screens; +using osu.Game.Screens.OnlinePlay.Match.Components; +using osu.Game.Screens.Select; +using osuTK; + +namespace osu.Game.Overlays.FirstRunSetup +{ + public class ScreenSetupUIScale : FirstRunSetupScreen + { + [Resolved] + private OsuConfigManager osuConfig { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + OsuScreenStack stack; + Content.Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(20), + Children = new Drawable[] + { + new OsuTextFlowContainer + { + Text = "The osu! user interface size can be adjusted to your liking.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + new SettingsSlider + { + LabelText = GraphicsSettingsStrings.UIScaling, + TransferValueOnCommit = true, + Current = osuConfig.GetBindable(OsuSetting.UIScale), + KeyboardStep = 0.01f, + }, + new DrawSizePreservingFillContainer + { + Masking = true, + RelativeSizeAxes = Axes.X, + Height = 300, + Child = stack = new OsuScreenStack() + } + } + }, + new PurpleTriangleButton + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding(10), + Text = "Finish", + Action = () => Overlay.Hide() + } + }; + + stack.Push(new PlaySongSelect()); + } + + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + Overlay.MoveDisplayTo(new Vector2(0.5f)); + Overlay.ResizeDisplayTo(new Vector2(0.8f)); + } + + private class UIScaleSlider : OsuSliderBar + { + public override LocalisableString TooltipText => base.TooltipText + "x"; + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs index be21712c95..b436db2431 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -4,7 +4,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; -using osu.Framework.Utils; using osu.Game.Graphics.Containers; using osu.Game.Screens.OnlinePlay.Match.Components; using osuTK; @@ -38,7 +37,7 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.X, Margin = new MarginPadding(10), Text = "Get started", - Action = () => this.Push(new ScreenWelcome()), + Action = () => this.Push(new ScreenSetupUIScale()), } }; } @@ -46,13 +45,7 @@ namespace osu.Game.Overlays.FirstRunSetup public override void OnEntering(IScreen last) { base.OnEntering(last); - Overlay.MoveDisplayTo(new Vector2(RNG.NextSingle(), RNG.NextSingle())); - } - - public override void OnResuming(IScreen last) - { - base.OnResuming(last); - Overlay.MoveDisplayTo(new Vector2(RNG.NextSingle(), RNG.NextSingle())); + Overlay.MoveDisplayTo(new Vector2(0.5f)); } } } diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 218e6018e2..1578ef8cbf 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -57,7 +57,8 @@ namespace osu.Game.Overlays { Origin = Anchor.Centre, RelativePositionAxes = Axes.Both, - Size = new Vector2(400, 300), + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), Position = new Vector2(0.5f), EdgeEffect = new EdgeEffectParameters { @@ -130,5 +131,8 @@ namespace osu.Game.Overlays public void MoveDisplayTo(Vector2 position) => currentDisplayContainer.MoveTo(position, 1000, Easing.OutElasticQuarter); + + public void ResizeDisplayTo(Vector2 scale) => + currentDisplayContainer.ScaleTo(scale, 1000, Easing.OutElasticQuarter); } } From e064f2f23efac42cca67225d59854852edb61092 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 15:20:54 +0900 Subject: [PATCH 0400/1276] Improve general layout and allow overlay to be displayed without dependencies for now --- .../Visual/Navigation/TestFirstRunSetup.cs | 7 +- .../Overlays/FirstRunSetup/ScreenWelcome.cs | 13 +- osu.Game/Overlays/FirstRunSetupOverlay.cs | 118 ++++++++++++------ 3 files changed, 91 insertions(+), 47 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs b/osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs index ab49334af1..9855865284 100644 --- a/osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs +++ b/osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs @@ -2,14 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Game.Overlays; namespace osu.Game.Tests.Visual.Navigation { - public class TestFirstRunSetup : OsuGameTestScene + public class TestFirstRunSetup : OsuTestScene { [Test] public void TestOverlay() { + AddStep("add overlay", () => + { + Child = new FirstRunSetupOverlay(); + }); } } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs index b436db2431..f67ab1cd7f 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -4,9 +4,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Screens.OnlinePlay.Match.Components; -using osuTK; namespace osu.Game.Overlays.FirstRunSetup { @@ -22,9 +22,10 @@ namespace osu.Game.Overlays.FirstRunSetup Direction = FillDirection.Vertical, Children = new Drawable[] { - new OsuTextFlowContainer + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Text = "Welcome to the first-run setup guide!\n\nThis will help you get osu! setup in a way that suits you.", + Text = + "Welcome to the first-run setup guide!\n\nosu! is a very configurable game, and diving straight into the settings can sometimes be overwhelming. This guide will help you get the important choices out of the way to ensure a great first experience!", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -41,11 +42,5 @@ namespace osu.Game.Overlays.FirstRunSetup } }; } - - public override void OnEntering(IScreen last) - { - base.OnEntering(last); - Overlay.MoveDisplayTo(new Vector2(0.5f)); - } } } diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 1578ef8cbf..d213f87fe5 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -11,10 +11,10 @@ using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Overlays.Dialog; using osu.Game.Overlays.FirstRunSetup; -using osu.Game.Rulesets.UI; using osu.Game.Screens.Menu; using osuTK; using osuTK.Graphics; @@ -26,18 +26,17 @@ namespace osu.Game.Overlays { protected override bool StartHidden => true; - [Resolved] + [Resolved(canBeNull: true)] private DialogOverlay dialogOverlay { get; set; } - [Resolved] + [Resolved(canBeNull: true)] private OsuGame osuGame { get; set; } + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + private ScreenWelcome welcomeScreen; - private Container currentDisplayContainer; - - private PlayfieldBorder border; - public FirstRunSetupOverlay() { RelativeSizeAxes = Axes.Both; @@ -48,18 +47,12 @@ namespace osu.Game.Overlays { Children = new Drawable[] { - border = new PlayfieldBorder + new Container { - PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Full }, - Colour = colours.Blue, - }, - currentDisplayContainer = new Container - { - Origin = Anchor.Centre, - RelativePositionAxes = Axes.Both, RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.5f), - Position = new Vector2(0.5f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.95f), EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, @@ -73,12 +66,68 @@ namespace osu.Game.Overlays new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.8f, + Colour = colourProvider.Background5, }, - new ScreenStack(welcomeScreen = new ScreenWelcome()) + new GridContainer { RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + Colour = colourProvider.Background6, + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding(10), + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "First run setup", + Font = OsuFont.Default.With(size: 32), + Colour = colourProvider.Content2, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + new OsuTextFlowContainer + { + Text = "Setup osu! to suit you", + Colour = colourProvider.Content1, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + }, + } + }, + } + }, + }, + new Drawable[] + { + new ScreenStack(welcomeScreen = new ScreenWelcome()) + { + RelativeSizeAxes = Axes.Both, + }, + } + } }, } } @@ -89,24 +138,25 @@ namespace osu.Game.Overlays { base.LoadComplete(); - // if we are valid for display, only do so after reaching the main menu. - osuGame.PerformFromScreen(_ => + if (osuGame != null) + { + // if we are valid for display, only do so after reaching the main menu. + osuGame.PerformFromScreen(_ => + { + Show(); + }, new[] { typeof(MainMenu) }); + } + else { Show(); - }, new[] { typeof(MainMenu) }); - - border - .FadeInFromZero(500) - .Delay(1000) - .FadeOut(500) - .Loop(); + } } protected override bool OnClick(ClickEvent e) { - if (dialogOverlay.CurrentDialog == null) + if (dialogOverlay?.CurrentDialog == null) { - dialogOverlay.Push(new ConfirmDialog("Are you sure you want to exit the setup process?", + dialogOverlay?.Push(new ConfirmDialog("Are you sure you want to exit the setup process?", Hide, () => { })); } @@ -128,11 +178,5 @@ namespace osu.Game.Overlays base.PopOut(); this.FadeOut(100); } - - public void MoveDisplayTo(Vector2 position) => - currentDisplayContainer.MoveTo(position, 1000, Easing.OutElasticQuarter); - - public void ResizeDisplayTo(Vector2 scale) => - currentDisplayContainer.ScaleTo(scale, 1000, Easing.OutElasticQuarter); } } From 3b94e01fe64ad1094e895f6822444c36b45cf042 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 15:21:09 +0900 Subject: [PATCH 0401/1276] Improve layout of ui scale step --- .../FirstRunSetup/ScreenSetupUIScale.cs | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs index 7c703f65bd..1a8ef0338c 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs @@ -5,13 +5,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; -using osu.Framework.Screens; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Overlays.Settings; using osu.Game.Screens; +using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Select; using osuTK; @@ -23,6 +24,9 @@ namespace osu.Game.Overlays.FirstRunSetup [Resolved] private OsuConfigManager osuConfig { get; set; } + [Cached] + private OsuLogo osuLogo = new OsuLogo(); + [BackgroundDependencyLoader] private void load() { @@ -36,7 +40,7 @@ namespace osu.Game.Overlays.FirstRunSetup Spacing = new Vector2(20), Children = new Drawable[] { - new OsuTextFlowContainer + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 32)) { Text = "The osu! user interface size can be adjusted to your liking.", RelativeSizeAxes = Axes.X, @@ -49,12 +53,42 @@ namespace osu.Game.Overlays.FirstRunSetup Current = osuConfig.GetBindable(OsuSetting.UIScale), KeyboardStep = 0.01f, }, - new DrawSizePreservingFillContainer + new GridContainer { - Masking = true, - RelativeSizeAxes = Axes.X, - Height = 300, - Child = stack = new OsuScreenStack() + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new ScalingContainer(ScalingMode.Off) + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Child = stack = new OsuScreenStack() + }, + new ScalingContainer(ScalingMode.Off) + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Child = stack = new OsuScreenStack() + } + }, + new Drawable[] + { + new ScalingContainer(ScalingMode.Off) + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Child = stack = new OsuScreenStack() + }, + new ScalingContainer(ScalingMode.Off) + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Child = stack = new OsuScreenStack() + } + } + } } } }, @@ -72,13 +106,6 @@ namespace osu.Game.Overlays.FirstRunSetup stack.Push(new PlaySongSelect()); } - public override void OnEntering(IScreen last) - { - base.OnEntering(last); - Overlay.MoveDisplayTo(new Vector2(0.5f)); - Overlay.ResizeDisplayTo(new Vector2(0.8f)); - } - private class UIScaleSlider : OsuSliderBar { public override LocalisableString TooltipText => base.TooltipText + "x"; From 07da1cd731b7acf89337a066fd3ed677216c5e67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 16:07:30 +0900 Subject: [PATCH 0402/1276] Move buttons to top level and add backwards navigation --- .../FirstRunSetup/FirstRunSetupScreen.cs | 28 ++-- .../Overlays/FirstRunSetup/ScreenWelcome.cs | 13 +- osu.Game/Overlays/FirstRunSetupOverlay.cs | 128 +++++++++++++++++- 3 files changed, 133 insertions(+), 36 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index d00d21d347..5cf3d562a9 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Screens; @@ -14,38 +13,31 @@ namespace osu.Game.Overlays.FirstRunSetup [Resolved] protected FirstRunSetupOverlay Overlay { get; private set; } - protected Container Content { get; private set; } - - protected FirstRunSetupScreen() - { - InternalChildren = new Drawable[] - { - Content = new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(20), - }, - }; - } - protected override bool OnClick(ClickEvent e) => true; public override void OnEntering(IScreen last) { base.OnEntering(last); - this.FadeOut().Delay(200).FadeIn(200); + this + .FadeInFromZero(500) + .MoveToX(100) + .MoveToX(0, 500, Easing.OutQuint); } public override void OnResuming(IScreen last) { base.OnResuming(last); - this.FadeIn(200); + this + .FadeInFromZero(500) + .MoveToX(0, 500, Easing.OutQuint); } public override void OnSuspending(IScreen next) { base.OnSuspending(next); - this.FadeOut(200); + this + .FadeOut(100) + .MoveToX(-100, 500, Easing.OutQuint); } } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs index f67ab1cd7f..879ffdf198 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -3,10 +3,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Overlays.FirstRunSetup { @@ -14,7 +12,7 @@ namespace osu.Game.Overlays.FirstRunSetup { public ScreenWelcome() { - Content.Children = new Drawable[] + InternalChildren = new Drawable[] { new FillFlowContainer { @@ -31,15 +29,6 @@ namespace osu.Game.Overlays.FirstRunSetup }, } }, - new PurpleTriangleButton - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding(10), - Text = "Get started", - Action = () => this.Push(new ScreenSetupUIScale()), - } }; } } diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index d213f87fe5..4eb46c38f3 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -1,6 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + +using System; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -8,14 +12,17 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays.Dialog; using osu.Game.Overlays.FirstRunSetup; using osu.Game.Screens.Menu; +using osu.Game.Screens.OnlinePlay.Match.Components; using osuTK; using osuTK.Graphics; @@ -27,15 +34,26 @@ namespace osu.Game.Overlays protected override bool StartHidden => true; [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private DialogOverlay? dialogOverlay { get; set; } [Resolved(canBeNull: true)] - private OsuGame osuGame { get; set; } + private OsuGame? osuGame { get; set; } + + private ScreenStack stack = null!; + + private PurpleTriangleButton nextButton = null!; + private DangerousTriangleButton backButton = null!; [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - private ScreenWelcome welcomeScreen; + private int? currentStepIndex; + + private readonly FirstRunStep[] steps = + { + new FirstRunStep(typeof(ScreenWelcome), "Welcome"), + new FirstRunStep(typeof(ScreenSetupUIScale), "UI Scale"), + }; public FirstRunSetupOverlay() { @@ -75,6 +93,7 @@ namespace osu.Game.Overlays { new Dimension(GridSizeMode.AutoSize), new Dimension(), + new Dimension(GridSizeMode.AutoSize), }, Content = new[] { @@ -122,10 +141,60 @@ namespace osu.Game.Overlays }, new Drawable[] { - new ScreenStack(welcomeScreen = new ScreenWelcome()) + new Container { RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(20), + Child = stack = new ScreenStack + { + RelativeSizeAxes = Axes.Both, + }, }, + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(20), + Child = new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 10), + new Dimension(), + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new[] + { + backButton = new DangerousTriangleButton + { + Width = 200, + Text = "Back", + Action = showLastStep, + Enabled = { Value = false }, + }, + Empty(), + nextButton = new PurpleTriangleButton + { + RelativeSizeAxes = Axes.X, + Width = 1, + Text = "Get started", + Action = showNextStep + } + }, + } + }, + } } } }, @@ -169,8 +238,43 @@ namespace osu.Game.Overlays base.PopIn(); this.FadeIn(400, Easing.OutQuint); - if (welcomeScreen.GetChildScreen() != null) - welcomeScreen.MakeCurrent(); + if (currentStepIndex == null) + showNextStep(); + } + + private void showLastStep() + { + Debug.Assert(currentStepIndex > 0); + + stack.CurrentScreen.Exit(); + currentStepIndex--; + + backButton.Enabled.Value = currentStepIndex != 0; + } + + private void showNextStep() + { + if (currentStepIndex == null) + currentStepIndex = 0; + else + currentStepIndex++; + + Debug.Assert(currentStepIndex != null); + backButton.Enabled.Value = currentStepIndex > 0; + + if (currentStepIndex < steps.Length) + { + var nextStep = steps[currentStepIndex.Value]; + stack.Push((Screen)Activator.CreateInstance(nextStep.ScreenType)); + } + else + { + Hide(); + } + + nextButton.Text = currentStepIndex + 1 < steps.Length + ? $"Next ({steps[currentStepIndex.Value + 1].Description})" + : "Finish"; } protected override void PopOut() @@ -178,5 +282,17 @@ namespace osu.Game.Overlays base.PopOut(); this.FadeOut(100); } + + private class FirstRunStep + { + public readonly Type ScreenType; + public readonly LocalisableString Description; + + public FirstRunStep(Type screenType, LocalisableString description) + { + ScreenType = screenType; + Description = description; + } + } } } From 288f759bb4bd8480e4157faf6e1e3d4681028299 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 16:34:13 +0900 Subject: [PATCH 0403/1276] Add test coverage of navigation --- .../Visual/Navigation/TestFirstRunSetup.cs | 20 ------ .../TestSceneFirstRunSetupOverlay.cs | 63 +++++++++++++++++++ osu.Game/Overlays/FirstRunSetupOverlay.cs | 19 +++--- 3 files changed, 75 insertions(+), 27 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs create mode 100644 osu.Game.Tests/Visual/Navigation/TestSceneFirstRunSetupOverlay.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs b/osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs deleted file mode 100644 index 9855865284..0000000000 --- a/osu.Game.Tests/Visual/Navigation/TestFirstRunSetup.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Game.Overlays; - -namespace osu.Game.Tests.Visual.Navigation -{ - public class TestFirstRunSetup : OsuTestScene - { - [Test] - public void TestOverlay() - { - AddStep("add overlay", () => - { - Child = new FirstRunSetupOverlay(); - }); - } - } -} diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/Navigation/TestSceneFirstRunSetupOverlay.cs new file mode 100644 index 0000000000..c9aba315eb --- /dev/null +++ b/osu.Game.Tests/Visual/Navigation/TestSceneFirstRunSetupOverlay.cs @@ -0,0 +1,63 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Overlays; +using osu.Game.Overlays.FirstRunSetup; + +namespace osu.Game.Tests.Visual.Navigation +{ + public class TestSceneFirstRunSetupOverlay : OsuTestScene + { + private FirstRunSetupOverlay overlay; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("add overlay", () => + { + Child = overlay = new FirstRunSetupOverlay(); + }); + } + + [Test] + public void TestOverlayRunsToFinish() + { + AddUntilStep("step through", () => + { + if (overlay.CurrentScreen?.IsLoaded != false) + overlay.NextButton.TriggerClick(); + + return overlay.State.Value == Visibility.Hidden; + }); + } + + [Test] + public void TestBackButton() + { + AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value); + + AddUntilStep("step to last", () => + { + var nextButton = overlay.NextButton; + + if (overlay.CurrentScreen?.IsLoaded != false) + nextButton.TriggerClick(); + + return nextButton.Text.ToString() == "Finish"; + }); + + AddUntilStep("step back to start", () => + { + if (overlay.CurrentScreen?.IsLoaded != false) + overlay.BackButton.TriggerClick(); + + return overlay.CurrentScreen is ScreenWelcome; + }); + + AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value); + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 4eb46c38f3..bd6cb0b4d7 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -41,14 +41,19 @@ namespace osu.Game.Overlays private ScreenStack stack = null!; - private PurpleTriangleButton nextButton = null!; - private DangerousTriangleButton backButton = null!; + public PurpleTriangleButton NextButton = null!; + public DangerousTriangleButton BackButton = null!; [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); private int? currentStepIndex; + /// + /// The currently displayed screen, if any. + /// + public FirstRunSetupScreen? CurrentScreen => (FirstRunSetupScreen?)stack.CurrentScreen; + private readonly FirstRunStep[] steps = { new FirstRunStep(typeof(ScreenWelcome), "Welcome"), @@ -176,7 +181,7 @@ namespace osu.Game.Overlays { new[] { - backButton = new DangerousTriangleButton + BackButton = new DangerousTriangleButton { Width = 200, Text = "Back", @@ -184,7 +189,7 @@ namespace osu.Game.Overlays Enabled = { Value = false }, }, Empty(), - nextButton = new PurpleTriangleButton + NextButton = new PurpleTriangleButton { RelativeSizeAxes = Axes.X, Width = 1, @@ -249,7 +254,7 @@ namespace osu.Game.Overlays stack.CurrentScreen.Exit(); currentStepIndex--; - backButton.Enabled.Value = currentStepIndex != 0; + BackButton.Enabled.Value = currentStepIndex != 0; } private void showNextStep() @@ -260,7 +265,7 @@ namespace osu.Game.Overlays currentStepIndex++; Debug.Assert(currentStepIndex != null); - backButton.Enabled.Value = currentStepIndex > 0; + BackButton.Enabled.Value = currentStepIndex > 0; if (currentStepIndex < steps.Length) { @@ -272,7 +277,7 @@ namespace osu.Game.Overlays Hide(); } - nextButton.Text = currentStepIndex + 1 < steps.Length + NextButton.Text = currentStepIndex + 1 < steps.Length ? $"Next ({steps[currentStepIndex.Value + 1].Description})" : "Finish"; } From 56c4283764752f6a1eb2282df17e2db40196c794 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 17:10:08 +0900 Subject: [PATCH 0404/1276] Only exit dialog from click if outside the dialog content --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index bd6cb0b4d7..0efeded062 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -60,6 +60,8 @@ namespace osu.Game.Overlays new FirstRunStep(typeof(ScreenSetupUIScale), "UI Scale"), }; + private Container mainContent; + public FirstRunSetupOverlay() { RelativeSizeAxes = Axes.Both; @@ -70,7 +72,7 @@ namespace osu.Game.Overlays { Children = new Drawable[] { - new Container + mainContent = new Container { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -228,7 +230,7 @@ namespace osu.Game.Overlays protected override bool OnClick(ClickEvent e) { - if (dialogOverlay?.CurrentDialog == null) + if (!mainContent.IsHovered && dialogOverlay?.CurrentDialog == null) { dialogOverlay?.Push(new ConfirmDialog("Are you sure you want to exit the setup process?", Hide, From ea52fab5b1202268f43fadeb9f47cb7c516e3717 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 18:58:14 +0900 Subject: [PATCH 0405/1276] Tidy up dependencies and test naming --- .../TestSceneFirstRunSetupOverlay.cs | 63 -------- .../TestSceneFirstRunScreenUIScale.cs | 19 +++ .../TestSceneFirstRunSetupOverlay.cs | 111 +++++++++++++ .../FirstRunSetup/FirstRunSetupScreen.cs | 25 +-- .../FirstRunSetup/ScreenSetupUIScale.cs | 114 -------------- .../Overlays/FirstRunSetup/ScreenUIScale.cs | 146 ++++++++++++++++++ osu.Game/Overlays/FirstRunSetupOverlay.cs | 38 ++--- 7 files changed, 304 insertions(+), 212 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Navigation/TestSceneFirstRunSetupOverlay.cs create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs delete mode 100644 osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs create mode 100644 osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/Navigation/TestSceneFirstRunSetupOverlay.cs deleted file mode 100644 index c9aba315eb..0000000000 --- a/osu.Game.Tests/Visual/Navigation/TestSceneFirstRunSetupOverlay.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Framework.Graphics.Containers; -using osu.Framework.Testing; -using osu.Game.Overlays; -using osu.Game.Overlays.FirstRunSetup; - -namespace osu.Game.Tests.Visual.Navigation -{ - public class TestSceneFirstRunSetupOverlay : OsuTestScene - { - private FirstRunSetupOverlay overlay; - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("add overlay", () => - { - Child = overlay = new FirstRunSetupOverlay(); - }); - } - - [Test] - public void TestOverlayRunsToFinish() - { - AddUntilStep("step through", () => - { - if (overlay.CurrentScreen?.IsLoaded != false) - overlay.NextButton.TriggerClick(); - - return overlay.State.Value == Visibility.Hidden; - }); - } - - [Test] - public void TestBackButton() - { - AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value); - - AddUntilStep("step to last", () => - { - var nextButton = overlay.NextButton; - - if (overlay.CurrentScreen?.IsLoaded != false) - nextButton.TriggerClick(); - - return nextButton.Text.ToString() == "Finish"; - }); - - AddUntilStep("step back to start", () => - { - if (overlay.CurrentScreen?.IsLoaded != false) - overlay.BackButton.TriggerClick(); - - return overlay.CurrentScreen is ScreenWelcome; - }); - - AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value); - } - } -} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs new file mode 100644 index 0000000000..5ca09b34aa --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Screens; +using osu.Game.Overlays.FirstRunSetup; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneFirstRunScreenUIScale : OsuManualInputManagerTestScene + { + public TestSceneFirstRunScreenUIScale() + { + AddStep("load screen", () => + { + Child = new ScreenStack(new ScreenUIScale()); + }); + } + } +} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs new file mode 100644 index 0000000000..d892301a81 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -0,0 +1,111 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using Moq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Screens; +using osu.Framework.Testing; +using osu.Game.Overlays; +using osu.Game.Overlays.Dialog; +using osu.Game.Overlays.FirstRunSetup; +using osu.Game.Screens; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneFirstRunSetupOverlay : OsuManualInputManagerTestScene + { + private FirstRunSetupOverlay overlay; + + private readonly Mock perfomer = new Mock(); + private readonly Mock dialogOverlay = new Mock(); + + [BackgroundDependencyLoader] + private void load() + { + Dependencies.CacheAs(perfomer.Object); + Dependencies.CacheAs(dialogOverlay.Object); + + perfomer.Setup(g => g.PerformFromScreen(It.IsAny>(), It.IsAny>())) + .Callback((Action action, IEnumerable types) => action(null)); + + dialogOverlay.Setup(d => d.Push(It.IsAny())) + .Callback((PopupDialog dialog) => dialog.PerformOkAction()); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("add overlay", () => + { + Child = overlay = new FirstRunSetupOverlay + { + State = { Value = Visibility.Visible } + }; + }); + } + + [Test] + public void TestOverlayRunsToFinish() + { + AddUntilStep("step through", () => + { + if (overlay.CurrentScreen?.IsLoaded != false) + overlay.NextButton.TriggerClick(); + + return overlay.State.Value == Visibility.Hidden; + }); + } + + [Test] + public void TestBackButton() + { + AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value); + + AddUntilStep("step to last", () => + { + var nextButton = overlay.NextButton; + + if (overlay.CurrentScreen?.IsLoaded != false) + nextButton.TriggerClick(); + + return nextButton.Text.ToString() == "Finish"; + }); + + AddUntilStep("step back to start", () => + { + if (overlay.CurrentScreen?.IsLoaded != false) + overlay.BackButton.TriggerClick(); + + return overlay.CurrentScreen is ScreenWelcome; + }); + + AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value); + } + + [Test] + public void TestClickAwayToExit() + { + AddStep("click inside content", () => + { + InputManager.MoveMouseTo(overlay.ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("overlay not dismissed", () => overlay.State.Value == Visibility.Visible); + + AddStep("click outside content", () => + { + InputManager.MoveMouseTo(overlay.ScreenSpaceDrawQuad.TopLeft - new Vector2(1)); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("overlay dismissed", () => overlay.State.Value == Visibility.Hidden); + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index 5cf3d562a9..2d025d46ef 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -1,26 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Input.Events; using osu.Framework.Screens; namespace osu.Game.Overlays.FirstRunSetup { public abstract class FirstRunSetupScreen : Screen { - [Resolved] - protected FirstRunSetupOverlay Overlay { get; private set; } - - protected override bool OnClick(ClickEvent e) => true; + private const float offset = 100; public override void OnEntering(IScreen last) { base.OnEntering(last); this .FadeInFromZero(500) - .MoveToX(100) + .MoveToX(offset) .MoveToX(0, 500, Easing.OutQuint); } @@ -32,12 +27,22 @@ namespace osu.Game.Overlays.FirstRunSetup .MoveToX(0, 500, Easing.OutQuint); } - public override void OnSuspending(IScreen next) + public override bool OnExiting(IScreen next) { - base.OnSuspending(next); this .FadeOut(100) - .MoveToX(-100, 500, Easing.OutQuint); + .MoveToX(offset, 500, Easing.OutQuint); + + return base.OnExiting(next); + } + + public override void OnSuspending(IScreen next) + { + this + .FadeOut(100) + .MoveToX(-offset, 500, Easing.OutQuint); + + base.OnSuspending(next); } } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs deleted file mode 100644 index 1a8ef0338c..0000000000 --- a/osu.Game/Overlays/FirstRunSetup/ScreenSetupUIScale.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; -using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Localisation; -using osu.Game.Overlays.Settings; -using osu.Game.Screens; -using osu.Game.Screens.Menu; -using osu.Game.Screens.OnlinePlay.Match.Components; -using osu.Game.Screens.Select; -using osuTK; - -namespace osu.Game.Overlays.FirstRunSetup -{ - public class ScreenSetupUIScale : FirstRunSetupScreen - { - [Resolved] - private OsuConfigManager osuConfig { get; set; } - - [Cached] - private OsuLogo osuLogo = new OsuLogo(); - - [BackgroundDependencyLoader] - private void load() - { - OsuScreenStack stack; - Content.Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(20), - Children = new Drawable[] - { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 32)) - { - Text = "The osu! user interface size can be adjusted to your liking.", - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }, - new SettingsSlider - { - LabelText = GraphicsSettingsStrings.UIScaling, - TransferValueOnCommit = true, - Current = osuConfig.GetBindable(OsuSetting.UIScale), - KeyboardStep = 0.01f, - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new ScalingContainer(ScalingMode.Off) - { - Masking = true, - RelativeSizeAxes = Axes.Both, - Child = stack = new OsuScreenStack() - }, - new ScalingContainer(ScalingMode.Off) - { - Masking = true, - RelativeSizeAxes = Axes.Both, - Child = stack = new OsuScreenStack() - } - }, - new Drawable[] - { - new ScalingContainer(ScalingMode.Off) - { - Masking = true, - RelativeSizeAxes = Axes.Both, - Child = stack = new OsuScreenStack() - }, - new ScalingContainer(ScalingMode.Off) - { - Masking = true, - RelativeSizeAxes = Axes.Both, - Child = stack = new OsuScreenStack() - } - } - } - } - } - }, - new PurpleTriangleButton - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding(10), - Text = "Finish", - Action = () => Overlay.Hide() - } - }; - - stack.Push(new PlaySongSelect()); - } - - private class UIScaleSlider : OsuSliderBar - { - public override LocalisableString TooltipText => base.TooltipText + "x"; - } - } -} diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs new file mode 100644 index 0000000000..5070856087 --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -0,0 +1,146 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; +using osu.Framework.Screens; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; +using osu.Game.Overlays.Settings; +using osu.Game.Screens; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Select; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Overlays.FirstRunSetup +{ + public class ScreenUIScale : FirstRunSetupScreen + { + [Resolved] + private OsuConfigManager osuConfig { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension() + }, + Content = new[] + { + new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(20), + Children = new Drawable[] + { + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24)) + { + Text = "The osu! user interface size can be adjusted to your liking.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + new SettingsSlider + { + LabelText = GraphicsSettingsStrings.UIScaling, + Current = osuConfig.GetBindable(OsuSetting.UIScale), + KeyboardStep = 0.01f, + }, + } + } + }, + new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new SampleScreenContainer(new MainMenu()), + new SampleScreenContainer(new PlaySongSelect()), + }, + new Drawable[] + { + new SampleScreenContainer(new MainMenu()), + new SampleScreenContainer(new MainMenu()), + } + } + } + }, + } + } + }; + } + + private class SampleScreenContainer : CompositeDrawable + { + public override bool HandlePositionalInput => false; + public override bool HandleNonPositionalInput => false; + public override bool PropagatePositionalInputSubTree => false; + public override bool PropagateNonPositionalInputSubTree => false; + + public SampleScreenContainer(Screen screen) + { + OsuScreenStack stack; + RelativeSizeAxes = Axes.Both; + + OsuLogo logo; + + InternalChildren = new Drawable[] + { + new DependencyProvidingContainer + { + CachedDependencies = new (Type, object)[] + { + (typeof(OsuLogo), logo = new OsuLogo + { + RelativePositionAxes = Axes.Both, + Position = new Vector2(0.5f), + }) + }, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new ScalingContainer(ScalingMode.Off) + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + stack = new OsuScreenStack(), + logo + }, + }, + } + }, + }; + + stack.Push(screen); + } + } + + private class UIScaleSlider : OsuSliderBar + { + public override LocalisableString TooltipText => base.TooltipText + "x"; + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 0efeded062..b391f71437 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -21,6 +21,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays.Dialog; using osu.Game.Overlays.FirstRunSetup; +using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Match.Components; using osuTK; @@ -33,16 +34,17 @@ namespace osu.Game.Overlays { protected override bool StartHidden => true; - [Resolved(canBeNull: true)] - private DialogOverlay? dialogOverlay { get; set; } + [Resolved] + private IDialogOverlay dialogOverlay { get; set; } = null!; - [Resolved(canBeNull: true)] - private OsuGame? osuGame { get; set; } + [Resolved] + private IPerformFromScreenRunner performer { get; set; } = null!; private ScreenStack stack = null!; public PurpleTriangleButton NextButton = null!; public DangerousTriangleButton BackButton = null!; + private Container mainContent = null!; [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); @@ -57,11 +59,9 @@ namespace osu.Game.Overlays private readonly FirstRunStep[] steps = { new FirstRunStep(typeof(ScreenWelcome), "Welcome"), - new FirstRunStep(typeof(ScreenSetupUIScale), "UI Scale"), + new FirstRunStep(typeof(ScreenUIScale), "UI Scale"), }; - private Container mainContent; - public FirstRunSetupOverlay() { RelativeSizeAxes = Axes.Both; @@ -91,7 +91,7 @@ namespace osu.Game.Overlays new Box { RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background5, + Colour = colourProvider.Background6, }, new GridContainer { @@ -114,7 +114,7 @@ namespace osu.Game.Overlays { new Box { - Colour = colourProvider.Background6, + Colour = colourProvider.Background5, RelativeSizeAxes = Axes.Both, }, new FillFlowContainer @@ -214,27 +214,15 @@ namespace osu.Game.Overlays { base.LoadComplete(); - if (osuGame != null) - { - // if we are valid for display, only do so after reaching the main menu. - osuGame.PerformFromScreen(_ => - { - Show(); - }, new[] { typeof(MainMenu) }); - } - else - { - Show(); - } + // if we are valid for display, only do so after reaching the main menu. + performer.PerformFromScreen(_ => { Show(); }, new[] { typeof(MainMenu) }); } protected override bool OnClick(ClickEvent e) { - if (!mainContent.IsHovered && dialogOverlay?.CurrentDialog == null) + if (!mainContent.IsHovered && dialogOverlay.CurrentDialog == null) { - dialogOverlay?.Push(new ConfirmDialog("Are you sure you want to exit the setup process?", - Hide, - () => { })); + dialogOverlay.Push(new ConfirmDialog("Are you sure you want to exit the setup process?", Hide, () => { })); } return base.OnClick(e); From 11395c40b7e4586c7c3c1030bc7e36d9e51ac5d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 18:58:24 +0900 Subject: [PATCH 0406/1276] Add button to access first run setup on demand --- osu.Game/Overlays/Settings/Sections/GeneralSection.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index 87e9f34833..786db85bbf 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; @@ -11,6 +12,9 @@ namespace osu.Game.Overlays.Settings.Sections { public class GeneralSection : SettingsSection { + [Resolved(CanBeNull = true)] + private FirstRunSetupOverlay firstRunSetupOverlay { get; set; } + public override LocalisableString Header => GeneralSettingsStrings.GeneralSectionHeader; public override Drawable CreateIcon() => new SpriteIcon @@ -22,6 +26,11 @@ namespace osu.Game.Overlays.Settings.Sections { Children = new Drawable[] { + new SettingsButton + { + Text = "Run setup wizard", + Action = () => firstRunSetupOverlay?.Show(), + }, new LanguageSettings(), new UpdateSettings(), }; From 671662144443d40ef313773a6674c7c5dc741f73 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:06:13 +0900 Subject: [PATCH 0407/1276] Re-fix clicking inside display also dismissing --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index b391f71437..d7fd7ed104 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -18,7 +18,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; using osu.Game.Overlays.Dialog; using osu.Game.Overlays.FirstRunSetup; using osu.Game.Screens; @@ -68,11 +67,11 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(IAPIProvider api, OsuColour colours) + private void load() { Children = new Drawable[] { - mainContent = new Container + mainContent = new BlockingContainer { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -210,6 +209,11 @@ namespace osu.Game.Overlays }; } + private class BlockingContainer : Container + { + protected override bool OnMouseDown(MouseDownEvent e) => true; + } + protected override void LoadComplete() { base.LoadComplete(); @@ -220,10 +224,7 @@ namespace osu.Game.Overlays protected override bool OnClick(ClickEvent e) { - if (!mainContent.IsHovered && dialogOverlay.CurrentDialog == null) - { - dialogOverlay.Push(new ConfirmDialog("Are you sure you want to exit the setup process?", Hide, () => { })); - } + dialogOverlay.Push(new ConfirmDialog("Are you sure you want to exit the setup process?", Hide, () => { })); return base.OnClick(e); } From fb7dc895036aae174fa77f10189b253eebb6f8a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:08:34 +0900 Subject: [PATCH 0408/1276] Ensure wizard returns to initial screen after completion --- .../UserInterface/TestSceneFirstRunSetupOverlay.cs | 4 ++++ osu.Game/Overlays/FirstRunSetupOverlay.cs | 9 +++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index d892301a81..d22dd260eb 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -60,6 +60,10 @@ namespace osu.Game.Tests.Visual.UserInterface return overlay.State.Value == Visibility.Hidden; }); + + AddStep("display again on demand", () => overlay.Show()); + + AddUntilStep("back at start", () => overlay.CurrentScreen is ScreenWelcome); } [Test] diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index d7fd7ed104..0c800888ee 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -262,15 +262,16 @@ namespace osu.Game.Overlays { var nextStep = steps[currentStepIndex.Value]; stack.Push((Screen)Activator.CreateInstance(nextStep.ScreenType)); + + NextButton.Text = currentStepIndex + 1 < steps.Length + ? $"Next ({steps[currentStepIndex.Value + 1].Description})" + : "Finish"; } else { Hide(); + currentStepIndex = null; } - - NextButton.Text = currentStepIndex + 1 < steps.Length - ? $"Next ({steps[currentStepIndex.Value + 1].Description})" - : "Finish"; } protected override void PopOut() From 3ea4eabdb6aba9f384ae376b062a452dcbea3c46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:18:13 +0900 Subject: [PATCH 0409/1276] Ensure button text is updated on going backwards --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 0c800888ee..abde9636ba 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -246,6 +246,8 @@ namespace osu.Game.Overlays currentStepIndex--; BackButton.Enabled.Value = currentStepIndex != 0; + + updateButtonText(); } private void showNextStep() @@ -260,12 +262,8 @@ namespace osu.Game.Overlays if (currentStepIndex < steps.Length) { - var nextStep = steps[currentStepIndex.Value]; - stack.Push((Screen)Activator.CreateInstance(nextStep.ScreenType)); - - NextButton.Text = currentStepIndex + 1 < steps.Length - ? $"Next ({steps[currentStepIndex.Value + 1].Description})" - : "Finish"; + stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value].ScreenType)); + updateButtonText(); } else { @@ -274,6 +272,15 @@ namespace osu.Game.Overlays } } + private void updateButtonText() + { + Debug.Assert(currentStepIndex != null); + + NextButton.Text = currentStepIndex + 1 < steps.Length + ? $"Next ({steps[currentStepIndex.Value + 1].Description})" + : "Finish"; + } + protected override void PopOut() { base.PopOut(); From 8bfa59d12f6165f7f30cca862264b9d84c73bd41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:35:09 +0900 Subject: [PATCH 0410/1276] Ensure all other dialogs and overlays are dismissed when the first run wizard is shown --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 52aa6b8f07..7aa488d7a8 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -844,7 +844,7 @@ namespace osu.Game Add(new MusicKeyBindingHandler()); // side overlays which cancel each other. - var singleDisplaySideOverlays = new OverlayContainer[] { Settings, Notifications }; + var singleDisplaySideOverlays = new OverlayContainer[] { Settings, Notifications, firstRunOverlay }; foreach (var overlay in singleDisplaySideOverlays) { @@ -869,7 +869,7 @@ namespace osu.Game } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay, wikiOverlay }; + var singleDisplayOverlays = new OverlayContainer[] { firstRunOverlay, chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay, wikiOverlay }; foreach (var overlay in singleDisplayOverlays) { From 9074eb283dd43e2f0f593c26e3cfddd7b81ec22f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:35:51 +0900 Subject: [PATCH 0411/1276] Show a notification instead of blocking exit of wizard --- .../TestSceneFirstRunSetupOverlay.cs | 6 ------ osu.Game/Overlays/FirstRunSetupOverlay.cs | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index d22dd260eb..3a4dc09e26 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Overlays; -using osu.Game.Overlays.Dialog; using osu.Game.Overlays.FirstRunSetup; using osu.Game.Screens; using osuTK; @@ -23,19 +22,14 @@ namespace osu.Game.Tests.Visual.UserInterface private FirstRunSetupOverlay overlay; private readonly Mock perfomer = new Mock(); - private readonly Mock dialogOverlay = new Mock(); [BackgroundDependencyLoader] private void load() { Dependencies.CacheAs(perfomer.Object); - Dependencies.CacheAs(dialogOverlay.Object); perfomer.Setup(g => g.PerformFromScreen(It.IsAny>(), It.IsAny>())) .Callback((Action action, IEnumerable types) => action(null)); - - dialogOverlay.Setup(d => d.Push(It.IsAny())) - .Callback((PopupDialog dialog) => dialog.PerformOkAction()); } [SetUpSteps] diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index abde9636ba..b7d7a21493 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -222,13 +222,6 @@ namespace osu.Game.Overlays performer.PerformFromScreen(_ => { Show(); }, new[] { typeof(MainMenu) }); } - protected override bool OnClick(ClickEvent e) - { - dialogOverlay.Push(new ConfirmDialog("Are you sure you want to exit the setup process?", Hide, () => { })); - - return base.OnClick(e); - } - protected override void PopIn() { base.PopIn(); @@ -283,6 +276,20 @@ namespace osu.Game.Overlays protected override void PopOut() { + if (currentStepIndex != null) + { + notificationOverlay?.Post(new SimpleNotification + { + Text = "Click here to resume initial setup at any point", + Icon = FontAwesome.Solid.Horse, + Activated = () => + { + Show(); + return true; + }, + }); + } + base.PopOut(); this.FadeOut(100); } From 2682373bf25102647c142cdecdaf442be1428725 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:36:12 +0900 Subject: [PATCH 0412/1276] Use existing blocking / exit logic provided by `OsuFocusedOverlayContainer` --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 230 +++++++++++----------- 1 file changed, 115 insertions(+), 115 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index b7d7a21493..4a9e8d0efd 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -11,15 +11,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; +using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Dialog; using osu.Game.Overlays.FirstRunSetup; +using osu.Game.Overlays.Notifications; using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Match.Components; @@ -33,17 +33,16 @@ namespace osu.Game.Overlays { protected override bool StartHidden => true; - [Resolved] - private IDialogOverlay dialogOverlay { get; set; } = null!; - [Resolved] private IPerformFromScreenRunner performer { get; set; } = null!; + [Resolved(canBeNull: true)] + private NotificationOverlay notificationOverlay { get; set; } = null!; + private ScreenStack stack = null!; public PurpleTriangleButton NextButton = null!; public DangerousTriangleButton BackButton = null!; - private Container mainContent = null!; [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); @@ -61,6 +60,8 @@ namespace osu.Game.Overlays new FirstRunStep(typeof(ScreenUIScale), "UI Scale"), }; + private Container stackContainer = null!; + public FirstRunSetupOverlay() { RelativeSizeAxes = Axes.Both; @@ -69,151 +70,140 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(0.95f); + + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 5, + Colour = Color4.Black.Opacity(0.2f), + }; + + Masking = true; + CornerRadius = 10; + Children = new Drawable[] { - mainContent = new BlockingContainer + new Box { RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(0.95f), - EdgeEffect = new EdgeEffectParameters + Colour = colourProvider.Background6, + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - Type = EdgeEffectType.Shadow, - Radius = 5, - Colour = Color4.Black.Opacity(0.2f), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), }, - Masking = true, - CornerRadius = 10, - Children = new Drawable[] + Content = new[] { - new Box + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background6, - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + new Container { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] { - new Container + new Box + { + Colour = colourProvider.Background5, + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer { RelativeSizeAxes = Axes.X, + Margin = new MarginPadding(10), AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, Children = new Drawable[] { - new Box + new OsuSpriteText { - Colour = colourProvider.Background5, - RelativeSizeAxes = Axes.Both, + Text = "First run setup", + Font = OsuFont.Default.With(size: 32), + Colour = colourProvider.Content2, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, }, - new FillFlowContainer + new OsuTextFlowContainer { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding(10), - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "First run setup", - Font = OsuFont.Default.With(size: 32), - Colour = colourProvider.Content2, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - new OsuTextFlowContainer - { - Text = "Setup osu! to suit you", - Colour = colourProvider.Content1, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - }, - } + Text = "Setup osu! to suit you", + Colour = colourProvider.Content1, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, }, } }, - }, - new Drawable[] + } + }, + }, + new Drawable[] + { + stackContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(20), + }, + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(20), + Child = new GridContainer { - new Container + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(20), - Child = stack = new ScreenStack - { - RelativeSizeAxes = Axes.Both, - }, + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 10), + new Dimension(), }, - }, - new Drawable[] - { - new Container + RowDimensions = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(20), - Child = new GridContainer + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] + BackButton = new DangerousTriangleButton { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, 10), - new Dimension(), + Width = 200, + Text = "Back", + Action = showLastStep, + Enabled = { Value = false }, }, - RowDimensions = new[] + Empty(), + NextButton = new PurpleTriangleButton { - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new[] - { - BackButton = new DangerousTriangleButton - { - Width = 200, - Text = "Back", - Action = showLastStep, - Enabled = { Value = false }, - }, - Empty(), - NextButton = new PurpleTriangleButton - { - RelativeSizeAxes = Axes.X, - Width = 1, - Text = "Get started", - Action = showNextStep - } - }, + RelativeSizeAxes = Axes.X, + Width = 1, + Text = "Get started", + Action = showNextStep } }, } - } + }, } - }, + } } - } + }, }; } - private class BlockingContainer : Container - { - protected override bool OnMouseDown(MouseDownEvent e) => true; - } - protected override void LoadComplete() { base.LoadComplete(); @@ -246,7 +236,14 @@ namespace osu.Game.Overlays private void showNextStep() { if (currentStepIndex == null) + { + stackContainer.Child = stack = new ScreenStack + { + RelativeSizeAxes = Axes.Both, + }; + currentStepIndex = 0; + } else currentStepIndex++; @@ -260,8 +257,11 @@ namespace osu.Game.Overlays } else { - Hide(); + stack.FadeOut(500); + stack.Expire(); + currentStepIndex = null; + Hide(); } } From 5fd64a4c782cb67e9d88ac9751c4955165553d5f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:41:07 +0900 Subject: [PATCH 0413/1276] Add test coverage to ensure we don't leave any screens in the first run overlay --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 3a4dc09e26..dfe7ba0252 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Moq; using NUnit.Framework; using osu.Framework.Allocation; @@ -55,6 +56,8 @@ namespace osu.Game.Tests.Visual.UserInterface return overlay.State.Value == Visibility.Hidden; }); + AddUntilStep("wait for screens removed", () => !overlay.ChildrenOfType().Any()); + AddStep("display again on demand", () => overlay.Show()); AddUntilStep("back at start", () => overlay.CurrentScreen is ScreenWelcome); From 02f8367d66abbd278751092cb06b2f17ca97aabb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:47:47 +0900 Subject: [PATCH 0414/1276] Improve animation when showing/hiding first run overlay --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 43 ++++++++++++++--------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 4a9e8d0efd..ee78865618 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -36,8 +36,8 @@ namespace osu.Game.Overlays [Resolved] private IPerformFromScreenRunner performer { get; set; } = null!; - [Resolved(canBeNull: true)] - private NotificationOverlay notificationOverlay { get; set; } = null!; + [Resolved] + private INotificationOverlay notificationOverlay { get; set; } = null!; private ScreenStack stack = null!; @@ -49,6 +49,8 @@ namespace osu.Game.Overlays private int? currentStepIndex; + private const float scale_when_hidden = 0.9f; + /// /// The currently displayed screen, if any. /// @@ -212,15 +214,6 @@ namespace osu.Game.Overlays performer.PerformFromScreen(_ => { Show(); }, new[] { typeof(MainMenu) }); } - protected override void PopIn() - { - base.PopIn(); - this.FadeIn(400, Easing.OutQuint); - - if (currentStepIndex == null) - showNextStep(); - } - private void showLastStep() { Debug.Assert(currentStepIndex > 0); @@ -257,9 +250,6 @@ namespace osu.Game.Overlays } else { - stack.FadeOut(500); - stack.Expire(); - currentStepIndex = null; Hide(); } @@ -274,11 +264,24 @@ namespace osu.Game.Overlays : "Finish"; } + protected override void PopIn() + { + base.PopIn(); + + this.ScaleTo(scale_when_hidden) + .ScaleTo(1, 400, Easing.OutElasticHalf); + + this.FadeIn(400, Easing.OutQuint); + + if (currentStepIndex == null) + showNextStep(); + } + protected override void PopOut() { if (currentStepIndex != null) { - notificationOverlay?.Post(new SimpleNotification + notificationOverlay.Post(new SimpleNotification { Text = "Click here to resume initial setup at any point", Icon = FontAwesome.Solid.Horse, @@ -289,9 +292,17 @@ namespace osu.Game.Overlays }, }); } + else + { + stack? + .FadeOut(100) + .Expire(); + } base.PopOut(); - this.FadeOut(100); + + this.ScaleTo(0.96f, 400, Easing.OutQuint); + this.FadeOut(200, Easing.OutQuint); } private class FirstRunStep From a8d32a20611650c6bf3af729e0cb4c9bcfb79f19 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 23:37:08 +0900 Subject: [PATCH 0415/1276] Add test coverage of notification resume flow --- .../TestSceneFirstRunSetupOverlay.cs | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index dfe7ba0252..77a37e7420 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -12,6 +12,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Overlays.FirstRunSetup; +using osu.Game.Overlays.Notifications; using osu.Game.Screens; using osuTK; using osuTK.Input; @@ -22,20 +23,34 @@ namespace osu.Game.Tests.Visual.UserInterface { private FirstRunSetupOverlay overlay; - private readonly Mock perfomer = new Mock(); + private readonly Mock performer = new Mock(); + + private readonly Mock notificationOverlay = new Mock(); + + private Notification lastNotification; [BackgroundDependencyLoader] private void load() { - Dependencies.CacheAs(perfomer.Object); - - perfomer.Setup(g => g.PerformFromScreen(It.IsAny>(), It.IsAny>())) - .Callback((Action action, IEnumerable types) => action(null)); + Dependencies.CacheAs(performer.Object); + Dependencies.CacheAs(notificationOverlay.Object); } [SetUpSteps] public void SetUpSteps() { + AddStep("setup dependencies", () => + { + performer.Reset(); + notificationOverlay.Reset(); + + performer.Setup(g => g.PerformFromScreen(It.IsAny>(), It.IsAny>())) + .Callback((Action action, IEnumerable types) => action(null)); + + notificationOverlay.Setup(n => n.Post(It.IsAny())) + .Callback((Notification n) => lastNotification = n); + }); + AddStep("add overlay", () => { Child = overlay = new FirstRunSetupOverlay @@ -58,6 +73,8 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for screens removed", () => !overlay.ChildrenOfType().Any()); + AddStep("no notifications", () => notificationOverlay.VerifyNoOtherCalls()); + AddStep("display again on demand", () => overlay.Show()); AddUntilStep("back at start", () => overlay.CurrentScreen is ScreenWelcome); @@ -108,5 +125,23 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("overlay dismissed", () => overlay.State.Value == Visibility.Hidden); } + + [Test] + public void TestResumeViaNotification() + { + AddStep("step to next", () => overlay.NextButton.TriggerClick()); + + AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenUIScale); + + AddStep("hide", () => overlay.Hide()); + AddAssert("overlay hidden", () => overlay.State.Value == Visibility.Hidden); + + AddStep("notification arrived", () => notificationOverlay.Verify(n => n.Post(It.IsAny()), Times.Once)); + + AddStep("run notification action", () => lastNotification.Activated()); + + AddAssert("overlay shown", () => overlay.State.Value == Visibility.Visible); + AddAssert("is resumed", () => overlay.CurrentScreen is ScreenUIScale); + } } } From 5dc38050056e4bb524e48af24bb385c65c5c276b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 13:48:43 +0900 Subject: [PATCH 0416/1276] Fix `stack` nullability --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index ee78865618..27ff4aaabf 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays [Resolved] private INotificationOverlay notificationOverlay { get; set; } = null!; - private ScreenStack stack = null!; + private ScreenStack? stack; public PurpleTriangleButton NextButton = null!; public DangerousTriangleButton BackButton = null!; @@ -54,7 +54,7 @@ namespace osu.Game.Overlays /// /// The currently displayed screen, if any. /// - public FirstRunSetupScreen? CurrentScreen => (FirstRunSetupScreen?)stack.CurrentScreen; + public FirstRunSetupScreen? CurrentScreen => (FirstRunSetupScreen?)stack?.CurrentScreen; private readonly FirstRunStep[] steps = { @@ -217,6 +217,7 @@ namespace osu.Game.Overlays private void showLastStep() { Debug.Assert(currentStepIndex > 0); + Debug.Assert(stack != null); stack.CurrentScreen.Exit(); currentStepIndex--; @@ -241,6 +242,8 @@ namespace osu.Game.Overlays currentStepIndex++; Debug.Assert(currentStepIndex != null); + Debug.Assert(stack != null); + BackButton.Enabled.Value = currentStepIndex > 0; if (currentStepIndex < steps.Length) @@ -294,9 +297,8 @@ namespace osu.Game.Overlays } else { - stack? - .FadeOut(100) - .Expire(); + stack?.FadeOut(100) + .Expire(); } base.PopOut(); From e67cc293b8215e57d412165989754071d748f254 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 13:52:55 +0900 Subject: [PATCH 0417/1276] Add localisation support for all new strings --- osu.Game/Localisation/CommonStrings.cs | 10 ++++ .../FirstRunSetupOverlayStrings.cs | 58 +++++++++++++++++++ .../Localisation/GeneralSettingsStrings.cs | 5 ++ .../Overlays/FirstRunSetup/ScreenUIScale.cs | 2 +- .../Overlays/FirstRunSetup/ScreenWelcome.cs | 4 +- osu.Game/Overlays/FirstRunSetupOverlay.cs | 19 +++--- .../Settings/Sections/GeneralSection.cs | 2 +- 7 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 osu.Game/Localisation/FirstRunSetupOverlayStrings.cs diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 3ea337c279..05c4d97ccc 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -19,6 +19,16 @@ namespace osu.Game.Localisation /// public static LocalisableString Clear => new TranslatableString(getKey(@"clear"), @"Clear"); + /// + /// "Back" + /// + public static LocalisableString Back => new TranslatableString(getKey(@"back"), @"Back"); + + /// + /// "Finish" + /// + public static LocalisableString Finish => new TranslatableString(getKey(@"finish"), @"Finish"); + /// /// "Enabled" /// diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs new file mode 100644 index 0000000000..ec2e97d82a --- /dev/null +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class FirstRunSetupOverlayStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.FirstRunSetupOverlay"; + + /// + /// "Get started" + /// + public static LocalisableString GetStarted => new TranslatableString(getKey(@"get_started"), @"Get started"); + + /// + /// "Click to resume first-run setup at any point" + /// + public static LocalisableString ClickToResumeFirstRunSetupAtAnyPoint => new TranslatableString(getKey(@"click_to_resume_first_run_setup_at_any_point"), @"Click to resume first-run setup at any point"); + + /// + /// "First-run setup" + /// + public static LocalisableString FirstRunSetup => new TranslatableString(getKey(@"first_run_setup"), @"First-run setup"); + + /// + /// "Setup osu! to suit you" + /// + public static LocalisableString SetupOsuToSuitYou => new TranslatableString(getKey(@"setup_osu_to_suit_you"), @"Setup osu! to suit you"); + + /// + /// "Welcome" + /// + public static LocalisableString Welcome => new TranslatableString(getKey(@"welcome"), @"Welcome"); + + /// + /// "Welcome to the first-run setup guide! + /// + /// osu! is a very configurable game, and diving straight into the settings can sometimes be overwhelming. This guide will help you get the important choices out of the way to ensure a great first experience!" + /// + public static LocalisableString WelcomeDescription => new TranslatableString(getKey(@"welcome_description"), @"Welcome to the first-run setup guide! + +osu! is a very configurable game, and diving straight into the settings can sometimes be overwhelming. This guide will help you get the important choices out of the way to ensure a great first experience!"); + + /// + /// "The size of the osu! user interface size can be adjusted to your liking." + /// + public static LocalisableString UIScaleDescription => new TranslatableString(getKey(@"ui_scale_description"), @"The size of the osu! user interface size can be adjusted to your liking."); + + /// + /// "Next ({0})" + /// + public static LocalisableString Next(LocalisableString nextStepDescription) => new TranslatableString(getKey(@"next"), @"Next ({0})", nextStepDescription); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs index c65e6c77f4..2aa91f5245 100644 --- a/osu.Game/Localisation/GeneralSettingsStrings.cs +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -59,6 +59,11 @@ namespace osu.Game.Localisation /// public static LocalisableString ChangeFolderLocation => new TranslatableString(getKey(@"change_folder_location"), @"Change folder location..."); + /// + /// "Run setup wizard" + /// + public static LocalisableString RunSetupWizard => new TranslatableString(getKey(@"run_setup_wizard"), @"Run setup wizard"); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 5070856087..80ff7bdf25 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.FirstRunSetup { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24)) { - Text = "The osu! user interface size can be adjusted to your liking.", + Text = FirstRunSetupOverlayStrings.UIScaleDescription, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs index 879ffdf198..9f80c80a02 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Localisation; namespace osu.Game.Overlays.FirstRunSetup { @@ -22,8 +23,7 @@ namespace osu.Game.Overlays.FirstRunSetup { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Text = - "Welcome to the first-run setup guide!\n\nosu! is a very configurable game, and diving straight into the settings can sometimes be overwhelming. This guide will help you get the important choices out of the way to ensure a great first experience!", + Text = FirstRunSetupOverlayStrings.WelcomeDescription, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 27ff4aaabf..e8829136f0 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -18,6 +18,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; using osu.Game.Overlays.FirstRunSetup; using osu.Game.Overlays.Notifications; using osu.Game.Screens; @@ -58,8 +59,8 @@ namespace osu.Game.Overlays private readonly FirstRunStep[] steps = { - new FirstRunStep(typeof(ScreenWelcome), "Welcome"), - new FirstRunStep(typeof(ScreenUIScale), "UI Scale"), + new FirstRunStep(typeof(ScreenWelcome), FirstRunSetupOverlayStrings.Welcome), + new FirstRunStep(typeof(ScreenUIScale), GraphicsSettingsStrings.UIScaling), }; private Container stackContainer = null!; @@ -129,7 +130,7 @@ namespace osu.Game.Overlays { new OsuSpriteText { - Text = "First run setup", + Text = FirstRunSetupOverlayStrings.FirstRunSetup, Font = OsuFont.Default.With(size: 32), Colour = colourProvider.Content2, Anchor = Anchor.TopCentre, @@ -137,7 +138,7 @@ namespace osu.Game.Overlays }, new OsuTextFlowContainer { - Text = "Setup osu! to suit you", + Text = FirstRunSetupOverlayStrings.SetupOsuToSuitYou, Colour = colourProvider.Content1, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -184,7 +185,7 @@ namespace osu.Game.Overlays BackButton = new DangerousTriangleButton { Width = 200, - Text = "Back", + Text = CommonStrings.Back, Action = showLastStep, Enabled = { Value = false }, }, @@ -193,7 +194,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.X, Width = 1, - Text = "Get started", + Text = FirstRunSetupOverlayStrings.GetStarted, Action = showNextStep } }, @@ -263,8 +264,8 @@ namespace osu.Game.Overlays Debug.Assert(currentStepIndex != null); NextButton.Text = currentStepIndex + 1 < steps.Length - ? $"Next ({steps[currentStepIndex.Value + 1].Description})" - : "Finish"; + ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description) + : CommonStrings.Finish; } protected override void PopIn() @@ -286,7 +287,7 @@ namespace osu.Game.Overlays { notificationOverlay.Post(new SimpleNotification { - Text = "Click here to resume initial setup at any point", + Text = FirstRunSetupOverlayStrings.ClickToResumeFirstRunSetupAtAnyPoint, Icon = FontAwesome.Solid.Horse, Activated = () => { diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index 786db85bbf..ced3116728 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections { new SettingsButton { - Text = "Run setup wizard", + Text = GeneralSettingsStrings.RunSetupWizard, Action = () => firstRunSetupOverlay?.Show(), }, new LanguageSettings(), From 6d534046ff2cedc67315f4d6e10f8f810afab33a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 14:46:01 +0900 Subject: [PATCH 0418/1276] Add keyboard traversal support for first run dialog (and tidy up step traversal logic) --- .../TestSceneFirstRunSetupOverlay.cs | 30 ++++- osu.Game/Overlays/FirstRunSetupOverlay.cs | 125 +++++++++++------- 2 files changed, 103 insertions(+), 52 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 77a37e7420..ff54e34ca0 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -60,13 +60,19 @@ namespace osu.Game.Tests.Visual.UserInterface }); } - [Test] - public void TestOverlayRunsToFinish() + [TestCase(false)] + [TestCase(true)] + public void TestOverlayRunsToFinish(bool keyboard) { AddUntilStep("step through", () => { if (overlay.CurrentScreen?.IsLoaded != false) - overlay.NextButton.TriggerClick(); + { + if (keyboard) + InputManager.Key(Key.Enter); + else + overlay.NextButton.TriggerClick(); + } return overlay.State.Value == Visibility.Hidden; }); @@ -80,8 +86,9 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("back at start", () => overlay.CurrentScreen is ScreenWelcome); } - [Test] - public void TestBackButton() + [TestCase(false)] + [TestCase(true)] + public void TestBackButton(bool keyboard) { AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value); @@ -98,12 +105,23 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("step back to start", () => { if (overlay.CurrentScreen?.IsLoaded != false) - overlay.BackButton.TriggerClick(); + { + if (keyboard) + InputManager.Key(Key.Escape); + else + overlay.BackButton.TriggerClick(); + } return overlay.CurrentScreen is ScreenWelcome; }); AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value); + + if (keyboard) + { + AddStep("exit via keyboard", () => InputManager.Key(Key.Escape)); + AddAssert("overlay dismissed", () => overlay.State.Value == Visibility.Hidden); + } } [Test] diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index e8829136f0..00396e6d85 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -12,12 +12,14 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Overlays.FirstRunSetup; using osu.Game.Overlays.Notifications; @@ -215,57 +217,30 @@ namespace osu.Game.Overlays performer.PerformFromScreen(_ => { Show(); }, new[] { typeof(MainMenu) }); } - private void showLastStep() + public override bool OnPressed(KeyBindingPressEvent e) { - Debug.Assert(currentStepIndex > 0); - Debug.Assert(stack != null); - - stack.CurrentScreen.Exit(); - currentStepIndex--; - - BackButton.Enabled.Value = currentStepIndex != 0; - - updateButtonText(); - } - - private void showNextStep() - { - if (currentStepIndex == null) + if (!e.Repeat) { - stackContainer.Child = stack = new ScreenStack + switch (e.Action) { - RelativeSizeAxes = Axes.Both, - }; + case GlobalAction.Select: + NextButton.TriggerClick(); + return true; - currentStepIndex = 0; + case GlobalAction.Back: + if (BackButton.Enabled.Value) + { + BackButton.TriggerClick(); + return true; + } + + // If back button is disabled, we are at the first step. + // The base call will handle dismissal of the overlay. + break; + } } - else - currentStepIndex++; - Debug.Assert(currentStepIndex != null); - Debug.Assert(stack != null); - - BackButton.Enabled.Value = currentStepIndex > 0; - - if (currentStepIndex < steps.Length) - { - stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value].ScreenType)); - updateButtonText(); - } - else - { - currentStepIndex = null; - Hide(); - } - } - - private void updateButtonText() - { - Debug.Assert(currentStepIndex != null); - - NextButton.Text = currentStepIndex + 1 < steps.Length - ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description) - : CommonStrings.Finish; + return base.OnPressed(e); } protected override void PopIn() @@ -278,7 +253,7 @@ namespace osu.Game.Overlays this.FadeIn(400, Easing.OutQuint); if (currentStepIndex == null) - showNextStep(); + showFirstStep(); } protected override void PopOut() @@ -308,6 +283,64 @@ namespace osu.Game.Overlays this.FadeOut(200, Easing.OutQuint); } + private void showFirstStep() + { + Debug.Assert(currentStepIndex == null); + + stackContainer.Child = stack = new ScreenStack + { + RelativeSizeAxes = Axes.Both, + }; + + currentStepIndex = -1; + showNextStep(); + } + + private void showLastStep() + { + if (currentStepIndex == 0) + return; + + Debug.Assert(stack != null); + + stack.CurrentScreen.Exit(); + currentStepIndex--; + + BackButton.Enabled.Value = currentStepIndex != 0; + + updateButtonText(); + } + + private void showNextStep() + { + Debug.Assert(currentStepIndex != null); + Debug.Assert(stack != null); + + currentStepIndex++; + + BackButton.Enabled.Value = currentStepIndex > 0; + + if (currentStepIndex < steps.Length) + { + stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value].ScreenType)); + updateButtonText(); + } + else + { + currentStepIndex = null; + Hide(); + } + } + + private void updateButtonText() + { + Debug.Assert(currentStepIndex != null); + + NextButton.Text = currentStepIndex + 1 < steps.Length + ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description) + : CommonStrings.Finish; + } + private class FirstRunStep { public readonly Type ScreenType; From c4bade099574fa36af497f4d8fe8d97493041ee2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 15:46:03 +0900 Subject: [PATCH 0419/1276] Expose `MainMenu` buttons --- osu.Game/Screens/Menu/ButtonSystem.cs | 14 +++++++++++--- osu.Game/Screens/Menu/MainMenu.cs | 26 +++++++++++++------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 598c441042..a153eda0c4 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -88,6 +88,8 @@ namespace osu.Game.Screens.Menu private readonly LogoTrackingContainer logoTrackingContainer; + public bool ReturnToTopOnIdle { get; set; } = true; + public ButtonSystem() { RelativeSizeAxes = Axes.Both; @@ -101,7 +103,8 @@ namespace osu.Game.Screens.Menu buttonArea.AddRange(new Drawable[] { new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, FontAwesome.Solid.Cog, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), - backButton = new MainMenuButton(ButtonSystemStrings.Back, @"button-back-select", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH) + backButton = new MainMenuButton(ButtonSystemStrings.Back, @"button-back-select", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, + -WEDGE_WIDTH) { VisibleState = ButtonSystemState.Play, }, @@ -131,9 +134,11 @@ namespace osu.Game.Screens.Menu buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, + Key.P)); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-edit-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, + Key.D)); if (host.CanExit) buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); @@ -201,6 +206,9 @@ namespace osu.Game.Screens.Menu private void updateIdleState(bool isIdle) { + if (!ReturnToTopOnIdle) + return; + if (isIdle && State != ButtonSystemState.Exit && State != ButtonSystemState.EnteringMode) State = ButtonSystemState.Initial; } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 08df06816b..422508b60e 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Menu public const float FADE_OUT_DURATION = 400; - public override bool HideOverlaysOnEnter => buttons == null || buttons.State == ButtonSystemState.Initial; + public override bool HideOverlaysOnEnter => Buttons == null || Buttons.State == ButtonSystemState.Initial; public override bool AllowBackButton => false; @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Menu private MenuSideFlashes sideFlashes; - private ButtonSystem buttons; + protected ButtonSystem Buttons; [Resolved] private GameHost host { get; set; } @@ -101,7 +101,7 @@ namespace osu.Game.Screens.Menu ParallaxAmount = 0.01f, Children = new Drawable[] { - buttons = new ButtonSystem + Buttons = new ButtonSystem { OnEdit = delegate { @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Menu exitConfirmOverlay?.CreateProxy() ?? Empty() }); - buttons.StateChanged += state => + Buttons.StateChanged += state => { switch (state) { @@ -140,8 +140,8 @@ namespace osu.Game.Screens.Menu } }; - buttons.OnSettings = () => settings?.ToggleVisibility(); - buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); + Buttons.OnSettings = () => settings?.ToggleVisibility(); + Buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); LoadComponentAsync(background = new BackgroundScreenDefault()); preloadSongSelect(); @@ -179,7 +179,7 @@ namespace osu.Game.Screens.Menu public override void OnEntering(IScreen last) { base.OnEntering(last); - buttons.FadeInFromZero(500); + Buttons.FadeInFromZero(500); if (last is IntroScreen && musicController.TrackLoaded) { @@ -203,14 +203,14 @@ namespace osu.Game.Screens.Menu { base.LogoArriving(logo, resuming); - buttons.SetOsuLogo(logo); + Buttons.SetOsuLogo(logo); logo.FadeColour(Color4.White, 100, Easing.OutQuint); logo.FadeIn(100, Easing.OutQuint); if (resuming) { - buttons.State = ButtonSystemState.TopLevel; + Buttons.State = ButtonSystemState.TopLevel; this.FadeIn(FADE_IN_DURATION, Easing.OutQuint); buttonsContainer.MoveTo(new Vector2(0, 0), FADE_IN_DURATION, Easing.OutQuint); @@ -245,15 +245,15 @@ namespace osu.Game.Screens.Menu var seq = logo.FadeOut(300, Easing.InSine) .ScaleTo(0.2f, 300, Easing.InSine); - seq.OnComplete(_ => buttons.SetOsuLogo(null)); - seq.OnAbort(_ => buttons.SetOsuLogo(null)); + seq.OnComplete(_ => Buttons.SetOsuLogo(null)); + seq.OnAbort(_ => Buttons.SetOsuLogo(null)); } public override void OnSuspending(IScreen next) { base.OnSuspending(next); - buttons.State = ButtonSystemState.EnteringMode; + Buttons.State = ButtonSystemState.EnteringMode; this.FadeOut(FADE_OUT_DURATION, Easing.InSine); buttonsContainer.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine); @@ -285,7 +285,7 @@ namespace osu.Game.Screens.Menu return true; } - buttons.State = ButtonSystemState.Exit; + Buttons.State = ButtonSystemState.Exit; OverlayActivationMode.Value = OverlayActivation.Disabled; songTicker.Hide(); From c27831145ccd47975d0ba862d0b11408fe9c5368 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 16:37:29 +0900 Subject: [PATCH 0420/1276] Add scroll and flow at the `FirstRunSetupScreen` level --- .../FirstRunSetup/FirstRunSetupScreen.cs | 23 +++++++++++++++++++ .../Overlays/FirstRunSetup/ScreenWelcome.cs | 19 ++++----------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index 2d025d46ef..b2284a999d 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Screens; +using osu.Game.Graphics.Containers; +using osuTK; namespace osu.Game.Overlays.FirstRunSetup { @@ -10,6 +13,26 @@ namespace osu.Game.Overlays.FirstRunSetup { private const float offset = 100; + protected FillFlowContainer Content { get; private set; } + + protected FirstRunSetupScreen() + { + InternalChildren = new Drawable[] + { + new OsuScrollContainer(Direction.Vertical) + { + RelativeSizeAxes = Axes.Both, + Child = Content = new FillFlowContainer + { + Spacing = new Vector2(20), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + }, + } + }; + } + public override void OnEntering(IScreen last) { base.OnEntering(last); diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs index 9f80c80a02..39da180f40 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Localisation; @@ -13,21 +12,13 @@ namespace osu.Game.Overlays.FirstRunSetup { public ScreenWelcome() { - InternalChildren = new Drawable[] + Content.Children = new Drawable[] { - new FillFlowContainer + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) - { - Text = FirstRunSetupOverlayStrings.WelcomeDescription, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }, - } + Text = FirstRunSetupOverlayStrings.WelcomeDescription, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y }, }; } From a195d4f5aa5a132bf6860e23ab04f327cb84b8a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 16:49:41 +0900 Subject: [PATCH 0421/1276] Use a culture name that doesn't cause everything to fall over --- osu.Game/Localisation/DebugLocalisationStore.cs | 2 +- osu.Game/Localisation/Language.cs | 2 +- osu.Game/OsuGame.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/DebugLocalisationStore.cs b/osu.Game/Localisation/DebugLocalisationStore.cs index 99dca6265e..2b114b1bd8 100644 --- a/osu.Game/Localisation/DebugLocalisationStore.cs +++ b/osu.Game/Localisation/DebugLocalisationStore.cs @@ -21,7 +21,7 @@ namespace osu.Game.Localisation public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - public CultureInfo EffectiveCulture { get; } = new CultureInfo(@"debug"); + public CultureInfo EffectiveCulture { get; } = CultureInfo.CurrentCulture; public void Dispose() { diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index c98586457e..c13a1a10cb 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -114,7 +114,7 @@ namespace osu.Game.Localisation #if DEBUG [Description(@"Debug (show raw keys)")] - DebugLocalisation + debug #endif } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b591b09584..ea21954b9c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -629,9 +629,9 @@ namespace osu.Game foreach (var language in Enum.GetValues(typeof(Language)).OfType()) { #if DEBUG - if (language == Language.DebugLocalisation) + if (language == Language.debug) { - Localisation.AddLanguage(Language.DebugLocalisation.ToString(), new DebugLocalisationStore()); + Localisation.AddLanguage(Language.debug.ToString(), new DebugLocalisationStore()); continue; } #endif From 1490502d4ccc84472f34e23e04e91e04b001f7b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 16:37:38 +0900 Subject: [PATCH 0422/1276] Improve overall usability of scale adjust screen --- .../Graphics/Containers/ScalingContainer.cs | 10 +- .../Overlays/FirstRunSetup/ScreenUIScale.cs | 153 +++++++++++------- 2 files changed, 101 insertions(+), 62 deletions(-) diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index ca8b6f388f..6589de40b7 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -79,12 +79,12 @@ namespace osu.Game.Graphics.Containers }; } - private class ScalingDrawSizePreservingFillContainer : DrawSizePreservingFillContainer + public class ScalingDrawSizePreservingFillContainer : DrawSizePreservingFillContainer { private readonly bool applyUIScale; private Bindable uiScale; - private float currentScale = 1; + protected float CurrentScale { get; private set; } = 1; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; @@ -99,14 +99,14 @@ namespace osu.Game.Graphics.Containers if (applyUIScale) { uiScale = osuConfig.GetBindable(OsuSetting.UIScale); - uiScale.BindValueChanged(args => this.TransformTo(nameof(currentScale), args.NewValue, duration, Easing.OutQuart), true); + uiScale.BindValueChanged(args => this.TransformTo(nameof(CurrentScale), args.NewValue, duration, Easing.OutQuart), true); } } protected override void Update() { - Scale = new Vector2(currentScale); - Size = new Vector2(1 / currentScale); + Scale = new Vector2(CurrentScale); + Size = new Vector2(1 / CurrentScale); base.Update(); } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 80ff7bdf25..52aeb8082e 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -2,17 +2,23 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Overlays.Settings; +using osu.Game.Rulesets; using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.Select; @@ -27,77 +33,113 @@ namespace osu.Game.Overlays.FirstRunSetup private OsuConfigManager osuConfig { get; set; } [BackgroundDependencyLoader] - private void load() + private void load(RulesetStore rulesets, BeatmapManager beatmaps) { - InternalChildren = new Drawable[] + Content.Children = new Drawable[] { - new GridContainer + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24)) { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + Text = FirstRunSetupOverlayStrings.UIScaleDescription, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + new SettingsSlider + { + LabelText = GraphicsSettingsStrings.UIScaling, + Current = osuConfig.GetBindable(OsuSetting.UIScale), + KeyboardStep = 0.01f, + }, + new InverseScalingDrawSizePreservingFillContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.None, + Size = new Vector2(960, 960 / 16f * 9 / 2), + Children = new Drawable[] { - new Dimension(GridSizeMode.AutoSize), - new Dimension() - }, - Content = new[] - { - new Drawable[] + new GridContainer { - new FillFlowContainer + RelativeSizeAxes = Axes.Both, + Content = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(20), - Children = new Drawable[] + new Drawable[] { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24)) - { - Text = FirstRunSetupOverlayStrings.UIScaleDescription, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }, - new SettingsSlider - { - LabelText = GraphicsSettingsStrings.UIScaling, - Current = osuConfig.GetBindable(OsuSetting.UIScale), - KeyboardStep = 0.01f, - }, - } + new SampleScreenContainer(new PinnedMainMenu()), + new SampleScreenContainer(new PlaySongSelect()), + }, + // TODO: add more screens here in the future (gameplay / results) + // requires a bit more consideration to isolate their behaviour from the "parent" game. } - }, - new Drawable[] - { - new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new SampleScreenContainer(new MainMenu()), - new SampleScreenContainer(new PlaySongSelect()), - }, - new Drawable[] - { - new SampleScreenContainer(new MainMenu()), - new SampleScreenContainer(new MainMenu()), - } - } - } - }, + } } } }; } + private class InverseScalingDrawSizePreservingFillContainer : ScalingContainer.ScalingDrawSizePreservingFillContainer + { + private Vector2 initialSize; + + public InverseScalingDrawSizePreservingFillContainer() + : base(true) + { + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + initialSize = Size; + } + + protected override void Update() + { + Size = initialSize / CurrentScale; + } + } + + private class PinnedMainMenu : MainMenu + { + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + Buttons.ReturnToTopOnIdle = false; + Buttons.State = ButtonSystemState.TopLevel; + } + } + + private class UIScaleSlider : OsuSliderBar + { + public override LocalisableString TooltipText => base.TooltipText + "x"; + } + private class SampleScreenContainer : CompositeDrawable { + // Minimal isolation from main game. + + [Cached] + [Cached(typeof(IBindable))] + protected readonly Bindable Ruleset = new Bindable(); + + [Cached] + [Cached(typeof(IBindable))] + protected Bindable Beatmap { get; private set; } = new Bindable(); + public override bool HandlePositionalInput => false; public override bool HandleNonPositionalInput => false; public override bool PropagatePositionalInputSubTree => false; public override bool PropagateNonPositionalInputSubTree => false; + [BackgroundDependencyLoader] + private void load(AudioManager audio, TextureStore textures, RulesetStore rulesets) + { + Beatmap.Value = new DummyWorkingBeatmap(audio, textures); + Beatmap.Value.LoadTrack(); + + Ruleset.Value = rulesets.AvailableRulesets.First(); + } + public SampleScreenContainer(Screen screen) { OsuScreenStack stack; @@ -105,6 +147,8 @@ namespace osu.Game.Overlays.FirstRunSetup OsuLogo logo; + Padding = new MarginPadding(5); + InternalChildren = new Drawable[] { new DependencyProvidingContainer @@ -120,7 +164,7 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new ScalingContainer(ScalingMode.Off) + new ScalingContainer.ScalingDrawSizePreservingFillContainer(true) { Masking = true, RelativeSizeAxes = Axes.Both, @@ -137,10 +181,5 @@ namespace osu.Game.Overlays.FirstRunSetup stack.Push(screen); } } - - private class UIScaleSlider : OsuSliderBar - { - public override LocalisableString TooltipText => base.TooltipText + "x"; - } } } From 3378c919013f67690a168fddd79d07d9baf2078c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 16:53:41 +0900 Subject: [PATCH 0423/1276] Fix double applied padding --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 00396e6d85..1411539b37 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -165,7 +165,10 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(20), + Padding = new MarginPadding(20) + { + Top = 0 // provided by the stack container above. + }, Child = new GridContainer { RelativeSizeAxes = Axes.X, From e2da1d76ca34999f4e3d45531b65ea8795838299 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 16:59:25 +0900 Subject: [PATCH 0424/1276] Only show first run setup once per install --- osu.Game/Configuration/OsuConfigManager.cs | 3 +++ osu.Game/Overlays/FirstRunSetupOverlay.cs | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 9aacb50684..2c04b03646 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -134,6 +134,8 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.Version, string.Empty); + SetDefault(OsuSetting.ShowFirstRunSetup, true); + SetDefault(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); SetDefault(OsuSetting.ScreenshotCaptureMenuCursor, false); @@ -308,6 +310,7 @@ namespace osu.Game.Configuration BeatmapListingCardSize, ToolbarClockDisplayMode, Version, + ShowFirstRunSetup, ShowConvertedBeatmaps, Skin, ScreenshotFormat, diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 1411539b37..207eeb0f6e 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,6 +16,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Screens; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -42,6 +44,9 @@ namespace osu.Game.Overlays [Resolved] private INotificationOverlay notificationOverlay { get; set; } = null!; + [Resolved] + private OsuConfigManager config { get; set; } = null!; + private ScreenStack? stack; public PurpleTriangleButton NextButton = null!; @@ -50,6 +55,8 @@ namespace osu.Game.Overlays [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + private readonly Bindable showFirstRunSetup = new Bindable(); + private int? currentStepIndex; private const float scale_when_hidden = 0.9f; @@ -216,8 +223,13 @@ namespace osu.Game.Overlays { base.LoadComplete(); - // if we are valid for display, only do so after reaching the main menu. - performer.PerformFromScreen(_ => { Show(); }, new[] { typeof(MainMenu) }); + config.BindWith(OsuSetting.ShowFirstRunSetup, showFirstRunSetup); + + if (showFirstRunSetup.Value) + { + // if we are valid for display, only do so after reaching the main menu. + performer.PerformFromScreen(_ => { Show(); }, new[] { typeof(MainMenu) }); + } } public override bool OnPressed(KeyBindingPressEvent e) @@ -330,6 +342,7 @@ namespace osu.Game.Overlays } else { + showFirstRunSetup.Value = false; currentStepIndex = null; Hide(); } From c562004fe9784065fddc14fe56950148ed50eca2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 17:08:38 +0900 Subject: [PATCH 0425/1276] Add test coverage of only showing on first run --- .../TestSceneFirstRunSetupOverlay.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index ff54e34ca0..00f1d4ac4e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; +using osu.Game.Configuration; using osu.Game.Overlays; using osu.Game.Overlays.FirstRunSetup; using osu.Game.Overlays.Notifications; @@ -29,9 +30,12 @@ namespace osu.Game.Tests.Visual.UserInterface private Notification lastNotification; + protected OsuConfigManager LocalConfig; + [BackgroundDependencyLoader] private void load() { + Dependencies.Cache(LocalConfig = new OsuConfigManager(LocalStorage)); Dependencies.CacheAs(performer.Object); Dependencies.CacheAs(notificationOverlay.Object); } @@ -60,6 +64,29 @@ namespace osu.Game.Tests.Visual.UserInterface }); } + [Test] + public void TestDoesntOpenOnSecondRun() + { + AddStep("set first run", () => LocalConfig.SetValue(OsuSetting.ShowFirstRunSetup, true)); + + AddUntilStep("step through", () => + { + if (overlay.CurrentScreen?.IsLoaded != false) overlay.NextButton.TriggerClick(); + return overlay.State.Value == Visibility.Hidden; + }); + + AddAssert("first run false", () => !LocalConfig.Get(OsuSetting.ShowFirstRunSetup)); + + AddStep("add overlay", () => + { + Child = overlay = new FirstRunSetupOverlay(); + }); + + AddWaitStep("wait some", 5); + + AddAssert("overlay didn't show", () => overlay.State.Value == Visibility.Hidden); + } + [TestCase(false)] [TestCase(true)] public void TestOverlayRunsToFinish(bool keyboard) From 17eaa44af1c773d581a8a54455ef505fbf000b4f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 17:40:35 +0900 Subject: [PATCH 0426/1276] Ensure notifications don't interrupt the first run setup process --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 35 +++++++++++++++++++---- osu.Game/Screens/OsuScreen.cs | 2 +- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 207eeb0f6e..b118e61fba 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -74,6 +74,8 @@ namespace osu.Game.Overlays private Container stackContainer = null!; + private Bindable? overlayActivationMode; + public FirstRunSetupOverlay() { RelativeSizeAxes = Axes.Both; @@ -225,11 +227,7 @@ namespace osu.Game.Overlays config.BindWith(OsuSetting.ShowFirstRunSetup, showFirstRunSetup); - if (showFirstRunSetup.Value) - { - // if we are valid for display, only do so after reaching the main menu. - performer.PerformFromScreen(_ => { Show(); }, new[] { typeof(MainMenu) }); - } + if (showFirstRunSetup.Value) Show(); } public override bool OnPressed(KeyBindingPressEvent e) @@ -258,6 +256,26 @@ namespace osu.Game.Overlays return base.OnPressed(e); } + public override void Show() + { + // if we are valid for display, only do so after reaching the main menu. + performer.PerformFromScreen(screen => + { + MainMenu menu = (MainMenu)screen; + + // Eventually I'd like to replace this with a better method that doesn't access the screen. + // Either this dialog would be converted to its own screen, or at very least be "hosted" by a screen pushed to the main menu. + // Alternatively, another method of disabling notifications could be added to `INotificationOverlay`. + if (menu != null) + { + overlayActivationMode = menu.OverlayActivationMode.GetBoundCopy(); + overlayActivationMode.Value = OverlayActivation.UserTriggered; + } + + base.Show(); + }, new[] { typeof(MainMenu) }); + } + protected override void PopIn() { base.PopIn(); @@ -273,6 +291,13 @@ namespace osu.Game.Overlays protected override void PopOut() { + if (overlayActivationMode != null) + { + // If this is non-null we are guaranteed to have come from the main menu. + overlayActivationMode.Value = OverlayActivation.All; + overlayActivationMode = null; + } + if (currentStepIndex != null) { notificationOverlay.Post(new SimpleNotification diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index ed4901e1fa..d87a86cedf 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens /// protected virtual OverlayActivation InitialOverlayActivationMode => OverlayActivation.All; - protected readonly Bindable OverlayActivationMode; + public readonly Bindable OverlayActivationMode; IBindable IOsuScreen.OverlayActivationMode => OverlayActivationMode; From 2a3a0c1cd38dbe61d4941913b18785ed3a5732a6 Mon Sep 17 00:00:00 2001 From: chickensalt Date: Tue, 19 Apr 2022 18:44:20 +1000 Subject: [PATCH 0427/1276] remove notification on trying to multi when logged out fixes #17877 --- osu.Game/Screens/Menu/ButtonSystem.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 598c441042..6d09b6de9f 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -161,17 +161,7 @@ namespace osu.Game.Screens.Menu { if (api.State.Value != APIState.Online) { - notifications?.Post(new SimpleNotification - { - Text = "You gotta be online to multi 'yo!", - Icon = FontAwesome.Solid.Globe, - Activated = () => - { - loginOverlay?.Show(); - return true; - } - }); - + loginOverlay?.Show(); return; } From ce70c10afae4bc91f6cb751f8b59fd46c0488689 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 18:00:39 +0900 Subject: [PATCH 0428/1276] Tidy up unused dependencies --- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 52aeb8082e..e632170975 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -29,11 +29,8 @@ namespace osu.Game.Overlays.FirstRunSetup { public class ScreenUIScale : FirstRunSetupScreen { - [Resolved] - private OsuConfigManager osuConfig { get; set; } - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets, BeatmapManager beatmaps) + private void load(OsuConfigManager config) { Content.Children = new Drawable[] { @@ -46,7 +43,7 @@ namespace osu.Game.Overlays.FirstRunSetup new SettingsSlider { LabelText = GraphicsSettingsStrings.UIScaling, - Current = osuConfig.GetBindable(OsuSetting.UIScale), + Current = config.GetBindable(OsuSetting.UIScale), KeyboardStep = 0.01f, }, new InverseScalingDrawSizePreservingFillContainer From 75a6e9fd7f7bbf82325051c89fe7eab3caa60dcc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 18:10:10 +0900 Subject: [PATCH 0429/1276] Convert to use `nullable` and rearrange fields --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 9d14041dd8..1fedc7daae 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -1,7 +1,7 @@ +#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; @@ -33,9 +33,25 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy /// If the "circle" texture could not be found with this prefix, /// then it is nullified and the default prefix "hitcircle" is used instead. /// - private string priorityLookupPrefix; + private string? priorityLookupPrefix; - public LegacyMainCirclePiece(string priorityLookupPrefix = null, bool hasNumber = true) + private Drawable hitCircleSprite = null!; + + protected Container OverlayLayer { get; private set; } = null!; + + private Drawable hitCircleOverlay = null!; + private SkinnableSpriteText hitCircleText = null!; + + private readonly Bindable accentColour = new Bindable(); + private readonly IBindable indexInCurrentCombo = new Bindable(); + + [Resolved(canBeNull: true)] + private DrawableHitObject? drawableObject { get; set; } + + [Resolved] + private ISkinSource skin { get; set; } = null!; + + public LegacyMainCirclePiece(string? priorityLookupPrefix = null, bool hasNumber = true) { this.priorityLookupPrefix = priorityLookupPrefix; this.hasNumber = hasNumber; @@ -43,30 +59,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); } - private Drawable hitCircleSprite; - - protected Container OverlayLayer { get; private set; } - - private Drawable hitCircleOverlay; - private SkinnableSpriteText hitCircleText; - - private readonly Bindable accentColour = new Bindable(); - private readonly IBindable indexInCurrentCombo = new Bindable(); - - [Resolved(canBeNull: true)] - [CanBeNull] - private DrawableHitObject drawableObject { get; set; } - - [Resolved] - private ISkinSource skin { get; set; } - [BackgroundDependencyLoader] private void load() { - var drawableOsuObject = (DrawableOsuHitObject)drawableObject; + var drawableOsuObject = (DrawableOsuHitObject?)drawableObject; // attempt lookup using priority specification - Texture baseTexture = getTexture(string.Empty); + Texture? baseTexture = getTexture(string.Empty); // if the base texture was not found using the priority specification, nullify the specification and fall back to "hitcircle". if (baseTexture == null) @@ -122,10 +121,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); } - Texture getTexture(string name) + Texture? getTexture(string name) => skin.GetTexture($"{priorityLookupPrefix ?? @"hitcircle"}{name}"); - Drawable getAnimation(string name, double frameLength) + Drawable? getAnimation(string name, double frameLength) => skin.GetAnimation($"{priorityLookupPrefix ?? @"hitcircle"}{name}", true, true, frameLength: frameLength); } From e8adbb319bd1a4d6eb6c16218cda13870a8dcdd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 22:57:04 +0900 Subject: [PATCH 0430/1276] Skip first run setup in `OsuGameTestScene`s --- osu.Game/Tests/Visual/OsuGameTestScene.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index 34d7723fa3..7a6f8c8cfb 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -156,6 +156,7 @@ namespace osu.Game.Tests.Visual base.LoadComplete(); LocalConfig.SetValue(OsuSetting.IntroSequence, IntroSequence.Circles); + LocalConfig.SetValue(OsuSetting.ShowFirstRunSetup, false); API.Login("Rhythm Champion", "osu!"); From 44c822f34d7a332ba41381d871c42814e9a6daea Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 19 Apr 2022 16:56:55 +0100 Subject: [PATCH 0431/1276] Use normal `OsuScrollContainer` for `ChannelList` --- .../Overlays/Chat/ChannelList/ChannelList.cs | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 5c2a1ad89f..94c1a99a81 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Chat.ChannelList RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background6, }, - new ChannelListScrollContainer + new OsuScrollContainer { Padding = new MarginPadding { Vertical = 7 }, RelativeSizeAxes = Axes.Both, @@ -131,31 +131,5 @@ namespace osu.Game.Overlays.Chat.ChannelList }); } } - - private class ChannelListScrollContainer : OsuScrollContainer - { - protected override ScrollbarContainer CreateScrollbar(Direction direction) - => new ChannelListScrollBar(direction); - - protected class ChannelListScrollBar : OsuScrollbar - { - private const float bar_size = 4; - private const float bar_margin = 7; - - public ChannelListScrollBar(Direction scrollDir) - : base(scrollDir) - { - Size = new Vector2(bar_size); - Margin = new MarginPadding { Horizontal = bar_margin }; - CornerRadius = 2; - } - - public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None) - { - Vector2 size = new Vector2(bar_size, val); - this.ResizeTo(size, duration, easing); - } - } - } } } From 033b556be58fe1b780d52d0b6c90b75eaea23f31 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 19:39:06 +0300 Subject: [PATCH 0432/1276] Simplify texture lookup further --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 35 +++++-------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 1fedc7daae..2107de61b6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -8,7 +8,6 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; @@ -24,16 +23,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public override bool RemoveCompletedTransforms => false; - private readonly bool hasNumber; - /// /// A prioritised prefix to perform texture lookups with. /// - /// - /// If the "circle" texture could not be found with this prefix, - /// then it is nullified and the default prefix "hitcircle" is used instead. - /// - private string? priorityLookupPrefix; + private readonly string? priorityLookupPrefix; + + private readonly bool hasNumber; private Drawable hitCircleSprite = null!; @@ -64,23 +59,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { var drawableOsuObject = (DrawableOsuHitObject?)drawableObject; - // attempt lookup using priority specification - Texture? baseTexture = getTexture(string.Empty); - - // if the base texture was not found using the priority specification, nullify the specification and fall back to "hitcircle". - if (baseTexture == null) - { - priorityLookupPrefix = null; - baseTexture = getTexture(string.Empty); - } + // if a base texture for the specified prefix exists, continue using it for subsequent lookups. + // otherwise fall back to the default prefix "hitcircle". + string circleName = (priorityLookupPrefix != null && skin.GetTexture(priorityLookupPrefix) != null) ? priorityLookupPrefix : @"hitcircle"; // at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it. - // the flow above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist. + // the conditional above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist. // expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png. InternalChildren = new[] { - hitCircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = baseTexture }) + hitCircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) }) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -89,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Child = hitCircleOverlay = new KiaiFlashingDrawable(() => getAnimation(@"overlay", 1000 / 2d)) + Child = hitCircleOverlay = new KiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -120,12 +109,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy accentColour.BindTo(drawableOsuObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); } - - Texture? getTexture(string name) - => skin.GetTexture($"{priorityLookupPrefix ?? @"hitcircle"}{name}"); - - Drawable? getAnimation(string name, double frameLength) - => skin.GetAnimation($"{priorityLookupPrefix ?? @"hitcircle"}{name}", true, true, frameLength: frameLength); } protected override void LoadComplete() From 813dc2dd78fd0ba65bcae4caddd289be67447310 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 19:46:29 +0300 Subject: [PATCH 0433/1276] Fix wrong prefix for default priority lookup in test --- osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs index 766db25d71..c9eeef3c01 100644 --- a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Tests // available textures new[] { @"hitcircle", @"hitcircleoverlay" }, // priority lookup - @"", + null, // expected circle and overlay @"hitcircle", @"hitcircleoverlay", }, From a96664295bfd6cec5adc4b9c222ca47707e5f207 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 19:48:44 +0300 Subject: [PATCH 0434/1276] Fix nullability preprocessor placed over the copyright header --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 2107de61b6..db4d643046 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -1,4 +1,3 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. @@ -17,6 +16,8 @@ using osu.Game.Skinning; using osuTK; using osuTK.Graphics; +#nullable enable + namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacyMainCirclePiece : CompositeDrawable From fd20c2bdcd7c174b27efded6d8e371ec2233ba1f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 20 Apr 2022 00:24:18 +0300 Subject: [PATCH 0435/1276] Change circle/overlay sprite fields to `protected` for better test assertion --- .../LegacyMainCirclePieceTest.cs | 25 ++++++++++++------- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 20 +++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs index c9eeef3c01..fd5b6fd752 100644 --- a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Tests { // available textures new[] { @"hitcircle", @"hitcircleoverlay" }, - // priority lookup + // priority lookup prefix null, // expected circle and overlay @"hitcircle", @"hitcircleoverlay", @@ -66,8 +66,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCaseSource(nameof(texture_priority_cases))] public void TestTexturePriorities(string[] textureFilenames, string priorityLookup, string? expectedCircle, string? expectedOverlay) { - Sprite? circleSprite = null; - Sprite? overlaySprite = null; + TestLegacyMainCirclePiece piece = null!; AddStep("load circle piece", () => { @@ -80,18 +79,26 @@ namespace osu.Game.Rulesets.Osu.Tests Child = new DependencyProvidingContainer { CachedDependencies = new (Type, object)[] { (typeof(ISkinSource), skin.Object) }, - Child = new LegacyMainCirclePiece(priorityLookup, false), + Child = piece = new TestLegacyMainCirclePiece(priorityLookup), }; var sprites = this.ChildrenOfType().Where(s => s.Texture.AssetName != null).DistinctBy(s => s.Texture.AssetName).ToArray(); Debug.Assert(sprites.Length <= 2); - - circleSprite = sprites.ElementAtOrDefault(0); - overlaySprite = sprites.ElementAtOrDefault(1); }); - AddAssert("check circle sprite", () => circleSprite?.Texture?.AssetName == expectedCircle); - AddAssert("check overlay sprite", () => overlaySprite?.Texture?.AssetName == expectedOverlay); + AddAssert("check circle sprite", () => piece.CircleSprite?.Texture?.AssetName == expectedCircle); + AddAssert("check overlay sprite", () => piece.OverlaySprite?.Texture?.AssetName == expectedOverlay); + } + + private class TestLegacyMainCirclePiece : LegacyMainCirclePiece + { + public new Sprite? CircleSprite => base.CircleSprite.ChildrenOfType().DistinctBy(s => s.Texture.AssetName).SingleOrDefault(); + public new Sprite? OverlaySprite => base.OverlaySprite.ChildrenOfType().DistinctBy(s => s.Texture.AssetName).SingleOrDefault(); + + public TestLegacyMainCirclePiece(string? priorityLookupPrefix) + : base(priorityLookupPrefix, false) + { + } } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index db4d643046..391147648f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -31,11 +31,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly bool hasNumber; - private Drawable hitCircleSprite = null!; + protected Drawable CircleSprite = null!; + protected Drawable OverlaySprite = null!; protected Container OverlayLayer { get; private set; } = null!; - private Drawable hitCircleOverlay = null!; private SkinnableSpriteText hitCircleText = null!; private readonly Bindable accentColour = new Bindable(); @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy InternalChildren = new[] { - hitCircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) }) + CircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) }) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Child = hitCircleOverlay = new KiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d)) + Child = OverlaySprite = new KiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy bool overlayAboveNumber = skin.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true; if (overlayAboveNumber) - OverlayLayer.ChangeChildDepth(hitCircleOverlay, float.MinValue); + OverlayLayer.ChangeChildDepth(OverlaySprite, float.MinValue); if (drawableOsuObject != null) { @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { base.LoadComplete(); - accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true); + accentColour.BindValueChanged(colour => CircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true); if (hasNumber) indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); @@ -136,11 +136,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy switch (state) { case ArmedState.Hit: - hitCircleSprite.FadeOut(legacy_fade_duration, Easing.Out); - hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + CircleSprite.FadeOut(legacy_fade_duration, Easing.Out); + CircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); - hitCircleOverlay.FadeOut(legacy_fade_duration, Easing.Out); - hitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + OverlaySprite.FadeOut(legacy_fade_duration, Easing.Out); + OverlaySprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); if (hasNumber) { From 858c8f927f7baccc9328cb85847d0ea510d2b0a9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 20 Apr 2022 00:27:02 +0300 Subject: [PATCH 0436/1276] Attach comment explaining purpose of `CallBase` --- osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs index fd5b6fd752..d8c10b814d 100644 --- a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs @@ -72,7 +72,11 @@ namespace osu.Game.Rulesets.Osu.Tests { var skin = new Mock(); + // shouldn't be required as GetTexture(string) calls GetTexture(string, WrapMode, WrapMode) by default, + // but moq doesn't handle that well, therefore explicitly requiring to use `CallBase`: + // https://github.com/moq/moq4/issues/972 skin.Setup(s => s.GetTexture(It.IsAny())).CallBase(); + skin.Setup(s => s.GetTexture(It.IsIn(textureFilenames), It.IsAny(), It.IsAny())) .Returns((string componentName, WrapMode _, WrapMode __) => new Texture(1, 1) { AssetName = componentName }); From 4f8065160684cbc49e7a20cad355663e245a5578 Mon Sep 17 00:00:00 2001 From: chickensalt Date: Wed, 20 Apr 2022 10:33:06 +1000 Subject: [PATCH 0437/1276] implement 2a3a0c1 for playlists as well --- osu.Game/Screens/Menu/ButtonSystem.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 6d09b6de9f..6d9941ba87 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -172,17 +172,7 @@ namespace osu.Game.Screens.Menu { if (api.State.Value != APIState.Online) { - notifications?.Post(new SimpleNotification - { - Text = "You gotta be online to view playlists 'yo!", - Icon = FontAwesome.Solid.Globe, - Activated = () => - { - loginOverlay?.Show(); - return true; - } - }); - + loginOverlay?.Show(); return; } From f5863c20307b8e4ad8123fb25641b588a782dbd7 Mon Sep 17 00:00:00 2001 From: chickensalt Date: Wed, 20 Apr 2022 13:40:02 +1000 Subject: [PATCH 0438/1276] remove unused import --- osu.Game/Screens/Menu/ButtonSystem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 6d9941ba87..b59e42551a 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -26,7 +26,6 @@ using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Online.API; using osu.Game.Overlays; -using osu.Game.Overlays.Notifications; using osuTK; using osuTK.Graphics; using osuTK.Input; From d46329f55a4fc651b34ac654e963eb79a6e73f23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 12:51:55 +0900 Subject: [PATCH 0439/1276] Remove unused dependency --- osu.Game/Screens/Menu/ButtonSystem.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index b59e42551a..2d18dce6da 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -116,9 +116,6 @@ namespace osu.Game.Screens.Menu [Resolved] private IAPIProvider api { get; set; } - [Resolved(CanBeNull = true)] - private INotificationOverlay notifications { get; set; } - [Resolved(CanBeNull = true)] private LoginOverlay loginOverlay { get; set; } From 488fc9db67b82baebdc59f71477569960580c439 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 14:48:51 +0900 Subject: [PATCH 0440/1276] Reverse content colours to match design spec --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index b118e61fba..7ad7e6f770 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -143,14 +143,14 @@ namespace osu.Game.Overlays { Text = FirstRunSetupOverlayStrings.FirstRunSetup, Font = OsuFont.Default.With(size: 32), - Colour = colourProvider.Content2, + Colour = colourProvider.Content1, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, new OsuTextFlowContainer { Text = FirstRunSetupOverlayStrings.SetupOsuToSuitYou, - Colour = colourProvider.Content1, + Colour = colourProvider.Content2, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, From 9797e2d8871682f9a036a625e5230f324788ec52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 14:49:17 +0900 Subject: [PATCH 0441/1276] Rename `showLastStep` method to avoid ambiguity --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 7ad7e6f770..0e8919fd82 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -200,7 +200,7 @@ namespace osu.Game.Overlays { Width = 200, Text = CommonStrings.Back, - Action = showLastStep, + Action = showPreviousStep, Enabled = { Value = false }, }, Empty(), @@ -336,7 +336,7 @@ namespace osu.Game.Overlays showNextStep(); } - private void showLastStep() + private void showPreviousStep() { if (currentStepIndex == 0) return; From 66373bf038f01e1ff5b26bdc134530db504ff6c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 14:50:41 +0900 Subject: [PATCH 0442/1276] Move back button enable handling to shared method --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 0e8919fd82..c74adbabae 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -346,8 +346,6 @@ namespace osu.Game.Overlays stack.CurrentScreen.Exit(); currentStepIndex--; - BackButton.Enabled.Value = currentStepIndex != 0; - updateButtonText(); } @@ -358,8 +356,6 @@ namespace osu.Game.Overlays currentStepIndex++; - BackButton.Enabled.Value = currentStepIndex > 0; - if (currentStepIndex < steps.Length) { stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value].ScreenType)); @@ -377,6 +373,8 @@ namespace osu.Game.Overlays { Debug.Assert(currentStepIndex != null); + BackButton.Enabled.Value = currentStepIndex != 0; + NextButton.Text = currentStepIndex + 1 < steps.Length ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description) : CommonStrings.Finish; From 7e7fa633f0b2fdde41d9a22c4ce052cb46762201 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 14:51:16 +0900 Subject: [PATCH 0443/1276] Use direct localised string reference in test --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 00f1d4ac4e..8a9ed50725 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Configuration; +using osu.Game.Localisation; using osu.Game.Overlays; using osu.Game.Overlays.FirstRunSetup; using osu.Game.Overlays.Notifications; @@ -126,7 +127,7 @@ namespace osu.Game.Tests.Visual.UserInterface if (overlay.CurrentScreen?.IsLoaded != false) nextButton.TriggerClick(); - return nextButton.Text.ToString() == "Finish"; + return nextButton.Text.ToString() == CommonStrings.Finish; }); AddUntilStep("step back to start", () => From 8d31b0bc015461a5c05bb7e0ff9747823cdded5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 15:05:07 +0900 Subject: [PATCH 0444/1276] Split out the base design of sheared overlay into its own abstract class This will allow for reuse with the first-run overlay. --- .../UserInterface/PopupScreenTitle.cs | 4 +- osu.Game/Overlays/Mods/ModSelectScreen.cs | 101 ++++--------- .../Overlays/Mods/ShearedOverlayContainer.cs | 136 ++++++++++++++++++ 3 files changed, 165 insertions(+), 76 deletions(-) create mode 100644 osu.Game/Overlays/Mods/ShearedOverlayContainer.cs diff --git a/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs b/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs index 5b7db09e77..2e5519726b 100644 --- a/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs @@ -21,6 +21,8 @@ namespace osu.Game.Graphics.UserInterface { public class PopupScreenTitle : CompositeDrawable { + public const float HEIGHT = main_area_height + 2 * corner_radius; + public LocalisableString Title { set => titleSpriteText.Text = value; @@ -67,7 +69,7 @@ namespace osu.Game.Graphics.UserInterface underlayContainer = new Container { RelativeSizeAxes = Axes.X, - Height = main_area_height + 2 * corner_radius, + Height = HEIGHT, CornerRadius = corner_radius, Masking = true, BorderThickness = 2, diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 62080ec1b5..6e8684478e 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Layout; using osu.Game.Configuration; @@ -21,39 +20,27 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public class ModSelectScreen : OsuFocusedOverlayContainer + public class ModSelectScreen : ShearedOverlayContainer { - [Cached] - private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green; [Cached] public Bindable> SelectedMods { get; private set; } = new Bindable>(Array.Empty()); - protected override bool StartHidden => true; - private readonly BindableBool customisationVisible = new BindableBool(); private DifficultyMultiplierDisplay multiplierDisplay; private ModSettingsArea modSettingsArea; private FillFlowContainer columnFlow; private GridContainer grid; - private Container mainContent; - - private PopupScreenTitle header; - private Container footer; [BackgroundDependencyLoader] private void load() { - RelativeSizeAxes = Axes.Both; - RelativePositionAxes = Axes.Both; - - InternalChildren = new Drawable[] + MainAreaContent.AddRange(new Drawable[] { - mainContent = new Container + new Container { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { @@ -62,24 +49,11 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.Both, RowDimensions = new[] { - new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize), new Dimension(), - new Dimension(GridSizeMode.Absolute, 75), }, Content = new[] { - new Drawable[] - { - header = new PopupScreenTitle - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Title = "Mod Select", - Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.", - Close = Hide - } - }, new Drawable[] { new Container @@ -120,6 +94,7 @@ namespace osu.Game.Overlays.Mods Child = columnFlow = new ModColumnContainer { Direction = FillDirection.Horizontal, + Shear = new Vector2(ModPanel.SHEAR_X, 0), RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Spacing = new Vector2(10, 0), @@ -137,52 +112,36 @@ namespace osu.Game.Overlays.Mods } } }, - new[] { Empty() } } }, - footer = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.X, - Height = 50, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Colour = colourProvider.Background5 - }, - new ShearedToggleButton(200) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = 14, Left = 70 }, - Text = "Mod Customisation", - Active = { BindTarget = customisationVisible } - } - } - }, - new ClickToReturnContainer - { - RelativeSizeAxes = Axes.Both, - HandleMouse = { BindTarget = customisationVisible }, - OnClicked = () => customisationVisible.Value = false - } } }, + }); + + Footer.Add(new ShearedToggleButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Vertical = 14, Left = 70 }, + Text = "Mod Customisation", + Active = { BindTarget = customisationVisible } + }); + + AddRange(new Drawable[] + { + new ClickToReturnContainer + { + RelativeSizeAxes = Axes.Both, + HandleMouse = { BindTarget = customisationVisible }, + OnClicked = () => customisationVisible.Value = false + }, modSettingsArea = new ModSettingsArea { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Height = 0 } - }; - - columnFlow.Shear = new Vector2(ModPanel.SHEAR_X, 0); + }); } protected override void LoadComplete() @@ -252,7 +211,7 @@ namespace osu.Game.Overlays.Mods float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0; modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic); - mainContent.TransformTo(nameof(Margin), new MarginPadding { Bottom = modAreaHeight }, transition_duration, Easing.InOutCubic); + TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic); } private bool selectionBindableSyncInProgress; @@ -287,10 +246,6 @@ namespace osu.Game.Overlays.Mods const double fade_in_duration = 400; base.PopIn(); - this.FadeIn(fade_in_duration, Easing.OutQuint); - - header.MoveToY(0, fade_in_duration, Easing.OutQuint); - footer.MoveToY(0, fade_in_duration, Easing.OutQuint); multiplierDisplay .Delay(fade_in_duration * 0.65f) @@ -311,15 +266,11 @@ namespace osu.Game.Overlays.Mods const double fade_out_duration = 500; base.PopOut(); - this.FadeOut(fade_out_duration, Easing.OutQuint); multiplierDisplay .FadeOut(fade_out_duration / 2, Easing.OutQuint) .ScaleTo(0.75f, fade_out_duration, Easing.OutQuint); - header.MoveToY(-header.DrawHeight, fade_out_duration, Easing.OutQuint); - footer.MoveToY(footer.DrawHeight, fade_out_duration, Easing.OutQuint); - for (int i = 0; i < columnFlow.Count; i++) { const float distance = 700; diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs new file mode 100644 index 0000000000..46a3ad3c8d --- /dev/null +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -0,0 +1,136 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Mods +{ + /// + /// A sheared overlay which provides a header and footer and basic animations. + /// Exposes , and as valid targets for content. + /// + public abstract class ShearedOverlayContainer : OsuFocusedOverlayContainer + { + [Cached] + protected readonly OverlayColourProvider ColourProvider; + + /// + /// The overlay's header. + /// + protected PopupScreenTitle Header { get; private set; } + + /// + /// The overlay's footer. + /// + protected Container Footer { get; private set; } + + /// + /// A container containing all content, including the header and footer. + /// May be used for overlay-wide animations. + /// + protected Container TopLevelContent { get; private set; } + + /// + /// A container for content that is to be displayed between the header and footer. + /// + protected Container MainAreaContent { get; private set; } + + /// + /// A container for content that is to be displayed inside the footer. + /// + protected Container FooterContent { get; private set; } + + protected abstract OverlayColourScheme ColourScheme { get; } + + protected override bool StartHidden => true; + + protected override bool BlockNonPositionalInput => true; + + protected ShearedOverlayContainer() + { + RelativeSizeAxes = Axes.Both; + + ColourProvider = new OverlayColourProvider(ColourScheme); + } + + [BackgroundDependencyLoader] + private void load() + { + const float footer_height = 50; + + Child = TopLevelContent = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + Header = new PopupScreenTitle + { + Anchor = Anchor.TopCentre, + Depth = float.MinValue, + Origin = Anchor.TopCentre, + Title = "Mod Select", + Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.", + Close = Hide + }, + MainAreaContent = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Top = PopupScreenTitle.HEIGHT, + Bottom = footer_height, + } + }, + Footer = new Container + { + RelativeSizeAxes = Axes.X, + Depth = float.MinValue, + Height = footer_height, + Margin = new MarginPadding { Top = 10 }, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourProvider.Background5 + }, + FooterContent = new Container + { + RelativeSizeAxes = Axes.Both, + }, + } + } + } + }; + } + + protected override void PopIn() + { + const double fade_in_duration = 400; + + base.PopIn(); + this.FadeIn(fade_in_duration, Easing.OutQuint); + + Header.MoveToY(0, fade_in_duration, Easing.OutQuint); + Footer.MoveToY(0, fade_in_duration, Easing.OutQuint); + } + + protected override void PopOut() + { + const double fade_out_duration = 500; + + base.PopOut(); + this.FadeOut(fade_out_duration, Easing.OutQuint); + + Header.MoveToY(-Header.DrawHeight, fade_out_duration, Easing.OutQuint); + Footer.MoveToY(Footer.DrawHeight, fade_out_duration, Easing.OutQuint); + } + } +} From 1032dc235dae06d61460c0fd019472bb4994ad04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 15:50:57 +0900 Subject: [PATCH 0445/1276] Rename `PopupScreenTitle` to `ShearedOverlayHeader` --- ...reenTitle.cs => TestSceneShearedOverlayHeader.cs} | 12 ++++++------ .../{PopupScreenTitle.cs => ShearedOverlayHeader.cs} | 4 ++-- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestScenePopupScreenTitle.cs => TestSceneShearedOverlayHeader.cs} (76%) rename osu.Game/Graphics/UserInterface/{PopupScreenTitle.cs => ShearedOverlayHeader.cs} (98%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupScreenTitle.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayHeader.cs similarity index 76% rename from osu.Game.Tests/Visual/UserInterface/TestScenePopupScreenTitle.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayHeader.cs index 22a8fa8a46..ef2b25cd92 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupScreenTitle.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayHeader.cs @@ -10,19 +10,19 @@ using osu.Game.Overlays; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestScenePopupScreenTitle : OsuTestScene + public class TestSceneShearedOverlayHeader : OsuTestScene { [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); [Test] - public void TestPopupScreenTitle() + public void TestShearedOverlayHeader() { AddStep("create content", () => { - Child = new PopupScreenTitle + Child = new ShearedOverlayHeader { - Title = "Popup Screen Title", + Title = "Sheared overlay header", Description = string.Join(" ", Enumerable.Repeat("This is a description.", 20)), Close = () => { } }; @@ -34,9 +34,9 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("create content", () => { - Child = new PopupScreenTitle + Child = new ShearedOverlayHeader { - Title = "Popup Screen Title", + Title = "Sheared overlay header", Description = "This is a description." }; }); diff --git a/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs b/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs similarity index 98% rename from osu.Game/Graphics/UserInterface/PopupScreenTitle.cs rename to osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs index 2e5519726b..9ed7bb35de 100644 --- a/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs @@ -19,7 +19,7 @@ using osuTK; namespace osu.Game.Graphics.UserInterface { - public class PopupScreenTitle : CompositeDrawable + public class ShearedOverlayHeader : CompositeDrawable { public const float HEIGHT = main_area_height + 2 * corner_radius; @@ -50,7 +50,7 @@ namespace osu.Game.Graphics.UserInterface private readonly OsuTextFlowContainer descriptionText; private readonly IconButton closeButton; - public PopupScreenTitle() + public ShearedOverlayHeader() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 46a3ad3c8d..b6f6cf1468 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Mods /// /// The overlay's header. /// - protected PopupScreenTitle Header { get; private set; } + protected ShearedOverlayHeader Header { get; private set; } /// /// The overlay's footer. @@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - Header = new PopupScreenTitle + Header = new ShearedOverlayHeader { Anchor = Anchor.TopCentre, Depth = float.MinValue, @@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Top = PopupScreenTitle.HEIGHT, + Top = ShearedOverlayHeader.HEIGHT, Bottom = footer_height, } }, From 2ee37aeceb5c30f1f36eaa94707a02484f4c217e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 15:57:45 +0900 Subject: [PATCH 0446/1276] Reduce nesting and usage of `GridContainer` --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 159 +++++++++------------- 1 file changed, 67 insertions(+), 92 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 6e8684478e..452c6e32a6 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -32,101 +32,10 @@ namespace osu.Game.Overlays.Mods private DifficultyMultiplierDisplay multiplierDisplay; private ModSettingsArea modSettingsArea; private FillFlowContainer columnFlow; - private GridContainer grid; [BackgroundDependencyLoader] private void load() { - MainAreaContent.AddRange(new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - grid = new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - }, - Content = new[] - { - new Drawable[] - { - new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.X, - RelativePositionAxes = Axes.X, - X = 0.3f, - Height = DifficultyMultiplierDisplay.HEIGHT, - Margin = new MarginPadding - { - Horizontal = 100, - Vertical = 10 - }, - Child = multiplierDisplay = new DifficultyMultiplierDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - } - } - }, - new Drawable[] - { - new Container - { - Depth = float.MaxValue, - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Both, - Children = new Drawable[] - { - new OsuScrollContainer(Direction.Horizontal) - { - RelativeSizeAxes = Axes.Both, - Masking = false, - ClampExtension = 100, - ScrollbarOverlapsContent = false, - Child = columnFlow = new ModColumnContainer - { - Direction = FillDirection.Horizontal, - Shear = new Vector2(ModPanel.SHEAR_X, 0), - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Spacing = new Vector2(10, 0), - Margin = new MarginPadding { Right = 70 }, - Children = new[] - { - new ModColumn(ModType.DifficultyReduction, false, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), - new ModColumn(ModType.DifficultyIncrease, false, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), - new ModColumn(ModType.Automation, false, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), - new ModColumn(ModType.Conversion, false), - new ModColumn(ModType.Fun, false) - } - } - } - } - } - }, - } - }, - } - }, - }); - - Footer.Add(new ShearedToggleButton(200) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = 14, Left = 70 }, - Text = "Mod Customisation", - Active = { BindTarget = customisationVisible } - }); - AddRange(new Drawable[] { new ClickToReturnContainer @@ -142,6 +51,72 @@ namespace osu.Game.Overlays.Mods Height = 0 } }); + + MainAreaContent.AddRange(new Drawable[] + { + new Container + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.X, + Height = DifficultyMultiplierDisplay.HEIGHT, + Margin = new MarginPadding + { + Horizontal = 100, + }, + Child = multiplierDisplay = new DifficultyMultiplierDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + } + }, + new Container + { + Margin = new MarginPadding + { + Vertical = DifficultyMultiplierDisplay.HEIGHT + 10, + }, + Depth = float.MaxValue, + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, + Children = new Drawable[] + { + new OsuScrollContainer(Direction.Horizontal) + { + RelativeSizeAxes = Axes.Both, + Masking = false, + ClampExtension = 100, + ScrollbarOverlapsContent = false, + Child = columnFlow = new ModColumnContainer + { + Direction = FillDirection.Horizontal, + Shear = new Vector2(ModPanel.SHEAR_X, 0), + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Spacing = new Vector2(10, 0), + Margin = new MarginPadding { Right = 70 }, + Children = new[] + { + new ModColumn(ModType.DifficultyReduction, false, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), + new ModColumn(ModType.DifficultyIncrease, false, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), + new ModColumn(ModType.Automation, false, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), + new ModColumn(ModType.Conversion, false), + new ModColumn(ModType.Fun, false) + } + } + } + } + } + }); + + Footer.Add(new ShearedToggleButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Vertical = 14, Left = 70 }, + Text = "Mod Customisation", + Active = { BindTarget = customisationVisible } + }); } protected override void LoadComplete() @@ -206,7 +181,7 @@ namespace osu.Game.Overlays.Mods { const double transition_duration = 300; - grid.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic); + MainAreaContent.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic); float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0; From 5c7ff363ce406ca8ef4c6c21bc6f76eb7b42f58d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 16:08:00 +0900 Subject: [PATCH 0447/1276] Move title/description to correct class --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 3 +++ osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 452c6e32a6..7c857d5a90 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -36,6 +36,9 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load() { + Header.Title = "Mod Select"; + Header.Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."; + AddRange(new Drawable[] { new ClickToReturnContainer diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index b6f6cf1468..76a81421fa 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -73,8 +73,6 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopCentre, Depth = float.MinValue, Origin = Anchor.TopCentre, - Title = "Mod Select", - Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.", Close = Hide }, MainAreaContent = new Container From e17f224793bc31ebf92b8ae5113f221d61d908b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 16:28:52 +0900 Subject: [PATCH 0448/1276] Fix padding mismatches --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 6 +++--- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 7c857d5a90..a9992d70bc 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -75,9 +75,9 @@ namespace osu.Game.Overlays.Mods }, new Container { - Margin = new MarginPadding + Padding = new MarginPadding { - Vertical = DifficultyMultiplierDisplay.HEIGHT + 10, + Top = DifficultyMultiplierDisplay.HEIGHT + PADDING, }, Depth = float.MaxValue, RelativeSizeAxes = Axes.Both, @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Mods { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = 14, Left = 70 }, + Margin = new MarginPadding { Vertical = PADDING, Left = 70 }, Text = "Mod Customisation", Active = { BindTarget = customisationVisible } }); diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 76a81421fa..0bdd4189a0 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -16,6 +16,8 @@ namespace osu.Game.Overlays.Mods /// public abstract class ShearedOverlayContainer : OsuFocusedOverlayContainer { + protected const float PADDING = 14; + [Cached] protected readonly OverlayColourProvider ColourProvider; @@ -81,7 +83,7 @@ namespace osu.Game.Overlays.Mods Padding = new MarginPadding { Top = ShearedOverlayHeader.HEIGHT, - Bottom = footer_height, + Bottom = footer_height + PADDING, } }, Footer = new Container @@ -89,7 +91,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.X, Depth = float.MinValue, Height = footer_height, - Margin = new MarginPadding { Top = 10 }, + Margin = new MarginPadding { Top = PADDING }, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Children = new Drawable[] From 65b2db5e71fc86dab1d1f2c7416123f002c83d93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 16:30:58 +0900 Subject: [PATCH 0449/1276] Move shear constant to overlay (this is going to likely be used everywhere ever) --- osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs | 6 +++--- osu.Game/Overlays/Mods/ModColumn.cs | 8 ++++---- osu.Game/Overlays/Mods/ModPanel.cs | 11 +++++------ osu.Game/Overlays/Mods/ModSelectScreen.cs | 4 ++-- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 2 ++ 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 248d4f288e..66fd6a202d 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.X, Masking = true, CornerRadius = ModPanel.CORNER_RADIUS, - Shear = new Vector2(ModPanel.SHEAR_X, 0), + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), Children = new Drawable[] { underlayBackground = new Box @@ -98,7 +98,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.Centre, Origin = Anchor.Centre, Margin = new MarginPadding { Horizontal = 18 }, - Shear = new Vector2(-ModPanel.SHEAR_X, 0), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Text = "Difficulty Multiplier", Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) } @@ -109,7 +109,7 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = new Vector2(-ModPanel.SHEAR_X, 0), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Direction = FillDirection.Horizontal, Spacing = new Vector2(2, 0), Children = new Drawable[] diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index f84ae4ac8a..78b4e24a4e 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Mods Width = 320; RelativeSizeAxes = Axes.Y; - Shear = new Vector2(ModPanel.SHEAR_X, 0); + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); Container controlContainer; InternalChildren = new Drawable[] @@ -113,7 +113,7 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.Y, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Shear = new Vector2(-ModPanel.SHEAR_X, 0), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Padding = new MarginPadding { Horizontal = 17, @@ -193,7 +193,7 @@ namespace osu.Game.Overlays.Mods Scale = new Vector2(0.8f), RelativeSizeAxes = Axes.X, LabelText = "Enable All", - Shear = new Vector2(-ModPanel.SHEAR_X, 0) + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) }); panelFlow.Padding = new MarginPadding { @@ -260,7 +260,7 @@ namespace osu.Game.Overlays.Mods var panels = newMods.Select(mod => new ModPanel(mod) { - Shear = new Vector2(-ModPanel.SHEAR_X, 0) + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) }); Task? loadTask; diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index 312171cf74..7c4f2dcb7e 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -42,7 +42,6 @@ namespace osu.Game.Overlays.Mods protected const double TRANSITION_DURATION = 150; - public const float SHEAR_X = 0.2f; public const float CORNER_RADIUS = 7; protected const float HEIGHT = 42; @@ -67,7 +66,7 @@ namespace osu.Game.Overlays.Mods Content.Masking = true; Content.CornerRadius = CORNER_RADIUS; Content.BorderThickness = 2; - Content.Shear = new Vector2(SHEAR_X, 0); + Content.Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); Children = new Drawable[] { @@ -83,7 +82,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.Centre, Origin = Anchor.Centre, Active = { BindTarget = Active }, - Shear = new Vector2(-SHEAR_X, 0), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Scale = new Vector2(HEIGHT / ModSwitchSmall.DEFAULT_SIZE) } }, @@ -116,10 +115,10 @@ namespace osu.Game.Overlays.Mods { Text = mod.Name, Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold), - Shear = new Vector2(-SHEAR_X, 0), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Margin = new MarginPadding { - Left = -18 * SHEAR_X + Left = -18 * ShearedOverlayContainer.SHEAR } }, new OsuSpriteText @@ -128,7 +127,7 @@ namespace osu.Game.Overlays.Mods Font = OsuFont.Default.With(size: 12), RelativeSizeAxes = Axes.X, Truncate = true, - Shear = new Vector2(-SHEAR_X, 0) + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) } } } diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index a9992d70bc..94f877a08c 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -93,7 +93,7 @@ namespace osu.Game.Overlays.Mods Child = columnFlow = new ModColumnContainer { Direction = FillDirection.Horizontal, - Shear = new Vector2(ModPanel.SHEAR_X, 0), + Shear = new Vector2(SHEAR, 0), RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Spacing = new Vector2(10, 0), @@ -284,7 +284,7 @@ namespace osu.Game.Overlays.Mods { Padding = new MarginPadding { - Left = DrawHeight * ModPanel.SHEAR_X, + Left = DrawHeight * SHEAR, Bottom = 10 }; diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 0bdd4189a0..62ed736dc2 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -18,6 +18,8 @@ namespace osu.Game.Overlays.Mods { protected const float PADDING = 14; + public const float SHEAR = 0.2f; + [Cached] protected readonly OverlayColourProvider ColourProvider; From 8c2d70e93fb01036538638dc50f399cc0bffa023 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 17:50:31 +0900 Subject: [PATCH 0450/1276] Disable forward button when already at end --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index c74adbabae..d5aa1178e0 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -346,7 +346,7 @@ namespace osu.Game.Overlays stack.CurrentScreen.Exit(); currentStepIndex--; - updateButtonText(); + updateButtons(); } private void showNextStep() @@ -359,7 +359,6 @@ namespace osu.Game.Overlays if (currentStepIndex < steps.Length) { stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value].ScreenType)); - updateButtonText(); } else { @@ -367,17 +366,21 @@ namespace osu.Game.Overlays currentStepIndex = null; Hide(); } + + updateButtons(); } - private void updateButtonText() + private void updateButtons() { - Debug.Assert(currentStepIndex != null); + BackButton.Enabled.Value = currentStepIndex > 0; + NextButton.Enabled.Value = currentStepIndex != null; - BackButton.Enabled.Value = currentStepIndex != 0; - - NextButton.Text = currentStepIndex + 1 < steps.Length - ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description) - : CommonStrings.Finish; + if (currentStepIndex != null) + { + NextButton.Text = currentStepIndex + 1 < steps.Length + ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description) + : CommonStrings.Finish; + } } private class FirstRunStep From 2906af323af33c835473b28bdc67ff88cb168cb1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 17:51:08 +0900 Subject: [PATCH 0451/1276] Fix incorrectly written string equality logic --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 8a9ed50725..efce4f350b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual.UserInterface if (overlay.CurrentScreen?.IsLoaded != false) nextButton.TriggerClick(); - return nextButton.Text.ToString() == CommonStrings.Finish; + return nextButton.Text == CommonStrings.Finish; }); AddUntilStep("step back to start", () => From 4466e15bfcf3a0b7c93ba3c9cfc1da87509a6393 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 23:17:29 +0900 Subject: [PATCH 0452/1276] Rename `AllowConfiguration` to `AllowCustomisation` and simplify drawable addition --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 57 ++++++++++--------- .../Screens/OnlinePlay/FreeModSelectScreen.cs | 2 +- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 41cbfdafb3..693c85fafc 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Mods /// /// Whether configurable s can be configured by the local user. /// - protected virtual bool AllowConfiguration => true; + protected virtual bool AllowCustomisation => true; /// /// Whether the total score multiplier calculated from the current selected set of mods should be shown. @@ -85,29 +85,12 @@ namespace osu.Game.Overlays.Mods MainAreaContent.AddRange(new Drawable[] { - new Container - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.X, - Height = DifficultyMultiplierDisplay.HEIGHT, - Margin = new MarginPadding - { - Horizontal = 100, - }, - Child = multiplierDisplay = new DifficultyMultiplierDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - } - }, new Container { Padding = new MarginPadding { - Top = DifficultyMultiplierDisplay.HEIGHT + PADDING, + Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING, }, - Depth = float.MaxValue, RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, Children = new Drawable[] @@ -140,14 +123,34 @@ namespace osu.Game.Overlays.Mods } }); - Footer.Add(new ShearedToggleButton(200) + if (ShowTotalMultiplier) { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = PADDING, Left = 70 }, - Text = "Mod Customisation", - Active = { BindTarget = customisationVisible } - }); + MainAreaContent.Add(new Container + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.X, + Height = DifficultyMultiplierDisplay.HEIGHT, + Margin = new MarginPadding { Horizontal = 100 }, + Child = multiplierDisplay = new DifficultyMultiplierDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }, + }); + } + + if (AllowCustomisation) + { + Footer.Add(new ShearedToggleButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Vertical = PADDING, Left = 70 }, + Text = "Mod Customisation", + Active = { BindTarget = customisationVisible } + }); + } } protected override void LoadComplete() @@ -194,7 +197,7 @@ namespace osu.Game.Overlays.Mods private void updateCustomisation(ValueChangedEvent> valueChangedEvent) { - if (!AllowConfiguration) + if (!AllowCustomisation) return; bool anyCustomisableMod = false; diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs index 438b334e0b..c85a4fc38b 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -10,7 +10,7 @@ namespace osu.Game.Screens.OnlinePlay { public class FreeModSelectScreen : ModSelectScreen { - protected override bool AllowConfiguration => false; + protected override bool AllowCustomisation => false; protected override bool ShowTotalMultiplier => false; public new Func IsValidMod From e596c9d1713f82f3000f1c5029ee20d4a96ebdb3 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 20 Apr 2022 19:12:43 +0100 Subject: [PATCH 0453/1276] Use `Bindable` to control selector active visibility --- .../Visual/Online/TestSceneChannelList.cs | 9 +++-- .../Overlays/Chat/ChannelList/ChannelList.cs | 8 +++- .../Chat/ChannelList/ChannelListItem.cs | 38 +++++++++++-------- .../Chat/ChannelList/ChannelListSelector.cs | 14 ++++++- 4 files changed, 46 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs index d6a1e71f0d..2bfe42e6d4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -25,6 +25,9 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly Bindable selected = new Bindable(); + [Cached] + private readonly Bindable selector = new Bindable(); + private OsuSpriteText selectorText; private OsuSpriteText selectedText; private OsuSpriteText leaveText; @@ -89,7 +92,7 @@ namespace osu.Game.Tests.Visual.Online channelList.OnRequestSelect += channel => { - channelList.SelectorActive.Value = false; + selector.Value = ChannelSelectorState.Hidden; selected.Value = channel; }; @@ -101,9 +104,9 @@ namespace osu.Game.Tests.Visual.Online channelList.RemoveChannel(channel); }; - channelList.SelectorActive.BindValueChanged(change => + selector.BindValueChanged(change => { - selectorText.Text = $"Channel Selector Active: {change.NewValue}"; + selectorText.Text = $"Channel Selector State: {change.NewValue}"; }, true); selected.BindValueChanged(change => diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 94c1a99a81..578ac2aa45 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -14,7 +14,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; -using osuTK; namespace osu.Game.Overlays.Chat.ChannelList { @@ -57,7 +56,6 @@ namespace osu.Game.Overlays.Chat.ChannelList new ChannelListSelector { Margin = new MarginPadding { Bottom = 10 }, - SelectorActive = { BindTarget = SelectorActive }, }, privateChannelFlow = new ChannelListItemFlow("DIRECT MESSAGES"), }, @@ -132,4 +130,10 @@ namespace osu.Game.Overlays.Chat.ChannelList } } } + + public enum ChannelSelectorState + { + Visibile, + Hidden, + } } diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index 43574351ed..3d59d1dc4c 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -31,14 +31,17 @@ namespace osu.Game.Overlays.Chat.ChannelList private readonly Channel channel; - private Box? hoverBox; - private Box? selectBox; - private OsuSpriteText? text; - private ChannelListItemCloseButton? close; + private Box hoverBox = null!; + private Box selectBox = null!; + private OsuSpriteText text = null!; + private ChannelListItemCloseButton close = null!; [Resolved] private Bindable selectedChannel { get; set; } = null!; + [Resolved] + private Bindable selectorState { get; set; } = null!; + [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -124,31 +127,26 @@ namespace osu.Game.Overlays.Chat.ChannelList { base.LoadComplete(); - selectedChannel.BindValueChanged(change => - { - if (change.NewValue == channel) - selectBox?.FadeIn(300, Easing.OutQuint); - else - selectBox?.FadeOut(200, Easing.OutQuint); - }, true); + selectedChannel.BindValueChanged(_ => updateSelectState(), true); + selectorState.BindValueChanged(_ => updateSelectState(), true); Unread.BindValueChanged(change => { - text!.FadeColour(change.NewValue ? colourProvider.Content1 : colourProvider.Light3, 300, Easing.OutQuint); + text.FadeColour(change.NewValue ? colourProvider.Content1 : colourProvider.Light3, 300, Easing.OutQuint); }, true); } protected override bool OnHover(HoverEvent e) { - hoverBox?.FadeIn(300, Easing.OutQuint); - close?.FadeIn(300, Easing.OutQuint); + hoverBox.FadeIn(300, Easing.OutQuint); + close.FadeIn(300, Easing.OutQuint); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - hoverBox?.FadeOut(200, Easing.OutQuint); - close?.FadeOut(200, Easing.OutQuint); + hoverBox.FadeOut(200, Easing.OutQuint); + close.FadeOut(200, Easing.OutQuint); base.OnHoverLost(e); } @@ -167,5 +165,13 @@ namespace osu.Game.Overlays.Chat.ChannelList Masking = true, }; } + + private void updateSelectState() + { + if (selectedChannel.Value == channel && selectorState.Value == ChannelSelectorState.Hidden) + selectBox.FadeIn(300, Easing.OutQuint); + else + selectBox.FadeOut(200, Easing.OutQuint); + } } } diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs index 5bc9a01598..4529a34f84 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs @@ -22,6 +22,9 @@ namespace osu.Game.Overlays.Chat.ChannelList private Box hoverBox = null!; private Box selectBox = null!; + [Resolved] + private Bindable selectorState { get; set; } = null!; + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { @@ -65,8 +68,15 @@ namespace osu.Game.Overlays.Chat.ChannelList { base.LoadComplete(); - SelectorActive.BindValueChanged(selected => selectBox.FadeTo(selected.NewValue ? 1 : 0)); - Action = () => SelectorActive.Value = true; + selectorState.BindValueChanged(selector => + { + if (selector.NewValue == ChannelSelectorState.Visibile) + selectBox.FadeIn(300, Easing.OutQuint); + else + selectBox.FadeOut(200, Easing.OutQuint); + }, true); + + Action = () => selectorState.Value = ChannelSelectorState.Visibile; } protected override bool OnHover(HoverEvent e) From 5319bce772d9e91857108628997c78a8284c79fc Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 20 Apr 2022 21:05:33 +0100 Subject: [PATCH 0454/1276] Revert DI usage of `ChannelSelectorState` in favour of directly binding `BindableBool` `SelectorActive` --- osu.Game.Tests/Visual/Online/TestSceneChannelList.cs | 9 +++------ osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 8 ++------ osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs | 9 ++++----- .../Overlays/Chat/ChannelList/ChannelListSelector.cs | 9 +++------ 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs index 2bfe42e6d4..d6a1e71f0d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -25,9 +25,6 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly Bindable selected = new Bindable(); - [Cached] - private readonly Bindable selector = new Bindable(); - private OsuSpriteText selectorText; private OsuSpriteText selectedText; private OsuSpriteText leaveText; @@ -92,7 +89,7 @@ namespace osu.Game.Tests.Visual.Online channelList.OnRequestSelect += channel => { - selector.Value = ChannelSelectorState.Hidden; + channelList.SelectorActive.Value = false; selected.Value = channel; }; @@ -104,9 +101,9 @@ namespace osu.Game.Tests.Visual.Online channelList.RemoveChannel(channel); }; - selector.BindValueChanged(change => + channelList.SelectorActive.BindValueChanged(change => { - selectorText.Text = $"Channel Selector State: {change.NewValue}"; + selectorText.Text = $"Channel Selector Active: {change.NewValue}"; }, true); selected.BindValueChanged(change => diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 578ac2aa45..076dc5719e 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -56,6 +56,7 @@ namespace osu.Game.Overlays.Chat.ChannelList new ChannelListSelector { Margin = new MarginPadding { Bottom = 10 }, + SelectorActive = { BindTarget = SelectorActive }, }, privateChannelFlow = new ChannelListItemFlow("DIRECT MESSAGES"), }, @@ -72,6 +73,7 @@ namespace osu.Game.Overlays.Chat.ChannelList ChannelListItem item = new ChannelListItem(channel); item.OnRequestSelect += chan => OnRequestSelect?.Invoke(chan); item.OnRequestLeave += chan => OnRequestLeave?.Invoke(chan); + item.SelectorActive.BindTarget = SelectorActive; ChannelListItemFlow flow = getFlowForChannel(channel); channelMap.Add(channel, item); @@ -130,10 +132,4 @@ namespace osu.Game.Overlays.Chat.ChannelList } } } - - public enum ChannelSelectorState - { - Visibile, - Hidden, - } } diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index 3d59d1dc4c..7c4a72559b 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -29,6 +29,8 @@ namespace osu.Game.Overlays.Chat.ChannelList public readonly BindableBool Unread = new BindableBool(); + public readonly BindableBool SelectorActive = new BindableBool(); + private readonly Channel channel; private Box hoverBox = null!; @@ -39,9 +41,6 @@ namespace osu.Game.Overlays.Chat.ChannelList [Resolved] private Bindable selectedChannel { get; set; } = null!; - [Resolved] - private Bindable selectorState { get; set; } = null!; - [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -128,7 +127,7 @@ namespace osu.Game.Overlays.Chat.ChannelList base.LoadComplete(); selectedChannel.BindValueChanged(_ => updateSelectState(), true); - selectorState.BindValueChanged(_ => updateSelectState(), true); + SelectorActive.BindValueChanged(_ => updateSelectState(), true); Unread.BindValueChanged(change => { @@ -168,7 +167,7 @@ namespace osu.Game.Overlays.Chat.ChannelList private void updateSelectState() { - if (selectedChannel.Value == channel && selectorState.Value == ChannelSelectorState.Hidden) + if (selectedChannel.Value == channel && !SelectorActive.Value) selectBox.FadeIn(300, Easing.OutQuint); else selectBox.FadeOut(200, Easing.OutQuint); diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs index 4529a34f84..57ab7584b5 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs @@ -22,9 +22,6 @@ namespace osu.Game.Overlays.Chat.ChannelList private Box hoverBox = null!; private Box selectBox = null!; - [Resolved] - private Bindable selectorState { get; set; } = null!; - [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { @@ -68,15 +65,15 @@ namespace osu.Game.Overlays.Chat.ChannelList { base.LoadComplete(); - selectorState.BindValueChanged(selector => + SelectorActive.BindValueChanged(selector => { - if (selector.NewValue == ChannelSelectorState.Visibile) + if (selector.NewValue) selectBox.FadeIn(300, Easing.OutQuint); else selectBox.FadeOut(200, Easing.OutQuint); }, true); - Action = () => selectorState.Value = ChannelSelectorState.Visibile; + Action = () => SelectorActive.Value = true; } protected override bool OnHover(HoverEvent e) From 5e5c8e78a6538b4bcda713375f59b1b2828ec9fa Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 27 Jan 2022 20:53:48 -0800 Subject: [PATCH 0455/1276] Use existing web localisation for most hardcoded strings --- osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs | 5 +++-- osu.Game/Beatmaps/BeatmapStatistic.cs | 3 ++- .../Drawables/BeatmapDownloadButton.cs | 3 ++- osu.Game/Graphics/UserInterface/OsuMenuItem.cs | 3 ++- .../UserInterface/PageSelector/PageSelector.cs | 5 +++-- .../PageSelector/PageSelectorPrevNextButton.cs | 6 ++++-- .../Graphics/UserInterface/SearchTextBox.cs | 3 ++- .../Graphics/UserInterface/ShowMoreButton.cs | 4 +++- .../Graphics/UserInterfaceV2/ColourDisplay.cs | 3 ++- osu.Game/Localisation/CommonStrings.cs | 10 ---------- .../API/Requests/Responses/APIPlayStyle.cs | 11 ++++++----- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 3 ++- .../Online/Leaderboards/LeaderboardScore.cs | 11 ++++++----- osu.Game/Online/Rooms/MatchType.cs | 7 ++++--- .../Overlays/AccountCreation/ScreenEntry.cs | 3 ++- .../Overlays/BeatmapSet/BeatmapAvailability.cs | 7 ++++--- .../Scores/NotSupporterPlaceholder.cs | 3 ++- osu.Game/Overlays/ChatOverlay.cs | 2 +- .../Comments/Buttons/LoadRepliesButton.cs | 3 ++- .../Comments/Buttons/ShowMoreRepliesButton.cs | 3 ++- .../Comments/CancellableCommentEditor.cs | 3 ++- .../Overlays/Comments/CommentsContainer.cs | 3 ++- osu.Game/Overlays/Comments/CommentsHeader.cs | 10 ++++++++-- .../Comments/CommentsShowMoreButton.cs | 6 +++++- osu.Game/Overlays/Comments/DrawableComment.cs | 2 +- .../Overlays/Comments/TotalCommentsCounter.cs | 3 ++- .../Dashboard/CurrentlyPlayingDisplay.cs | 3 ++- .../Dashboard/Home/DrawableBeatmapList.cs | 3 ++- .../Dashboard/Home/DrawableNewBeatmapList.cs | 4 +++- .../Home/DrawablePopularBeatmapList.cs | 4 +++- .../Dashboard/Home/News/ShowMoreNewsPanel.cs | 3 ++- osu.Game/Overlays/Dialog/ConfirmDialog.cs | 3 ++- osu.Game/Overlays/Login/LoginForm.cs | 7 ++++--- osu.Game/Overlays/Login/UserAction.cs | 3 +++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 ++- osu.Game/Overlays/NotificationOverlay.cs | 5 +++-- .../Notifications/NotificationSection.cs | 7 ++++--- .../Profile/Header/BottomHeaderContainer.cs | 18 ++++++++++++++---- .../Settings/Sections/Input/KeyBindingRow.cs | 6 +++--- osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 3 ++- .../Wiki/Markdown/WikiNoticeContainer.cs | 8 +++++--- osu.Game/Overlays/Wiki/WikiSidebar.cs | 4 +++- .../Compose/Components/SelectionHandler.cs | 3 ++- osu.Game/Screens/Edit/Editor.cs | 3 ++- .../Screens/Edit/Setup/DifficultySection.cs | 9 +++++---- osu.Game/Screens/Edit/Setup/MetadataSection.cs | 7 ++++--- .../OnlinePlay/Components/OverlinedHeader.cs | 3 ++- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 3 ++- .../Match/Components/MatchLeaderboardScore.cs | 5 +++-- .../OnlinePlay/Match/DrawableMatchRoom.cs | 3 ++- .../Participants/ParticipantsListHeader.cs | 3 ++- .../Screens/Play/BeatmapMetadataDisplay.cs | 9 +++++---- osu.Game/Screens/Play/Break/BreakInfo.cs | 3 ++- osu.Game/Screens/Play/Break/BreakInfoLine.cs | 4 ++-- .../Play/HUD/PerformancePointsCounter.cs | 3 ++- .../Play/PlayerSettings/AudioSettings.cs | 3 ++- .../Play/PlayerSettings/VisualSettings.cs | 11 ++++++----- .../Contracted/ContractedPanelMiddleContent.cs | 8 +++++--- .../Expanded/Statistics/AccuracyStatistic.cs | 3 ++- .../Expanded/Statistics/ComboStatistic.cs | 3 ++- .../Expanded/Statistics/CounterStatistic.cs | 3 ++- .../Statistics/PerformanceStatistic.cs | 3 ++- .../Expanded/Statistics/StatisticDisplay.cs | 8 +++++--- osu.Game/Screens/Select/BeatmapDetails.cs | 3 ++- .../Select/Carousel/DrawableCarouselBeatmap.cs | 14 ++++---------- .../Screens/Select/Details/AdvancedStats.cs | 13 +++++++------ osu.Game/Screens/Select/Filter/SortMode.cs | 16 +++++++++------- osu.Game/Screens/Select/FilterControl.cs | 3 ++- .../Select/Options/BeatmapOptionsOverlay.cs | 3 ++- osu.Game/Users/UserStatus.cs | 10 ++++++---- 70 files changed, 224 insertions(+), 147 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs index 2d3cc3c103..a5282877ee 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Beatmaps @@ -20,13 +21,13 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { new BeatmapStatistic { - Name = @"Circle Count", + Name = BeatmapsetsStrings.ShowStatsCountCircles, Content = circles.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), }, new BeatmapStatistic { - Name = @"Slider Count", + Name = BeatmapsetsStrings.ShowStatsCountSliders, Content = sliders.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), }, diff --git a/osu.Game/Beatmaps/BeatmapStatistic.cs b/osu.Game/Beatmaps/BeatmapStatistic.cs index 7d7ba09fcf..94ebb56a5c 100644 --- a/osu.Game/Beatmaps/BeatmapStatistic.cs +++ b/osu.Game/Beatmaps/BeatmapStatistic.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics; +using osu.Framework.Localisation; namespace osu.Game.Beatmaps { @@ -14,6 +15,6 @@ namespace osu.Game.Beatmaps public Func CreateIcon; public string Content; - public string Name; + public LocalisableString Name; } } diff --git a/osu.Game/Beatmaps/Drawables/BeatmapDownloadButton.cs b/osu.Game/Beatmaps/Drawables/BeatmapDownloadButton.cs index e2485e7a77..6ab92a2ba2 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapDownloadButton.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapDownloadButton.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Beatmaps.Drawables { @@ -104,7 +105,7 @@ namespace osu.Game.Beatmaps.Drawables if ((beatmapSet as IBeatmapSetOnlineInfo)?.Availability.DownloadDisabled == true) { button.Enabled.Value = false; - button.TooltipText = "this beatmap is currently not available for download."; + button.TooltipText = BeatmapsetsStrings.AvailabilityDisabled; } break; diff --git a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs index 0fe41937ce..1da60415ba 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; namespace osu.Game.Graphics.UserInterface { @@ -15,7 +16,7 @@ namespace osu.Game.Graphics.UserInterface { } - public OsuMenuItem(string text, MenuItemType type, Action action) + public OsuMenuItem(LocalisableString text, MenuItemType type, Action action) : base(text, action) { Type = type; diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index 005729580c..5c6d087279 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Framework.Bindables; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Graphics.UserInterface.PageSelector { @@ -29,7 +30,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector Direction = FillDirection.Horizontal, Children = new Drawable[] { - previousPageButton = new PageSelectorPrevNextButton(false, "prev") + previousPageButton = new PageSelectorPrevNextButton(false, CommonStrings.PaginationPrevious) { Action = () => CurrentPage.Value -= 1, }, @@ -38,7 +39,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, }, - nextPageButton = new PageSelectorPrevNextButton(true, "next") + nextPageButton = new PageSelectorPrevNextButton(true, CommonStrings.PaginationNext) { Action = () => CurrentPage.Value += 1 } diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs index 7503ab8135..889917c397 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; using osuTK; @@ -13,12 +15,12 @@ namespace osu.Game.Graphics.UserInterface.PageSelector public class PageSelectorPrevNextButton : PageSelectorButton { private readonly bool rightAligned; - private readonly string text; + private readonly LocalisableString text; private SpriteIcon icon; private OsuSpriteText name; - public PageSelectorPrevNextButton(bool rightAligned, string text) + public PageSelectorPrevNextButton(bool rightAligned, LocalisableString text) { this.rightAligned = rightAligned; this.text = text; diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index 6937782be6..dd9ed7c9e9 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Input; @@ -27,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface }); TextFlow.Padding = new MarginPadding { Right = 35 }; - PlaceholderText = "type to search"; + PlaceholderText = HomeStrings.SearchPlaceholder; } public override bool OnPressed(KeyBindingPressEvent e) diff --git a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs index 615895074c..05dda324d4 100644 --- a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs +++ b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs @@ -11,7 +11,9 @@ using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osuTK; using System.Collections.Generic; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Graphics.UserInterface { @@ -80,7 +82,7 @@ namespace osu.Game.Graphics.UserInterface Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), - Text = "show more".ToUpper(), + Text = CommonStrings.ButtonsShowMore.ToUpper(), }, rightIcon = new ChevronIcon { diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index 5240df74a2..cec319f28e 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -14,6 +14,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osuTK; namespace osu.Game.Graphics.UserInterfaceV2 @@ -139,7 +140,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 public MenuItem[] ContextMenuItems => new MenuItem[] { - new OsuMenuItem("Delete", MenuItemType.Destructive, () => DeleteRequested?.Invoke()) + new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => DeleteRequested?.Invoke()) }; } } diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 3ea337c279..b717bb83dd 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -9,16 +9,6 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.Common"; - /// - /// "Cancel" - /// - public static LocalisableString Cancel => new TranslatableString(getKey(@"cancel"), @"Cancel"); - - /// - /// "Clear" - /// - public static LocalisableString Clear => new TranslatableString(getKey(@"clear"), @"Clear"); - /// /// "Enabled" /// diff --git a/osu.Game/Online/API/Requests/Responses/APIPlayStyle.cs b/osu.Game/Online/API/Requests/Responses/APIPlayStyle.cs index 9573ae1825..a9d66f3d6a 100644 --- a/osu.Game/Online/API/Requests/Responses/APIPlayStyle.cs +++ b/osu.Game/Online/API/Requests/Responses/APIPlayStyle.cs @@ -1,22 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.ComponentModel; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Online.API.Requests.Responses { public enum APIPlayStyle { - [Description("Keyboard")] + [LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceKeyboard))] Keyboard, - [Description("Mouse")] + [LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceMouse))] Mouse, - [Description("Tablet")] + [LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceTablet))] Tablet, - [Description("Touch Screen")] + [LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceTouch))] Touch, } } diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index f83bf4877e..6a7da52416 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -12,6 +12,7 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Chat; +using osu.Game.Resources.Localisation.Web; using osuTK.Graphics; namespace osu.Game.Online.Chat @@ -63,7 +64,7 @@ namespace osu.Game.Online.Chat { RelativeSizeAxes = Axes.X, Height = text_box_height, - PlaceholderText = "type your message", + PlaceholderText = ChatStrings.InputPlaceholder, CornerRadius = corner_radius, ReleaseFocusOnCommit = false, HoldFocus = true, diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index eecb7ff6b3..c75e98cdaa 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -30,6 +30,7 @@ using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; using osu.Game.Online.API; +using osu.Game.Resources.Localisation.Web; using osu.Game.Utils; namespace osu.Game.Online.Leaderboards @@ -291,8 +292,8 @@ namespace osu.Game.Online.Leaderboards protected virtual IEnumerable GetStatistics(ScoreInfo model) => new[] { - new LeaderboardScoreStatistic(FontAwesome.Solid.Link, "Max Combo", model.MaxCombo.ToString()), - new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", model.DisplayAccuracy) + new LeaderboardScoreStatistic(FontAwesome.Solid.Link, BeatmapsetsStrings.ShowScoreboardHeadersCombo, model.MaxCombo.ToString()), + new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, BeatmapsetsStrings.ShowScoreboardHeadersAccuracy, model.DisplayAccuracy) }; protected override bool OnHover(HoverEvent e) @@ -403,9 +404,9 @@ namespace osu.Game.Online.Leaderboards { public IconUsage Icon; public LocalisableString Value; - public string Name; + public LocalisableString Name; - public LeaderboardScoreStatistic(IconUsage icon, string name, LocalisableString value) + public LeaderboardScoreStatistic(IconUsage icon, LocalisableString name, LocalisableString value) { Icon = icon; Name = name; @@ -426,7 +427,7 @@ namespace osu.Game.Online.Leaderboards items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => new LegacyScoreExporter(storage).Export(Score))); if (!isOnlineScope) - items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score)))); + items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score)))); return items.ToArray(); } diff --git a/osu.Game/Online/Rooms/MatchType.cs b/osu.Game/Online/Rooms/MatchType.cs index 36f0dc0c81..278f0693eb 100644 --- a/osu.Game/Online/Rooms/MatchType.cs +++ b/osu.Game/Online/Rooms/MatchType.cs @@ -1,7 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.ComponentModel; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Online.Rooms { @@ -11,10 +12,10 @@ namespace osu.Game.Online.Rooms Playlists, - [Description("Head to head")] + [LocalisableDescription(typeof(MatchesStrings), nameof(MatchesStrings.MatchTeamTypesHeadToHead))] HeadToHead, - [Description("Team VS")] + [LocalisableDescription(typeof(MatchesStrings), nameof(MatchesStrings.MatchTeamTypesTeamVs))] TeamVersus, } } diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index a2c04c6989..6cf3fb4267 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -16,6 +16,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays.Settings; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Graphics; @@ -68,7 +69,7 @@ namespace osu.Game.Overlays.AccountCreation }, usernameTextBox = new OsuTextBox { - PlaceholderText = "username", + PlaceholderText = UsersStrings.LoginUsername, RelativeSizeAxes = Axes.X, TabbableContentContainer = this }, diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs b/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs index dc46452dcb..b6e768d632 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet @@ -69,14 +70,14 @@ namespace osu.Game.Overlays.BeatmapSet { textContainer.Clear(); textContainer.AddParagraph(downloadDisabled - ? "This beatmap is currently not available for download." - : "Portions of this beatmap have been removed at the request of the creator or a third-party rights holder.", t => t.Colour = Color4.Orange); + ? BeatmapsetsStrings.AvailabilityDisabled + : BeatmapsetsStrings.AvailabilityPartsRemoved, t => t.Colour = Color4.Orange); if (hasExternalLink) { textContainer.NewParagraph(); textContainer.NewParagraph(); - textContainer.AddLink("Check here for more information.", BeatmapSet.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10)); + textContainer.AddLink(BeatmapsetsStrings.AvailabilityMoreInfo, BeatmapSet.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10)); } } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/NotSupporterPlaceholder.cs b/osu.Game/Overlays/BeatmapSet/Scores/NotSupporterPlaceholder.cs index b2c87a1477..d1a0960a08 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/NotSupporterPlaceholder.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/NotSupporterPlaceholder.cs @@ -7,6 +7,7 @@ using osu.Game.Graphics.Sprites; using osuTK; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -28,7 +29,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = @"You need to be an osu!supporter to access the friend and country rankings!", + Text = BeatmapsetsStrings.ShowScoreboardSupporterOnly, Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), }, text = new LinkFlowContainer(t => t.Font = t.Font.With(size: 11)) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 64b972262b..034670cf37 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -160,7 +160,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both, Height = 1, - PlaceholderText = "type your message", + PlaceholderText = Resources.Localisation.Web.ChatStrings.InputPlaceholder, ReleaseFocusOnCommit = false, HoldFocus = true, } diff --git a/osu.Game/Overlays/Comments/Buttons/LoadRepliesButton.cs b/osu.Game/Overlays/Comments/Buttons/LoadRepliesButton.cs index 4998e5391e..4bb5b9d66d 100644 --- a/osu.Game/Overlays/Comments/Buttons/LoadRepliesButton.cs +++ b/osu.Game/Overlays/Comments/Buttons/LoadRepliesButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments.Buttons { @@ -25,7 +26,7 @@ namespace osu.Game.Overlays.Comments.Buttons { public ButtonContent() { - Text = "load replies"; + Text = CommentsStrings.LoadReplies; } } } diff --git a/osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs b/osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs index c115a8bb8f..4908e29b7d 100644 --- a/osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs +++ b/osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs @@ -9,6 +9,7 @@ using osu.Game.Graphics.Sprites; using System.Collections.Generic; using osuTK; using osu.Framework.Allocation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments.Buttons { @@ -38,7 +39,7 @@ namespace osu.Game.Overlays.Comments.Buttons { AlwaysPresent = true, Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), - Text = "show more" + Text = CommonStrings.ButtonsShowMore } }; diff --git a/osu.Game/Overlays/Comments/CancellableCommentEditor.cs b/osu.Game/Overlays/Comments/CancellableCommentEditor.cs index c226b7f07f..74c221bd82 100644 --- a/osu.Game/Overlays/Comments/CancellableCommentEditor.cs +++ b/osu.Game/Overlays/Comments/CancellableCommentEditor.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments { @@ -54,7 +55,7 @@ namespace osu.Game.Overlays.Comments Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), Margin = new MarginPadding { Horizontal = 20 }, - Text = @"Cancel" + Text = CommonStrings.ButtonsCancel } } }; diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index 6a5734b553..a28b13fc12 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -16,6 +16,7 @@ using osu.Framework.Threading; using System.Collections.Generic; using JetBrains.Annotations; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using APIUser = osu.Game.Online.API.Requests.Responses.APIUser; namespace osu.Game.Overlays.Comments @@ -328,7 +329,7 @@ namespace osu.Game.Overlays.Comments Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Margin = new MarginPadding { Left = 50 }, - Text = @"No comments yet." + Text = CommentsStrings.Empty } }); } diff --git a/osu.Game/Overlays/Comments/CommentsHeader.cs b/osu.Game/Overlays/Comments/CommentsHeader.cs index bf80655c3d..e7d9e72dcc 100644 --- a/osu.Game/Overlays/Comments/CommentsHeader.cs +++ b/osu.Game/Overlays/Comments/CommentsHeader.cs @@ -12,7 +12,9 @@ using osu.Game.Graphics; using osu.Framework.Graphics.Sprites; using osuTK; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments { @@ -91,7 +93,7 @@ namespace osu.Game.Overlays.Comments Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), - Text = @"Show deleted" + Text = CommonStrings.ButtonsShowDeleted } }, }); @@ -126,9 +128,13 @@ namespace osu.Game.Overlays.Comments public enum CommentsSortCriteria { - [System.ComponentModel.Description(@"Recent")] + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.New))] New, + + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Old))] Old, + + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Top))] Top } } diff --git a/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs b/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs index adf64eabb1..b1ca39c3bf 100644 --- a/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs +++ b/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments { @@ -18,7 +21,8 @@ namespace osu.Game.Overlays.Comments private void onCurrentChanged(ValueChangedEvent count) { - Text = $@"Show More ({count.NewValue})".ToUpper(); + Text = new TranslatableString(@"_", "{0} ({1})", + CommonStrings.ButtonsShowMore.ToUpper(), count.NewValue); } } } diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 3286b6c5c0..3ec91c8e63 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -150,7 +150,7 @@ namespace osu.Game.Overlays.Comments { Alpha = Comment.IsDeleted ? 1 : 0, Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), - Text = "deleted" + Text = CommentsStrings.Deleted } } }, diff --git a/osu.Game/Overlays/Comments/TotalCommentsCounter.cs b/osu.Game/Overlays/Comments/TotalCommentsCounter.cs index 1bb9b52689..221a745189 100644 --- a/osu.Game/Overlays/Comments/TotalCommentsCounter.cs +++ b/osu.Game/Overlays/Comments/TotalCommentsCounter.cs @@ -9,6 +9,7 @@ using osuTK; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments { @@ -39,7 +40,7 @@ namespace osu.Game.Overlays.Comments Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: 20, italics: true), Colour = colourProvider.Light1, - Text = @"Comments" + Text = CommentsStrings.Title }, new CircularContainer { diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 786401b7a8..e036f1d304 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -14,6 +14,7 @@ using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Spectator; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; @@ -138,7 +139,7 @@ namespace osu.Game.Overlays.Dashboard new PurpleTriangleButton { RelativeSizeAxes = Axes.X, - Text = "Watch", + Text = CommonStrings.ButtonsWatchTo1, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), diff --git a/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs index c73cc828e2..382bc00b1d 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; @@ -49,7 +50,7 @@ namespace osu.Game.Overlays.Dashboard.Home flow.AddRange(beatmapSets.Select(CreateBeatmapPanel)); } - protected abstract string Title { get; } + protected abstract LocalisableString Title { get; } protected abstract DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet); } diff --git a/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs index 714e07a7ed..331fff0aea 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dashboard.Home { @@ -15,6 +17,6 @@ namespace osu.Game.Overlays.Dashboard.Home protected override DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet) => new DashboardNewBeatmapPanel(beatmapSet); - protected override string Title => "New Ranked Beatmaps"; + protected override LocalisableString Title => HomeStrings.UserBeatmapsNew; } } diff --git a/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs index 48b100b04e..154813dea1 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dashboard.Home { @@ -15,6 +17,6 @@ namespace osu.Game.Overlays.Dashboard.Home protected override DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet) => new DashboardPopularBeatmapPanel(beatmapSet); - protected override string Title => "Popular Beatmaps"; + protected override LocalisableString Title => HomeStrings.UserBeatmapsPopular; } } diff --git a/osu.Game/Overlays/Dashboard/Home/News/ShowMoreNewsPanel.cs b/osu.Game/Overlays/Dashboard/Home/News/ShowMoreNewsPanel.cs index d25df6f189..f6e966957e 100644 --- a/osu.Game/Overlays/Dashboard/Home/News/ShowMoreNewsPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/News/ShowMoreNewsPanel.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osuTK.Graphics; namespace osu.Game.Overlays.Dashboard.Home.News @@ -35,7 +36,7 @@ namespace osu.Game.Overlays.Dashboard.Home.News Anchor = Anchor.Centre, Origin = Anchor.Centre, Margin = new MarginPadding { Vertical = 20 }, - Text = "see more" + Text = CommonStrings.ButtonsSeeMore } }; diff --git a/osu.Game/Overlays/Dialog/ConfirmDialog.cs b/osu.Game/Overlays/Dialog/ConfirmDialog.cs index d1c0d746d1..58ce84e13a 100644 --- a/osu.Game/Overlays/Dialog/ConfirmDialog.cs +++ b/osu.Game/Overlays/Dialog/ConfirmDialog.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dialog { @@ -33,7 +34,7 @@ namespace osu.Game.Overlays.Dialog }, new PopupDialogCancelButton { - Text = Localisation.CommonStrings.Cancel, + Text = CommonStrings.ButtonsCancel, Action = onCancel }, }; diff --git a/osu.Game/Overlays/Login/LoginForm.cs b/osu.Game/Overlays/Login/LoginForm.cs index f7842dcd30..c31416e078 100644 --- a/osu.Game/Overlays/Login/LoginForm.cs +++ b/osu.Game/Overlays/Login/LoginForm.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays.Settings; +using osu.Game.Resources.Localisation.Web; using osuTK; namespace osu.Game.Overlays.Login @@ -50,14 +51,14 @@ namespace osu.Game.Overlays.Login { username = new OsuTextBox { - PlaceholderText = "username", + PlaceholderText = UsersStrings.LoginUsername, RelativeSizeAxes = Axes.X, Text = api?.ProvidedUsername ?? string.Empty, TabbableContentContainer = this }, password = new OsuPasswordTextBox { - PlaceholderText = "password", + PlaceholderText = UsersStrings.LoginPassword, RelativeSizeAxes = Axes.X, TabbableContentContainer = this, }, @@ -88,7 +89,7 @@ namespace osu.Game.Overlays.Login AutoSizeAxes = Axes.Y, Child = new SettingsButton { - Text = "Sign in", + Text = UsersStrings.LoginButton, Action = performLogin }, } diff --git a/osu.Game/Overlays/Login/UserAction.cs b/osu.Game/Overlays/Login/UserAction.cs index 07b6b4bf7e..d216670a28 100644 --- a/osu.Game/Overlays/Login/UserAction.cs +++ b/osu.Game/Overlays/Login/UserAction.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using System.ComponentModel; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Login { public enum UserAction { + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusOnline))] Online, [Description(@"Do not disturb")] diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ec7e49920c..9ce79c25f7 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Mods; using osu.Game.Screens; using osu.Game.Utils; @@ -317,7 +318,7 @@ namespace osu.Game.Overlays.Mods CloseButton = new TriangleButton { Width = 180, - Text = "Close", + Text = CommonStrings.ButtonsClose, Action = Hide, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 2b5909f45a..f1ed5c4ba6 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -15,7 +15,8 @@ using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Graphics; -using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; +using NotificationsStrings = osu.Game.Localisation.NotificationsStrings; namespace osu.Game.Overlays { @@ -61,7 +62,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.X, Children = new[] { - new NotificationSection(@"Notifications", @"Clear All") + new NotificationSection(AccountsStrings.NotificationsTitle, "Clear All") { AcceptTypes = new[] { typeof(SimpleNotification) } }, diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index a23ff07a64..a4851ab365 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; @@ -34,9 +35,9 @@ namespace osu.Game.Overlays.Notifications private readonly string clearButtonText; - private readonly string titleText; + private readonly LocalisableString titleText; - public NotificationSection(string title, string clearButtonText) + public NotificationSection(LocalisableString title, string clearButtonText) { this.clearButtonText = clearButtonText.ToUpperInvariant(); titleText = title; @@ -84,7 +85,7 @@ namespace osu.Game.Overlays.Notifications { new OsuSpriteText { - Text = titleText.ToUpperInvariant(), + Text = titleText.ToUpper(), Font = OsuFont.GetFont(weight: FontWeight.Bold) }, countDrawable = new OsuSpriteText diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index ea52cec2e1..a70d57661b 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -11,10 +10,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Graphics; @@ -83,7 +84,7 @@ namespace osu.Game.Overlays.Profile.Header if (user == null) return; if (user.JoinDate.ToUniversalTime().Year < 2008) - topLinkContainer.AddText("Here since the beginning"); + topLinkContainer.AddText(UsersStrings.ShowFirstMembers); else { topLinkContainer.AddText("Joined "); @@ -94,7 +95,7 @@ namespace osu.Game.Overlays.Profile.Header if (user.IsOnline) { - topLinkContainer.AddText("Currently online"); + topLinkContainer.AddText(UsersStrings.ShowLastvisitOnline); addSpacer(topLinkContainer); } else if (user.LastVisit.HasValue) @@ -108,7 +109,16 @@ namespace osu.Game.Overlays.Profile.Header if (user.PlayStyles?.Length > 0) { topLinkContainer.AddText("Plays with "); - topLinkContainer.AddText(string.Join(", ", user.PlayStyles.Select(style => style.GetDescription())), embolden); + + LocalisableString playStylesString = user.PlayStyles[0].GetLocalisableDescription(); + + for (int i = 1; i < user.PlayStyles.Length; i++) + { + playStylesString = new TranslatableString(@"_", @"{0}{1}", playStylesString, CommonStrings.ArrayAndWordsConnector); + playStylesString = new TranslatableString(@"_", @"{0}{1}", playStylesString, user.PlayStyles[i].GetLocalisableDescription()); + } + + topLinkContainer.AddText(playStylesString, embolden); addSpacer(topLinkContainer); } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 2405618917..459405f57d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -21,7 +21,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Input.Bindings; -using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -402,7 +402,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public CancelButton() { - Text = CommonStrings.Cancel; + Text = CommonStrings.ButtonsCancel; Size = new Vector2(80, 20); } } @@ -411,7 +411,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public ClearButton() { - Text = CommonStrings.Clear; + Text = CommonStrings.ButtonsClear; Size = new Vector2(80, 20); } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index b0c9a04285..d8ba07dc3b 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Effects; using osu.Game.Graphics; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; @@ -62,7 +63,7 @@ namespace osu.Game.Overlays.Toolbar switch (state.NewValue) { default: - Text = @"Guest"; + Text = UsersStrings.AnonymousUsername; avatar.User = new APIUser(); break; diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index a22c18b0a4..11cab80a57 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -7,7 +7,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Wiki.Markdown { @@ -46,14 +48,14 @@ namespace osu.Game.Overlays.Wiki.Markdown { Add(new NoticeBox { - Text = "The content on this page is incomplete or outdated. If you are able to help out, please consider updating the article!", + Text = WikiStrings.ShowIncompleteOrOutdated, }); } else if (needsCleanup) { Add(new NoticeBox { - Text = "This page does not meet the standards of the osu! wiki and needs to be cleaned up or rewritten. If you are able to help out, please consider updating the article!", + Text = WikiStrings.ShowNeedsCleanupOrRewrite, }); } } @@ -63,7 +65,7 @@ namespace osu.Game.Overlays.Wiki.Markdown [Resolved] private IMarkdownTextFlowComponent parentFlowComponent { get; set; } - public string Text { get; set; } + public LocalisableString Text { get; set; } [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, OsuColour colour) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index ee4e195f3f..da96885fb5 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -3,11 +3,13 @@ using Markdig.Syntax; using Markdig.Syntax.Inlines; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Wiki { @@ -24,7 +26,7 @@ namespace osu.Game.Overlays.Wiki { new OsuSpriteText { - Text = "CONTENTS", + Text = WikiStrings.ShowToc.ToUpper(), Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), Margin = new MarginPadding { Bottom = 5 }, }, diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 9d5d8013b7..78b98a3649 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -17,6 +17,7 @@ using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Edit; using osuTK; using osuTK.Input; @@ -358,7 +359,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (SelectedBlueprints.Count == 1) items.AddRange(SelectedBlueprints[0].ContextMenuItems); - items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, DeleteSelected)); + items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, DeleteSelected)); return items.ToArray(); } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 602b7563ac..30c57733ed 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -29,6 +29,7 @@ using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Components; @@ -252,7 +253,7 @@ namespace osu.Game.Screens.Edit { Items = createFileMenuItems() }, - new MenuItem("Edit") + new MenuItem(CommonStrings.ButtonsEdit) { Items = new[] { diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 75c6a89a66..e799081115 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.Edit.Setup { @@ -27,7 +28,7 @@ namespace osu.Game.Screens.Edit.Setup { circleSizeSlider = new LabelledSliderBar { - Label = "Object Size", + Label = BeatmapsetsStrings.ShowStatsCs, FixedLabelWidth = LABEL_WIDTH, Description = "The size of all hit objects", Current = new BindableFloat(Beatmap.Difficulty.CircleSize) @@ -40,7 +41,7 @@ namespace osu.Game.Screens.Edit.Setup }, healthDrainSlider = new LabelledSliderBar { - Label = "Health Drain", + Label = BeatmapsetsStrings.ShowStatsDrain, FixedLabelWidth = LABEL_WIDTH, Description = "The rate of passive health drain throughout playable time", Current = new BindableFloat(Beatmap.Difficulty.DrainRate) @@ -53,7 +54,7 @@ namespace osu.Game.Screens.Edit.Setup }, approachRateSlider = new LabelledSliderBar { - Label = "Approach Rate", + Label = BeatmapsetsStrings.ShowStatsAr, FixedLabelWidth = LABEL_WIDTH, Description = "The speed at which objects are presented to the player", Current = new BindableFloat(Beatmap.Difficulty.ApproachRate) @@ -66,7 +67,7 @@ namespace osu.Game.Screens.Edit.Setup }, overallDifficultySlider = new LabelledSliderBar { - Label = "Overall Difficulty", + Label = BeatmapsetsStrings.ShowStatsAccuracy, FixedLabelWidth = LABEL_WIDTH, Description = "The harshness of hit windows and difficulty of special objects (ie. spinners)", Current = new BindableFloat(Beatmap.Difficulty.OverallDifficulty) diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 571dfb3f6f..6262b4c18b 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.Edit.Setup { @@ -48,15 +49,15 @@ namespace osu.Game.Screens.Edit.Setup creatorTextBox = createTextBox("Creator", metadata.Author.Username), difficultyTextBox = createTextBox("Difficulty Name", Beatmap.BeatmapInfo.DifficultyName), - sourceTextBox = createTextBox("Source", metadata.Source), - tagsTextBox = createTextBox("Tags", metadata.Tags) + sourceTextBox = createTextBox(BeatmapsetsStrings.ShowInfoSource, metadata.Source), + tagsTextBox = createTextBox(BeatmapsetsStrings.ShowInfoTags, metadata.Tags) }; foreach (var item in Children.OfType()) item.OnCommit += onCommit; } - private TTextBox createTextBox(string label, string initialValue) + private TTextBox createTextBox(LocalisableString label, string initialValue) where TTextBox : LabelledTextBox, new() => new TTextBox { diff --git a/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs index 08a0a3405e..f667a3c1d2 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; @@ -34,7 +35,7 @@ namespace osu.Game.Screens.OnlinePlay.Components private readonly Circle line; private readonly OsuSpriteText details; - public OverlinedHeader(string title) + public OverlinedHeader(LocalisableString title) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 25b36e0774..459b861d96 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -26,6 +26,7 @@ using osu.Game.Online; using osu.Game.Online.Chat; using osu.Game.Online.Rooms; using osu.Game.Overlays.BeatmapSet; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; @@ -449,7 +450,7 @@ namespace osu.Game.Screens.OnlinePlay Size = new Vector2(30, 30), Alpha = AllowEditing ? 1 : 0, Action = () => RequestEdit?.Invoke(Item), - TooltipText = "Edit" + TooltipText = CommonStrings.ButtonsEdit }, removeButton = new PlaylistRemoveButton { diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs index cf7e33fd63..799983342b 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; namespace osu.Game.Screens.OnlinePlay.Match.Components @@ -30,8 +31,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected override IEnumerable GetStatistics(ScoreInfo model) => new[] { - new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", model.DisplayAccuracy), - new LeaderboardScoreStatistic(FontAwesome.Solid.Sync, "Total Attempts", score.TotalAttempts.ToString()), + new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, RankingsStrings.StatAccuracy, model.DisplayAccuracy), + new LeaderboardScoreStatistic(FontAwesome.Solid.Sync, RankingsStrings.StatPlayCount, score.TotalAttempts.ToString()), new LeaderboardScoreStatistic(FontAwesome.Solid.Check, "Completed Beatmaps", score.CompletedBeatmaps.ToString()), }; } diff --git a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index cdd2ae0c9c..1828a072f8 100644 --- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -10,6 +10,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Rooms; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match.Components; using osuTK; @@ -49,7 +50,7 @@ namespace osu.Game.Screens.OnlinePlay.Match { RelativeSizeAxes = Axes.Y, Size = new Vector2(100, 1), - Text = "Edit", + Text = CommonStrings.ButtonsEdit, Action = () => OnEdit?.Invoke() }); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs index 7e442c6568..ef84c4b4fa 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Game.Online.Multiplayer; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.OnlinePlay.Components; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants @@ -13,7 +14,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants private MultiplayerClient client { get; set; } public ParticipantsListHeader() - : base("Participants") + : base(RankingsStrings.SpotlightParticipants) { } diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 0d759bacf0..4a0bab84fa 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; using osuTK; @@ -158,7 +159,7 @@ namespace osu.Game.Screens.Play { new Drawable[] { - new MetadataLineLabel("Source"), + new MetadataLineLabel(BeatmapsetsStrings.ShowInfoSource), new MetadataLineInfo(metadata.Source) }, new Drawable[] @@ -213,7 +214,7 @@ namespace osu.Game.Screens.Play private class MetadataLineLabel : OsuSpriteText { - public MetadataLineLabel(string text) + public MetadataLineLabel(LocalisableString text) { Anchor = Anchor.TopRight; Origin = Anchor.TopRight; @@ -225,10 +226,10 @@ namespace osu.Game.Screens.Play private class MetadataLineInfo : OsuSpriteText { - public MetadataLineInfo(string text) + public MetadataLineInfo(LocalisableString text) { Margin = new MarginPadding { Left = 5 }; - Text = string.IsNullOrEmpty(text) ? @"-" : text; + Text = string.IsNullOrEmpty(text.ToString()) ? @"-" : text; } } } diff --git a/osu.Game/Screens/Play/Break/BreakInfo.cs b/osu.Game/Screens/Play/Break/BreakInfo.cs index 6349ebd9a7..f95e949920 100644 --- a/osu.Game/Screens/Play/Break/BreakInfo.cs +++ b/osu.Game/Screens/Play/Break/BreakInfo.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; using osuTK; @@ -42,7 +43,7 @@ namespace osu.Game.Screens.Play.Break Direction = FillDirection.Vertical, Children = new Drawable[] { - AccuracyDisplay = new PercentageBreakInfoLine("Accuracy"), + AccuracyDisplay = new PercentageBreakInfoLine(BeatmapsetsStrings.ShowStatsAccuracy), // See https://github.com/ppy/osu/discussions/15185 // RankDisplay = new BreakInfoLine("Rank"), diff --git a/osu.Game/Screens/Play/Break/BreakInfoLine.cs b/osu.Game/Screens/Play/Break/BreakInfoLine.cs index 87f514ffd5..4cae90e50f 100644 --- a/osu.Game/Screens/Play/Break/BreakInfoLine.cs +++ b/osu.Game/Screens/Play/Break/BreakInfoLine.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Play.Break private readonly string prefix; - public BreakInfoLine(string name, string prefix = @"") + public BreakInfoLine(LocalisableString name, string prefix = @"") { this.prefix = prefix; @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Play.Break public class PercentageBreakInfoLine : BreakInfoLine { - public PercentageBreakInfoLine(string name, string prefix = "") + public PercentageBreakInfoLine(LocalisableString name, string prefix = "") : base(name, prefix) { } diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 8b1e0ae83e..019a9f9730 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -19,6 +19,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Judgements; @@ -200,7 +201,7 @@ namespace osu.Game.Screens.Play.HUD { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Text = @"pp", + Text = BeatmapsetsStrings.ShowScoreboardHeaderspp, Font = OsuFont.Numeric.With(size: 8), Padding = new MarginPadding { Bottom = 1.5f }, // align baseline better } diff --git a/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs b/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs index 32de5333e1..90caf6f0f3 100644 --- a/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Configuration; +using osu.Game.Localisation; using osu.Game.Scoring; namespace osu.Game.Screens.Play.PlayerSettings @@ -20,7 +21,7 @@ namespace osu.Game.Screens.Play.PlayerSettings { Children = new Drawable[] { - beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" }, + beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapHitsounds }, new BeatmapOffsetControl { ReferenceScore = { BindTarget = ReferenceScore }, diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index 81950efa9e..a999b32cb4 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Graphics.Sprites; +using osu.Game.Localisation; namespace osu.Game.Screens.Play.PlayerSettings { @@ -23,7 +24,7 @@ namespace osu.Game.Screens.Play.PlayerSettings { new OsuSpriteText { - Text = "Background dim:" + Text = GameplaySettingsStrings.BackgroundDim }, dimSliderBar = new PlayerSliderBar { @@ -31,7 +32,7 @@ namespace osu.Game.Screens.Play.PlayerSettings }, new OsuSpriteText { - Text = "Background blur:" + Text = GameplaySettingsStrings.BackgroundBlur }, blurSliderBar = new PlayerSliderBar { @@ -41,9 +42,9 @@ namespace osu.Game.Screens.Play.PlayerSettings { Text = "Toggles:" }, - showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboard / Video" }, - beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" }, - beatmapColorsToggle = new PlayerCheckbox { LabelText = "Beatmap colours" }, + showStoryboardToggle = new PlayerCheckbox { LabelText = GraphicsSettingsStrings.StoryboardVideo }, + beatmapSkinsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapSkins }, + beatmapColorsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapColours }, }; } diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index f9aff28bef..bb286f41c0 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -9,9 +9,11 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; @@ -127,8 +129,8 @@ namespace osu.Game.Screens.Ranking.Contracted Spacing = new Vector2(0, 5), Children = new[] { - createStatistic("Max Combo", $"x{score.MaxCombo}"), - createStatistic("Accuracy", $"{score.Accuracy.FormatAccuracy()}"), + createStatistic(BeatmapsetsStrings.ShowScoreboardHeadersCombo, $"x{score.MaxCombo}"), + createStatistic(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy, $"{score.Accuracy.FormatAccuracy()}"), } }, new ModFlowDisplay @@ -200,7 +202,7 @@ namespace osu.Game.Screens.Ranking.Contracted private Drawable createStatistic(HitResultDisplayStatistic result) => createStatistic(result.DisplayName, result.MaxCount == null ? $"{result.Count}" : $"{result.Count}/{result.MaxCount}"); - private Drawable createStatistic(string key, string value) => new Container + private Drawable createStatistic(LocalisableString key, string value) => new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs index 476c9fb42f..25a644d8d9 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs @@ -6,6 +6,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.Ranking.Expanded.Accuracy; using osu.Game.Utils; using osuTK; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics /// /// The accuracy to display. public AccuracyStatistic(double accuracy) - : base("accuracy") + : base(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy) { this.accuracy = accuracy; } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs index 0e42ec026a..cb25736f6e 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.Ranking.Expanded.Accuracy; using osuTK; @@ -27,7 +28,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics /// The combo to be displayed. /// The maximum value of . public ComboStatistic(int combo, int? maxCombo) - : base("combo", combo, maxCombo) + : base(BeatmapsetsStrings.ShowScoreboardHeadersCombo, combo, maxCombo) { isPerfect = combo == maxCombo; } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs index d37f6c5e5f..b1c72173da 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics /// The name of the statistic. /// The value to display. /// The maximum value of . Not displayed if null. - public CounterStatistic(string header, int count, int? maxCount = null) + public CounterStatistic(LocalisableString header, int count, int? maxCount = null) : base(header) { this.count = count; diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index 95f017d625..c681946a2f 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; namespace osu.Game.Screens.Ranking.Expanded.Statistics @@ -23,7 +24,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics private RollingCounter counter; public PerformanceStatistic(ScoreInfo score) - : base("PP") + : base(BeatmapsetsStrings.ShowScoreboardHeaderspp) { this.score = score; } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs index 9206c58bc9..c034abc916 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs @@ -3,10 +3,12 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -19,14 +21,14 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics { protected SpriteText HeaderText { get; private set; } - private readonly string header; + private readonly LocalisableString header; private Drawable content; /// /// Creates a new . /// /// The name of the statistic. - protected StatisticDisplay(string header) + protected StatisticDisplay(LocalisableString header) { this.header = header; RelativeSizeAxes = Axes.X; @@ -60,7 +62,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), - Text = header.ToUpperInvariant(), + Text = header.ToUpper(), } } }, diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index bbe0a37d8e..9ff1574fe4 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -16,6 +16,7 @@ using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.BeatmapSet; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.Select.Details; using osuTK; using osuTK.Graphics; @@ -155,7 +156,7 @@ namespace osu.Game.Screens.Select { new OsuSpriteText { - Text = "Points of Failure", + Text = BeatmapsetsStrings.ShowInfoPointsOfFailure, Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 14), }, failRetryGraph = new FailRetryGraph diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 3576b77ae8..c6037d1bd6 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -24,6 +24,7 @@ using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Graphics; @@ -136,14 +137,7 @@ namespace osu.Game.Screens.Select.Carousel }, new OsuSpriteText { - Text = "mapped by", - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft - }, - new OsuSpriteText - { - Text = $"{beatmapInfo.Metadata.Author.Username}", - Font = OsuFont.GetFont(italics: true), + Text = BeatmapsetsStrings.ShowDetailsMappedBy(beatmapInfo.Metadata.Author.Username), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft }, @@ -235,7 +229,7 @@ namespace osu.Game.Screens.Select.Carousel items.Add(new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested(beatmapInfo))); if (editRequested != null) - items.Add(new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested(beatmapInfo))); + items.Add(new OsuMenuItem(CommonStrings.ButtonsEdit, MenuItemType.Standard, () => editRequested(beatmapInfo))); if (beatmapInfo.OnlineID > 0 && beatmapOverlay != null) items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmapInfo.OnlineID))); @@ -250,7 +244,7 @@ namespace osu.Game.Screens.Select.Carousel } if (hideRequested != null) - items.Add(new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested(beatmapInfo))); + items.Add(new OsuMenuItem(CommonStrings.ButtonsHide, MenuItemType.Destructive, () => hideRequested(beatmapInfo))); return items.ToArray(); } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index adaaa6425c..a6f2520472 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -21,6 +21,7 @@ using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Configuration; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; namespace osu.Game.Screens.Select.Details @@ -63,10 +64,10 @@ namespace osu.Game.Screens.Select.Details Children = new[] { FirstValue = new StatisticRow(), // circle size/key amount - HpDrain = new StatisticRow { Title = "HP Drain" }, - Accuracy = new StatisticRow { Title = "Accuracy" }, - ApproachRate = new StatisticRow { Title = "Approach Rate" }, - starDifficulty = new StatisticRow(10, true) { Title = "Star Difficulty" }, + HpDrain = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsDrain }, + Accuracy = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAccuracy }, + ApproachRate = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAr }, + starDifficulty = new StatisticRow(10, true) { Title = BeatmapsetsStrings.ShowStatsStars }, }, }; } @@ -120,12 +121,12 @@ namespace osu.Game.Screens.Select.Details case 3: // Account for mania differences locally for now // Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes - FirstValue.Title = "Key Count"; + FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania; FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, null); break; default: - FirstValue.Title = "Circle Size"; + FirstValue.Title = BeatmapsetsStrings.ShowStatsCs; FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize); break; } diff --git a/osu.Game/Screens/Select/Filter/SortMode.cs b/osu.Game/Screens/Select/Filter/SortMode.cs index 18c5d713e1..1ab54fa069 100644 --- a/osu.Game/Screens/Select/Filter/SortMode.cs +++ b/osu.Game/Screens/Select/Filter/SortMode.cs @@ -2,36 +2,38 @@ // See the LICENCE file in the repository root for full licence text. using System.ComponentModel; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.Select.Filter { public enum SortMode { - [Description("Artist")] + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingArtist))] Artist, [Description("Author")] Author, - [Description("BPM")] + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatsBpm))] BPM, [Description("Date Added")] DateAdded, - [Description("Difficulty")] + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingDifficulty))] Difficulty, - [Description("Length")] + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.ArtistTracksLength))] Length, - [Description("Rank Achieved")] + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchFiltersRank))] RankAchieved, - [Description("Source")] + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoSource))] Source, - [Description("Title")] + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingTitle))] Title, } } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index b53d64260a..65dde146bb 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Screens.Select.Filter; using osuTK; @@ -139,7 +140,7 @@ namespace osu.Game.Screens.Select }, new OsuSpriteText { - Text = "Sort by", + Text = SortStrings.Default, Font = OsuFont.GetFont(size: 14), Margin = new MarginPadding(5), Anchor = Anchor.BottomRight, diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index b5fdbd225f..1a8b69d859 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -13,6 +13,7 @@ using osuTK.Input; using osu.Game.Graphics.Containers; using osu.Framework.Input.Events; using System.Linq; +using osu.Framework.Localisation; namespace osu.Game.Screens.Select.Options { @@ -63,7 +64,7 @@ namespace osu.Game.Screens.Select.Options /// Colour of the button. /// Icon of the button. /// Binding the button does. - public void AddButton(string firstLine, string secondLine, IconUsage icon, Color4 colour, Action action) + public void AddButton(LocalisableString firstLine, string secondLine, IconUsage icon, Color4 colour, Action action) { var button = new BeatmapOptionsButton { diff --git a/osu.Game/Users/UserStatus.cs b/osu.Game/Users/UserStatus.cs index 21c18413f4..7f275b3b2a 100644 --- a/osu.Game/Users/UserStatus.cs +++ b/osu.Game/Users/UserStatus.cs @@ -1,20 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; using osuTK.Graphics; using osu.Game.Graphics; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Users { public abstract class UserStatus { - public abstract string Message { get; } + public abstract LocalisableString Message { get; } public abstract Color4 GetAppropriateColour(OsuColour colours); } public class UserStatusOnline : UserStatus { - public override string Message => @"Online"; + public override LocalisableString Message => UsersStrings.StatusOnline; public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenLight; } @@ -25,13 +27,13 @@ namespace osu.Game.Users public class UserStatusOffline : UserStatus { - public override string Message => @"Offline"; + public override LocalisableString Message => UsersStrings.StatusOffline; public override Color4 GetAppropriateColour(OsuColour colours) => Color4.Black; } public class UserStatusDoNotDisturb : UserStatus { - public override string Message => @"Do not disturb"; + public override LocalisableString Message => "Do not disturb"; public override Color4 GetAppropriateColour(OsuColour colours) => colours.RedDark; } } From 0146949ad60c45e788230e6336bfb29aaaa3491e Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 20 Apr 2022 16:52:24 -0700 Subject: [PATCH 0456/1276] Fix failing string comparison tests --- osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 7ceae0a69b..8af70df48a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; @@ -55,7 +56,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("no mods selected", () => SelectedMods.Value = Array.Empty()); - AddAssert("first bar text is Circle Size", () => advancedStats.ChildrenOfType().First().Text == "Circle Size"); + AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType().First().Text == BeatmapsetsStrings.ShowStatsCs); AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue)); AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.HpDrain)); AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy)); @@ -78,7 +79,7 @@ namespace osu.Game.Tests.Visual.SongSelect StarRating = 8 }); - AddAssert("first bar text is Key Count", () => advancedStats.ChildrenOfType().First().Text == "Key Count"); + AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType().First().Text == BeatmapsetsStrings.ShowStatsCsMania); } [Test] From 56d8de23e2b649f305c87111fd31dd3a0abbaec1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 12:18:54 +0900 Subject: [PATCH 0457/1276] Replace horse with "more suitable" icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index d5aa1178e0..f73d82b793 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -303,7 +303,7 @@ namespace osu.Game.Overlays notificationOverlay.Post(new SimpleNotification { Text = FirstRunSetupOverlayStrings.ClickToResumeFirstRunSetupAtAnyPoint, - Icon = FontAwesome.Solid.Horse, + Icon = FontAwesome.Solid.Redo, Activated = () => { Show(); From a9163727db6195bee3a4212ad6d4c3cf8b1d0804 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 13:44:14 +0900 Subject: [PATCH 0458/1276] Mark selected channel null in test when selector is activated --- osu.Game.Tests/Visual/Online/TestSceneChannelList.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs index d6a1e71f0d..a3bfbd47a3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -104,6 +104,7 @@ namespace osu.Game.Tests.Visual.Online channelList.SelectorActive.BindValueChanged(change => { selectorText.Text = $"Channel Selector Active: {change.NewValue}"; + selected.Value = null; }, true); selected.BindValueChanged(change => From 9f525ee26743ce70b1f200979c1664841fef0896 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 14:09:51 +0900 Subject: [PATCH 0459/1276] Revert localisation of spectate/watch button --- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index e036f1d304..9807a65250 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -139,7 +139,7 @@ namespace osu.Game.Overlays.Dashboard new PurpleTriangleButton { RelativeSizeAxes = Axes.X, - Text = CommonStrings.ButtonsWatchTo1, + Text = "Spectate", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), From 848366416e7595b1dce036fa186e0f1369b66c5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 14:10:58 +0900 Subject: [PATCH 0460/1276] Revert string type changes in `MetadataLineInfo` --- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 4a0bab84fa..e8021d4065 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -226,10 +226,10 @@ namespace osu.Game.Screens.Play private class MetadataLineInfo : OsuSpriteText { - public MetadataLineInfo(LocalisableString text) + public MetadataLineInfo(string text) { Margin = new MarginPadding { Left = 5 }; - Text = string.IsNullOrEmpty(text.ToString()) ? @"-" : text; + Text = string.IsNullOrEmpty(text) ? @"-" : text; } } } From e54d3702a6fa6ec1b72432c4bde79428aa7927b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 15:16:39 +0900 Subject: [PATCH 0461/1276] Specify web proxy in SignalR connection building Might solve https://github.com/ppy/osu/discussions/17897 (depends on how adeptly signalr falls back to long polling if/when is fails to websocket via the proxy, or maybe succeed). As with the framework side change, I've tested this to not break a zero-proxy flow. Intending to have the user test this after next release. Reference: https://stackoverflow.com/questions/59343807/get-system-default-web-proxy-in-net-core / https://stackoverflow.com/questions/13515058/using-signalr-client-through-a-web-proxy --- osu.Game/Online/HubClientConnector.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index c79660568c..ca9bf00b23 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Client; @@ -144,6 +145,12 @@ namespace osu.Game.Online var builder = new HubConnectionBuilder() .WithUrl(endpoint, options => { + // Use HttpClient.DefaultProxy once on net6 everywhere. + // The credential setter can also be removed at this point. + options.Proxy = WebRequest.DefaultWebProxy; + if (options.Proxy != null) + options.Proxy.Credentials = CredentialCache.DefaultCredentials; + options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); options.Headers.Add("OsuVersionHash", versionHash); }); From b4c208be63db5babf41d3301de4f07f59379a7c9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 15:34:01 +0900 Subject: [PATCH 0462/1276] Move Code Quality check to top of checks list --- .github/workflows/ci.yml | 110 +++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e7f9628fc..f2066f27de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,60 @@ on: [push, pull_request] name: Continuous Integration jobs: + inspect-code: + name: Code Quality + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + # FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side. + # https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e + - name: Install .NET 3.1.x LTS + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "3.1.x" + + - name: Install .NET 6.0.x + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "6.0.x" + + - name: Restore Tools + run: dotnet tool restore + + - name: Restore Packages + run: dotnet restore + + - name: Restore inspectcode cache + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/inspectcode + key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }} + + - name: CodeFileSanity + run: | + # TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround. + # FIXME: Suppress warnings from templates project + exit_code=0 + while read -r line; do + if [[ ! -z "$line" ]]; then + echo "::error::$line" + exit_code=1 + fi + done <<< $(dotnet codefilesanity) + exit $exit_code + + # Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded. + # - name: .NET Format (Dry Run) + # run: dotnet format --dry-run --check + + - name: InspectCode + run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN + + - name: NVika + run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors + test: name: Test runs-on: ${{matrix.os.fullname}} @@ -93,58 +147,4 @@ jobs: # cannot accept .sln(f) files as arguments. # Build just the main game for now. - name: Build - run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug - - inspect-code: - name: Code Quality - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - # FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side. - # https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e - - name: Install .NET 3.1.x LTS - uses: actions/setup-dotnet@v1 - with: - dotnet-version: "3.1.x" - - - name: Install .NET 6.0.x - uses: actions/setup-dotnet@v1 - with: - dotnet-version: "6.0.x" - - - name: Restore Tools - run: dotnet tool restore - - - name: Restore Packages - run: dotnet restore - - - name: Restore inspectcode cache - uses: actions/cache@v3 - with: - path: ${{ github.workspace }}/inspectcode - key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }} - - - name: CodeFileSanity - run: | - # TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround. - # FIXME: Suppress warnings from templates project - exit_code=0 - while read -r line; do - if [[ ! -z "$line" ]]; then - echo "::error::$line" - exit_code=1 - fi - done <<< $(dotnet codefilesanity) - exit $exit_code - - # Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded. - # - name: .NET Format (Dry Run) - # run: dotnet format --dry-run --check - - - name: InspectCode - run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN - - - name: NVika - run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors + run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug \ No newline at end of file From 54ca4b9d36f55bd9e3636b1c88eea0ef553753e6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 15:34:14 +0900 Subject: [PATCH 0463/1276] Remove unused using --- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 9807a65250..a9312e9a3a 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -14,7 +14,6 @@ using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Spectator; -using osu.Game.Resources.Localisation.Web; using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; From 414408140e0e66e394dafc1738c2a2c574496f87 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Apr 2022 10:16:52 +0300 Subject: [PATCH 0464/1276] Improve AnchorOriginVisualiser --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index 0a4bd1d75f..1860c6006c 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -146,8 +146,10 @@ namespace osu.Game.Skinning.Editor { anchorLine = new Box { - Colour = Color4.Yellow, Height = 2, + Origin = Anchor.CentreLeft, + Colour = Color4.Yellow, + EdgeSmoothness = Vector2.One }, originBox = new Box { From 2769d8e8cfdbded19d1ba4407053efb8c162ae43 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 16:24:21 +0900 Subject: [PATCH 0465/1276] Add test coverage of `ShearedOverlayContainer` --- .../TestSceneShearedOverlayContainer.cs | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs new file mode 100644 index 0000000000..5a9cafde27 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs @@ -0,0 +1,102 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osu.Game.Overlays.Mods; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.UserInterface +{ + [TestFixture] + public class TestSceneShearedOverlayContainer : OsuManualInputManagerTestScene + { + private TestShearedOverlayContainer overlay; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create overlay", () => + { + Child = overlay = new TestShearedOverlayContainer + { + State = { Value = Visibility.Visible } + }; + }); + } + + [Test] + public void TestClickAwayToExit() + { + AddStep("click inside header", () => + { + InputManager.MoveMouseTo(overlay.ChildrenOfType().First().ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("overlay not dismissed", () => overlay.State.Value == Visibility.Visible); + + AddStep("click inside content", () => + { + InputManager.MoveMouseTo(overlay.ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("overlay not dismissed", () => overlay.State.Value == Visibility.Visible); + + AddStep("click outside header", () => + { + InputManager.MoveMouseTo(new Vector2(overlay.ScreenSpaceDrawQuad.TopLeft.X, overlay.ScreenSpaceDrawQuad.Centre.Y)); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("overlay dismissed", () => overlay.State.Value == Visibility.Hidden); + } + + public class TestShearedOverlayContainer : ShearedOverlayContainer + { + protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green; + + [BackgroundDependencyLoader] + private void load() + { + Header.Title = "Sheared overlay header"; + Header.Description = string.Join(" ", Enumerable.Repeat("This is a description.", 20)); + + MainAreaContent.Child = new InputBlockingContainer + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.9f), + Children = new Drawable[] + { + new Box + { + Colour = Color4.Blue, + RelativeSizeAxes = Axes.Both, + }, + new OsuSpriteText + { + Font = OsuFont.Default.With(size: 24), + Text = "Content", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }; + } + } + } +} From 0f4b40ab15b2727d5cf66c2f0a3bf606403e9c51 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 16:15:10 +0900 Subject: [PATCH 0466/1276] Add better click-to-dismiss logic for sheared overlays --- osu.Game/Graphics/InputBlockingContainer.cs | 21 +++++++++++++++++++ .../UserInterface/ShearedOverlayHeader.cs | 2 +- .../Overlays/Mods/ShearedOverlayContainer.cs | 15 ++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Graphics/InputBlockingContainer.cs diff --git a/osu.Game/Graphics/InputBlockingContainer.cs b/osu.Game/Graphics/InputBlockingContainer.cs new file mode 100644 index 0000000000..d8387b1401 --- /dev/null +++ b/osu.Game/Graphics/InputBlockingContainer.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; + +namespace osu.Game.Graphics +{ + /// + /// A simple container which blocks input events from travelling through it. + /// + public class InputBlockingContainer : Container + { + protected override bool OnHover(HoverEvent e) => true; + + protected override bool OnMouseDown(MouseDownEvent e) => true; + + protected override bool OnClick(ClickEvent e) => true; + } +} diff --git a/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs b/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs index 9ed7bb35de..452a1dd394 100644 --- a/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs +++ b/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs @@ -66,7 +66,7 @@ namespace osu.Game.Graphics.UserInterface }, Children = new Drawable[] { - underlayContainer = new Container + underlayContainer = new InputBlockingContainer { RelativeSizeAxes = Axes.X, Height = HEIGHT, diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 62ed736dc2..eca192c8e5 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -5,6 +5,8 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -88,7 +90,7 @@ namespace osu.Game.Overlays.Mods Bottom = footer_height + PADDING, } }, - Footer = new Container + Footer = new InputBlockingContainer { RelativeSizeAxes = Axes.X, Depth = float.MinValue, @@ -113,6 +115,17 @@ namespace osu.Game.Overlays.Mods }; } + protected override bool OnClick(ClickEvent e) + { + if (State.Value == Visibility.Visible) + { + Hide(); + return true; + } + + return base.OnClick(e); + } + protected override void PopIn() { const double fade_in_duration = 400; From 0b81ae9de2af8883ceb5931ed964e9949672b85c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 16:51:26 +0900 Subject: [PATCH 0467/1276] Convert to using sheared overlay container Add better click-to-dismiss logic for sheader overlays --- .../TestSceneFirstRunSetupOverlay.cs | 2 +- osu.Game/Overlays/FirstRunSetupOverlay.cs | 221 ++++++------------ 2 files changed, 75 insertions(+), 148 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index efce4f350b..e925859d71 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -165,7 +165,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("click outside content", () => { - InputManager.MoveMouseTo(overlay.ScreenSpaceDrawQuad.TopLeft - new Vector2(1)); + InputManager.MoveMouseTo(new Vector2(overlay.ScreenSpaceDrawQuad.TopLeft.X, overlay.ScreenSpaceDrawQuad.Centre.Y)); InputManager.Click(MouseButton.Left); }); diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index f73d82b793..d0e721cf46 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -7,10 +7,8 @@ using System; using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; @@ -18,25 +16,22 @@ using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Configuration; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Overlays.FirstRunSetup; +using osu.Game.Overlays.Mods; using osu.Game.Overlays.Notifications; using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Match.Components; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays { [Cached] - public class FirstRunSetupOverlay : OsuFocusedOverlayContainer + public class FirstRunSetupOverlay : ShearedOverlayContainer { - protected override bool StartHidden => true; + protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Purple; [Resolved] private IPerformFromScreenRunner performer { get; set; } = null!; @@ -52,15 +47,10 @@ namespace osu.Game.Overlays public PurpleTriangleButton NextButton = null!; public DangerousTriangleButton BackButton = null!; - [Cached] - private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - private readonly Bindable showFirstRunSetup = new Bindable(); private int? currentStepIndex; - private const float scale_when_hidden = 0.9f; - /// /// The currently displayed screen, if any. /// @@ -76,149 +66,89 @@ namespace osu.Game.Overlays private Bindable? overlayActivationMode; - public FirstRunSetupOverlay() - { - RelativeSizeAxes = Axes.Both; - } + private Container content = null!; [BackgroundDependencyLoader] private void load() { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; + Header.Title = FirstRunSetupOverlayStrings.FirstRunSetup; + Header.Description = FirstRunSetupOverlayStrings.SetupOsuToSuitYou; - RelativeSizeAxes = Axes.Both; - Size = new Vector2(0.95f); - - EdgeEffect = new EdgeEffectParameters + MainAreaContent.AddRange(new Drawable[] { - Type = EdgeEffectType.Shadow, - Radius = 5, - Colour = Color4.Black.Opacity(0.2f), - }; - - Masking = true; - CornerRadius = 10; - - Children = new Drawable[] - { - new Box + content = new Container { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background6, - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + Padding = new MarginPadding { Horizontal = 50 }, + Child = new InputBlockingContainer { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] + Masking = true, + CornerRadius = 14, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Box - { - Colour = colourProvider.Background5, - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding(10), - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = FirstRunSetupOverlayStrings.FirstRunSetup, - Font = OsuFont.Default.With(size: 32), - Colour = colourProvider.Content1, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - new OsuTextFlowContainer - { - Text = FirstRunSetupOverlayStrings.SetupOsuToSuitYou, - Colour = colourProvider.Content2, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - }, - } - }, - } - }, - }, - new Drawable[] - { - stackContainer = new Container + new Box { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(20), + Colour = ColourProvider.Background6, }, - }, - new Drawable[] - { - new Container + stackContainer = new Container { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(20) + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { - Top = 0 // provided by the stack container above. - }, - Child = new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, 10), - new Dimension(), - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new[] - { - BackButton = new DangerousTriangleButton - { - Width = 200, - Text = CommonStrings.Back, - Action = showPreviousStep, - Enabled = { Value = false }, - }, - Empty(), - NextButton = new PurpleTriangleButton - { - RelativeSizeAxes = Axes.X, - Width = 1, - Text = FirstRunSetupOverlayStrings.GetStarted, - Action = showNextStep - } - }, - } + Vertical = 20, + Horizontal = 20, }, } - } - } + }, + }, }, - }; + }); + + FooterContent.Add(new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.98f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 10), + new Dimension(), + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new[] + { + BackButton = new DangerousTriangleButton + { + Width = 200, + Text = CommonStrings.Back, + Action = showPreviousStep, + Enabled = { Value = false }, + }, + Empty(), + NextButton = new PurpleTriangleButton + { + RelativeSizeAxes = Axes.X, + Width = 1, + Text = FirstRunSetupOverlayStrings.GetStarted, + Action = showNextStep + } + }, + } + }); } protected override void LoadComplete() @@ -280,10 +210,8 @@ namespace osu.Game.Overlays { base.PopIn(); - this.ScaleTo(scale_when_hidden) - .ScaleTo(1, 400, Easing.OutElasticHalf); - - this.FadeIn(400, Easing.OutQuint); + content.ScaleTo(0.99f) + .ScaleTo(1, 400, Easing.OutQuint); if (currentStepIndex == null) showFirstStep(); @@ -291,6 +219,10 @@ namespace osu.Game.Overlays protected override void PopOut() { + base.PopOut(); + + content.ScaleTo(0.99f, 400, Easing.OutQuint); + if (overlayActivationMode != null) { // If this is non-null we are guaranteed to have come from the main menu. @@ -316,11 +248,6 @@ namespace osu.Game.Overlays stack?.FadeOut(100) .Expire(); } - - base.PopOut(); - - this.ScaleTo(0.96f, 400, Easing.OutQuint); - this.FadeOut(200, Easing.OutQuint); } private void showFirstStep() From f5d24add58766dfdf8822271907a32349a9df27f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 16:49:21 +0900 Subject: [PATCH 0468/1276] Fix `TestSceneSkinnableSound` not working under visual tests oops --- osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs index ccf13e1e8f..64afe1235b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Gameplay private TestSkinSourceContainer skinSource; private PausableSkinnableSound skinnableSound; - [SetUp] + [SetUpSteps] public void SetUpSteps() { AddStep("setup hierarchy", () => From 5ae3d0eb274785ac3bb55cf26e8860ca7e35456e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 19:36:19 +0900 Subject: [PATCH 0469/1276] Fix wording of ui scale screen description Co-authored-by: Walavouchey <36758269+Walavouchey@users.noreply.github.com> --- osu.Game/Localisation/FirstRunSetupOverlayStrings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs index ec2e97d82a..b55102db20 100644 --- a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -44,9 +44,9 @@ namespace osu.Game.Localisation osu! is a very configurable game, and diving straight into the settings can sometimes be overwhelming. This guide will help you get the important choices out of the way to ensure a great first experience!"); /// - /// "The size of the osu! user interface size can be adjusted to your liking." + /// "The size of the osu! user interface can be adjusted to your liking." /// - public static LocalisableString UIScaleDescription => new TranslatableString(getKey(@"ui_scale_description"), @"The size of the osu! user interface size can be adjusted to your liking."); + public static LocalisableString UIScaleDescription => new TranslatableString(getKey(@"ui_scale_description"), @"The size of the osu! user interface can be adjusted to your liking."); /// /// "Next ({0})" From 1f967ecba3f4b30821640091d94ee55d238e44cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 19:52:28 +0900 Subject: [PATCH 0470/1276] Update string keys and change "setup" to "set up" to fix grammar --- osu.Game/Localisation/FirstRunSetupOverlayStrings.cs | 6 +++--- osu.Game/Overlays/FirstRunSetupOverlay.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs index b55102db20..d2fb0e7467 100644 --- a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -22,12 +22,12 @@ namespace osu.Game.Localisation /// /// "First-run setup" /// - public static LocalisableString FirstRunSetup => new TranslatableString(getKey(@"first_run_setup"), @"First-run setup"); + public static LocalisableString FirstRunSetupTitle => new TranslatableString(getKey(@"first_run_setup_title"), @"First-run setup"); /// - /// "Setup osu! to suit you" + /// "Set up osu! to suit you" /// - public static LocalisableString SetupOsuToSuitYou => new TranslatableString(getKey(@"setup_osu_to_suit_you"), @"Setup osu! to suit you"); + public static LocalisableString FirstRunSetupDescription => new TranslatableString(getKey(@"first_run_setup_description"), @"Set up osu! to suit you"); /// /// "Welcome" diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index f73d82b793..dd36475a23 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -141,7 +141,7 @@ namespace osu.Game.Overlays { new OsuSpriteText { - Text = FirstRunSetupOverlayStrings.FirstRunSetup, + Text = FirstRunSetupOverlayStrings.FirstRunSetupTitle, Font = OsuFont.Default.With(size: 32), Colour = colourProvider.Content1, Anchor = Anchor.TopCentre, @@ -149,7 +149,7 @@ namespace osu.Game.Overlays }, new OsuTextFlowContainer { - Text = FirstRunSetupOverlayStrings.SetupOsuToSuitYou, + Text = FirstRunSetupOverlayStrings.FirstRunSetupDescription, Colour = colourProvider.Content2, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, From ed6481f7084277a063d26c63515d6631f560d9d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 19:55:15 +0900 Subject: [PATCH 0471/1276] Also rename `Welcome` to `WelcomeTitle` to match other usages --- osu.Game/Localisation/FirstRunSetupOverlayStrings.cs | 2 +- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs index d2fb0e7467..001de93c16 100644 --- a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -32,7 +32,7 @@ namespace osu.Game.Localisation /// /// "Welcome" /// - public static LocalisableString Welcome => new TranslatableString(getKey(@"welcome"), @"Welcome"); + public static LocalisableString WelcomeTitle => new TranslatableString(getKey(@"welcome_title"), @"Welcome"); /// /// "Welcome to the first-run setup guide! diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index dd36475a23..a12fec4507 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -68,7 +68,7 @@ namespace osu.Game.Overlays private readonly FirstRunStep[] steps = { - new FirstRunStep(typeof(ScreenWelcome), FirstRunSetupOverlayStrings.Welcome), + new FirstRunStep(typeof(ScreenWelcome), FirstRunSetupOverlayStrings.WelcomeTitle), new FirstRunStep(typeof(ScreenUIScale), GraphicsSettingsStrings.UIScaling), }; From 8c3b541312ebcfe59cea912c755de5dcfb37ee1c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:35:07 +0900 Subject: [PATCH 0472/1276] Add state for when user is ready for gameplay --- .../Online/Multiplayer/MultiplayerRoomUser.cs | 16 ++++++++++++++++ .../Online/Multiplayer/MultiplayerUserState.cs | 8 +++++++- .../Multiplayer/Participants/StateDisplay.cs | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index f0b7dcbff8..50e539e8a6 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -65,5 +65,21 @@ namespace osu.Game.Online.Multiplayer } public override int GetHashCode() => UserID.GetHashCode(); + + /// + /// Whether this user has finished loading and can start gameplay. + /// + public bool CanStartGameplay() + { + switch (State) + { + case MultiplayerUserState.Loaded: + case MultiplayerUserState.ReadyForGameplay: + return true; + + default: + return false; + } + } } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerUserState.cs b/osu.Game/Online/Multiplayer/MultiplayerUserState.cs index c467ff84bb..d1369a7970 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerUserState.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerUserState.cs @@ -29,10 +29,16 @@ namespace osu.Game.Online.Multiplayer WaitingForLoad, /// - /// The user's client has marked itself as loaded and ready to begin gameplay. + /// The user has marked itself as loaded, but may still be adjusting settings prior to being ready for gameplay. + /// Players remaining in this state for an extended period of time will be automatically transitioned to the state by the server. /// Loaded, + /// + /// The user has finished adjusting settings and is ready to start gameplay. + /// + ReadyForGameplay, + /// /// The user is currently playing in a game. This is a reserved state, and is set by the server. /// diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs index 2616b07c1f..658fc43e8d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/StateDisplay.cs @@ -112,6 +112,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants break; case MultiplayerUserState.Loaded: + case MultiplayerUserState.ReadyForGameplay: text.Text = "loaded"; icon.Icon = FontAwesome.Solid.DotCircle; icon.Colour = colours.YellowLight; From 08d250fe587e854affd32332f6d10dfd51bc5e61 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:37:26 +0900 Subject: [PATCH 0473/1276] Rename MatchStarted() -> GameplayStarted() --- .../Visual/Multiplayer/TestSceneMultiplayerPlayer.cs | 2 +- osu.Game/Online/Multiplayer/IMultiplayerClient.cs | 5 +++-- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 6 +++--- osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 2 +- .../Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 6 +++--- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 2 +- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs index 312281ac18..e05580fed6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen() && player.IsLoaded); - AddStep("start gameplay", () => ((IMultiplayerClient)MultiplayerClient).MatchStarted()); + AddStep("start gameplay", () => ((IMultiplayerClient)MultiplayerClient).GameplayStarted()); } [Test] diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 3e6821b1cd..43ccfd36ed 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -98,9 +98,10 @@ namespace osu.Game.Online.Multiplayer Task LoadRequested(); /// - /// Signals that a match has started. All users in the state should begin gameplay as soon as possible. + /// Signals that gameplay has started. + /// All users in the or states should begin gameplay as soon as possible. /// - Task MatchStarted(); + Task GameplayStarted(); /// /// Signals that the match has ended, all players have finished and results are ready to be displayed. diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 967220abbf..a9fdf1110f 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -72,7 +72,7 @@ namespace osu.Game.Online.Multiplayer /// /// Invoked when the multiplayer server requests gameplay to be started. /// - public event Action? MatchStarted; + public event Action? GameplayStarted; /// /// Invoked when the multiplayer server has finished collating results. @@ -604,14 +604,14 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } - Task IMultiplayerClient.MatchStarted() + Task IMultiplayerClient.GameplayStarted() { Scheduler.Add(() => { if (Room == null) return; - MatchStarted?.Invoke(); + GameplayStarted?.Invoke(); }, false); return Task.CompletedTask; diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 7e62908ecd..de93233830 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -54,7 +54,7 @@ namespace osu.Game.Online.Multiplayer connection.On(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged); connection.On(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged); connection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested); - connection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted); + connection.On(nameof(IMultiplayerClient.GameplayStarted), ((IMultiplayerClient)this).GameplayStarted); connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); connection.On>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged); connection.On(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 70f8f1b752..d3a78a43a5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -115,7 +115,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (!ValidForResume) return; // token retrieval may have failed. - client.MatchStarted += onMatchStarted; + client.GameplayStarted += onGameplayStarted; client.ResultsReady += onResultsReady; ScoreProcessor.HasCompleted.BindValueChanged(completed => @@ -175,7 +175,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer leaderboardFlow.Position = new Vector2(padding, padding + HUDOverlay.TopScoringElementsHeight); } - private void onMatchStarted() => Scheduler.Add(() => + private void onGameplayStarted() => Scheduler.Add(() => { if (!this.IsCurrentScreen()) return; @@ -223,7 +223,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client != null) { - client.MatchStarted -= onMatchStarted; + client.GameplayStarted -= onGameplayStarted; client.ResultsReady -= onResultsReady; } } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 21774b73a0..725499d0e5 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -155,7 +155,7 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var u in Room.Users.Where(u => u.State == MultiplayerUserState.Loaded)) ChangeUserState(u.UserID, MultiplayerUserState.Playing); - ((IMultiplayerClient)this).MatchStarted(); + ((IMultiplayerClient)this).GameplayStarted(); ChangeRoomState(MultiplayerRoomState.Playing); } From 59622deb1fcf0d06b60302698bad24238e66e49e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:38:58 +0900 Subject: [PATCH 0474/1276] Add LoadAborted() event --- .../Online/Multiplayer/IMultiplayerClient.cs | 7 ++++++- .../Online/Multiplayer/MultiplayerClient.cs | 18 ++++++++++++++++++ .../Multiplayer/OnlineMultiplayerClient.cs | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 43ccfd36ed..2f454ea835 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -93,10 +93,15 @@ namespace osu.Game.Online.Multiplayer Task UserModsChanged(int userId, IEnumerable mods); /// - /// Signals that a match is to be started. This will *only* be sent to clients which are to begin loading at this point. + /// Signals that the match is starting and the loading of gameplay should be started. This will *only* be sent to clients which are to begin loading at this point. /// Task LoadRequested(); + /// + /// Signals that loading of gameplay is to be aborted. + /// + Task LoadAborted(); + /// /// Signals that gameplay has started. /// All users in the or states should begin gameplay as soon as possible. diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index a9fdf1110f..cae675b406 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -69,6 +69,11 @@ namespace osu.Game.Online.Multiplayer /// public virtual event Action? LoadRequested; + /// + /// Invoked when the multiplayer server requests loading of play to be aborted. + /// + public event Action? LoadAborted; + /// /// Invoked when the multiplayer server requests gameplay to be started. /// @@ -604,6 +609,19 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } + Task IMultiplayerClient.LoadAborted() + { + Scheduler.Add(() => + { + if (Room == null) + return; + + LoadAborted?.Invoke(); + }, false); + + return Task.CompletedTask; + } + Task IMultiplayerClient.GameplayStarted() { Scheduler.Add(() => diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index de93233830..4dc23d8b85 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -55,6 +55,7 @@ namespace osu.Game.Online.Multiplayer connection.On(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged); connection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested); connection.On(nameof(IMultiplayerClient.GameplayStarted), ((IMultiplayerClient)this).GameplayStarted); + connection.On(nameof(IMultiplayerClient.LoadAborted), ((IMultiplayerClient)this).LoadAborted); connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); connection.On>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged); connection.On(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged); From 41355384bdab55039c78b59f11500d2d1d497761 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:55:13 +0900 Subject: [PATCH 0475/1276] Add support for gameplay abort/force start --- .../OnlinePlay/Multiplayer/Multiplayer.cs | 18 ++++++++++++++++++ .../Multiplayer/MultiplayerPlayer.cs | 13 +++++++++---- .../Multiplayer/MultiplayerPlayerLoader.cs | 10 ++++++++++ osu.Game/Screens/Play/PlayerLoader.cs | 12 ++++++++---- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 2482d52492..c78756771a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using osu.Framework.Allocation; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Screens.OnlinePlay.Components; @@ -20,6 +21,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.LoadComplete(); client.RoomUpdated += onRoomUpdated; + client.LoadAborted += onLoadAborted; onRoomUpdated(); } @@ -35,6 +37,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer transitionFromResults(); } + private void onLoadAborted() + { + // If the server aborts gameplay for this user (due to loading too slow), exit gameplay screens. + if (!this.IsCurrentScreen()) + { + Logger.Log("Gameplay aborted because loading the beatmap took too long.", LoggingTarget.Runtime, LogLevel.Important); + this.MakeCurrent(); + } + } + public override void OnResuming(IScreen last) { base.OnResuming(last); @@ -42,9 +54,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client.Room == null) return; + Debug.Assert(client.LocalUser != null); + if (!(last is MultiplayerPlayerLoader playerLoader)) return; + // Nothing needs to be done if already in the idle state (e.g. via load being aborted by the server). + if (client.LocalUser.State == MultiplayerUserState.Idle) + return; + // If gameplay wasn't finished, then we have a simple path back to the idle state by aborting gameplay. if (!playerLoader.GameplayPassed) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index d3a78a43a5..7dd57ee50f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -133,6 +133,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer failAndBail(); } }), true); + + client.ChangeState(MultiplayerUserState.Loaded) + .ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); } protected override void LoadComplete() @@ -144,10 +147,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override void StartGameplay() { - // block base call, but let the server know we are ready to start. - loadingDisplay.Show(); - - client.ChangeState(MultiplayerUserState.Loaded).ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); + if (client.LocalUser?.State == MultiplayerUserState.Loaded) + { + // block base call, but let the server know we are ready to start. + loadingDisplay.Show(); + client.ChangeState(MultiplayerUserState.ReadyForGameplay); + } } private void failAndBail(string message = null) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs index 772651727e..9964452f61 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Screens; +using osu.Game.Online.Multiplayer; using osu.Game.Screens.Play; namespace osu.Game.Screens.OnlinePlay.Multiplayer @@ -11,6 +13,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public bool GameplayPassed => player?.GameplayState.HasPassed == true; + [Resolved] + private MultiplayerClient multiplayerClient { get; set; } + private Player player; public MultiplayerPlayerLoader(Func createPlayer) @@ -18,6 +23,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { } + protected override bool ReadyForGameplay => + base.ReadyForGameplay + // The server is forcefully starting gameplay. + || multiplayerClient.LocalUser?.State == MultiplayerUserState.Playing; + public override void OnSuspending(IScreen next) { base.OnSuspending(next); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index aea39bc8ff..f7dae6eaa9 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -92,11 +92,15 @@ namespace osu.Game.Screens.Play !playerConsumed // don't push unless the player is completely loaded && CurrentPlayer?.LoadState == LoadState.Ready - // don't push if the user is hovering one of the panes, unless they are idle. - && (IsHovered || idleTracker.IsIdle.Value) - // don't push if the user is dragging a slider or otherwise. + // don't push unless the player is ready to start gameplay + && ReadyForGameplay; + + protected virtual bool ReadyForGameplay => + // not ready if the user is hovering one of the panes, unless they are idle. + (IsHovered || idleTracker.IsIdle.Value) + // not ready if the user is dragging a slider or otherwise. && inputManager.DraggedDrawable == null - // don't push if a focused overlay is visible, like settings. + // not ready if a focused overlay is visible, like settings. && inputManager.FocusedDrawable == null; private readonly Func createPlayer; From cbb07d4011a8db5de79c775f659eaa6ea7f0da34 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 22:56:24 +0900 Subject: [PATCH 0476/1276] Add countdown classes --- .../Online/Multiplayer/GameplayStartCountdown.cs | 15 +++++++++++++++ .../Online/Multiplayer/MultiplayerCountdown.cs | 1 + osu.Game/Online/SignalRWorkaroundTypes.cs | 3 ++- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Online/Multiplayer/GameplayStartCountdown.cs diff --git a/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs b/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs new file mode 100644 index 0000000000..d3d7f78507 --- /dev/null +++ b/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using MessagePack; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// A indicating that gameplay will start after a given period of time. + /// + [MessagePackObject] + public class GameplayStartCountdown : MultiplayerCountdown + { + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs b/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs index 81190e64c9..308eea3aef 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs @@ -14,6 +14,7 @@ namespace osu.Game.Online.Multiplayer /// [MessagePackObject] [Union(0, typeof(MatchStartCountdown))] // IMPORTANT: Add rules to SignalRUnionWorkaroundResolver for new derived types. + [Union(1, typeof(GameplayStartCountdown))] public abstract class MultiplayerCountdown { /// diff --git a/osu.Game/Online/SignalRWorkaroundTypes.cs b/osu.Game/Online/SignalRWorkaroundTypes.cs index 156f916cef..00fdadb87e 100644 --- a/osu.Game/Online/SignalRWorkaroundTypes.cs +++ b/osu.Game/Online/SignalRWorkaroundTypes.cs @@ -24,7 +24,8 @@ namespace osu.Game.Online (typeof(CountdownChangedEvent), typeof(MatchServerEvent)), (typeof(TeamVersusRoomState), typeof(MatchRoomState)), (typeof(TeamVersusUserState), typeof(MatchUserState)), - (typeof(MatchStartCountdown), typeof(MultiplayerCountdown)) + (typeof(MatchStartCountdown), typeof(MultiplayerCountdown)), + (typeof(GameplayStartCountdown), typeof(MultiplayerCountdown)) }; } } From ef1955d8ab2651a9ceff1386acd152762577bf45 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 23:01:16 +0900 Subject: [PATCH 0477/1276] Make buttons only respond to MatchStartCountdown --- .../Match/MultiplayerCountdownButton.cs | 2 +- .../Multiplayer/Match/MultiplayerReadyButton.cs | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs index c84fcff11e..1a51aebb76 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void onRoomUpdated() => Scheduler.AddOnce(() => { - bool countdownActive = multiplayerClient.Room?.Countdown != null; + bool countdownActive = multiplayerClient.Room?.Countdown is MatchStartCountdown; if (countdownActive) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 22a0243f8f..a7e18622dc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -55,7 +55,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void onRoomUpdated() => Scheduler.AddOnce(() => { - if (countdown != room?.Countdown) + MultiplayerCountdown newCountdown; + + switch (room?.Countdown) + { + case MatchStartCountdown _: + newCountdown = room.Countdown; + break; + + // Clear the countdown with any other (including non-null) countdown values. + default: + newCountdown = null; + break; + } + + if (newCountdown != countdown) { countdown = room?.Countdown; countdownChangeTime = Time.Current; From a0a83fb51fa1d69f4ade683fb5dd7e3db4e19130 Mon Sep 17 00:00:00 2001 From: 63411 <62799417+molneya@users.noreply.github.com> Date: Thu, 21 Apr 2022 23:03:28 +0800 Subject: [PATCH 0478/1276] Use closest endTime for holdAddition validity --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index ab6bd78ece..2e252acdc8 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -37,6 +37,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills var maniaCurrent = (ManiaDifficultyHitObject)current; double endTime = maniaCurrent.EndTime; int column = maniaCurrent.BaseObject.Column; + double closestEndTime = 100; double holdFactor = 1.0; // Factor to all additional strains in case something else is held double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly @@ -44,24 +45,26 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills // Fill up the holdEndTimes array for (int i = 0; i < holdEndTimes.Length; ++i) { - // If there is at least one other overlapping end or note, then we get an addition, buuuuuut... + // If there is at least one other overlapping end or note, then we get an addition if (Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1)) holdAddition = 1.0; - // ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1 - if (Precision.AlmostEquals(endTime, holdEndTimes[i], 1)) - holdAddition = 0; - // We give a slight bonus to everything if something is held meanwhile if (Precision.DefinitelyBigger(holdEndTimes[i], endTime, 1)) holdFactor = 1.25; + closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - holdEndTimes[i])); + // Decay individual strains individualStrains[i] = applyDecay(individualStrains[i], current.DeltaTime, individual_decay_base); } holdEndTimes[column] = endTime; + // The hold addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1 + if (closestEndTime < 1) + holdAddition = 0; + // Increase individual strain in own column individualStrains[column] += 2.0 * holdFactor; individualStrain = individualStrains[column]; From e7a149af6c1c7091a4406df3fddb980b8ac5fb9f Mon Sep 17 00:00:00 2001 From: 63411 <62799417+molneya@users.noreply.github.com> Date: Thu, 21 Apr 2022 23:05:20 +0800 Subject: [PATCH 0479/1276] Use curve for holdAddition validity cutoff --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 2e252acdc8..d5d5d5751d 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -62,8 +62,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills holdEndTimes[column] = endTime; // The hold addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1 - if (closestEndTime < 1) - holdAddition = 0; + holdAddition *= 1 / (1 + Math.Exp(0.5 * (24 - closestEndTime))); // Increase individual strain in own column individualStrains[column] += 2.0 * holdFactor; From 4d3044b841c4f5d9b73a144aaabf7bd1fc7b1725 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 00:49:15 +0900 Subject: [PATCH 0480/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8b22df418f..6b06305bf9 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0a102510e7..c57acb3b7f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 2f0facea18..d7f89b802b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 832d37b2c2c9b5f2cde6bec90804e6dab36e8e0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 00:52:44 +0900 Subject: [PATCH 0481/1276] Update screen transition events to use new event args --- .../Background/TestSceneUserDimBackgrounds.cs | 4 ++-- .../Visual/Gameplay/TestScenePause.cs | 4 ++-- .../Navigation/TestScenePerformFromScreen.cs | 8 ++++---- .../Visual/TestMultiplayerComponents.cs | 4 ++-- .../Graphics/Containers/ScalingContainer.cs | 2 +- .../AccountCreation/AccountCreationScreen.cs | 12 +++++------ .../Overlays/AccountCreation/ScreenEntry.cs | 4 ++-- .../Overlays/AccountCreation/ScreenWarning.cs | 4 ++-- .../Maintenance/DirectorySelectScreen.cs | 4 ++-- .../Maintenance/MigrationRunScreen.cs | 8 ++++---- .../StableDirectorySelectScreen.cs | 4 ++-- osu.Game/Screens/BackgroundScreen.cs | 16 +++++++-------- .../Backgrounds/BackgroundScreenBlack.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 16 +++++++-------- .../Screens/Edit/GameplayTest/EditorPlayer.cs | 8 ++++---- .../Edit/GameplayTest/EditorPlayerLoader.cs | 4 ++-- osu.Game/Screens/Import/FileImportScreen.cs | 8 ++++---- osu.Game/Screens/Loader.cs | 4 ++-- osu.Game/Screens/Menu/Disclaimer.cs | 4 ++-- osu.Game/Screens/Menu/IntroCircles.cs | 4 ++-- osu.Game/Screens/Menu/IntroScreen.cs | 12 +++++------ osu.Game/Screens/Menu/IntroTriangles.cs | 8 ++++---- osu.Game/Screens/Menu/IntroWelcome.cs | 4 ++-- osu.Game/Screens/Menu/MainMenu.cs | 18 ++++++++--------- .../Components/OnlinePlayBackgroundScreen.cs | 8 ++++---- .../Lounge/LoungeBackgroundScreen.cs | 2 +- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 16 +++++++-------- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 16 +++++++-------- .../OnlinePlay/Multiplayer/Multiplayer.cs | 6 +++--- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 6 +++--- .../Multiplayer/MultiplayerMatchSubScreen.cs | 6 +++--- .../Multiplayer/MultiplayerPlayerLoader.cs | 6 +++--- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 20 +++++++++---------- .../OnlinePlay/OnlinePlaySongSelect.cs | 4 ++-- .../Screens/OnlinePlay/OnlinePlaySubScreen.cs | 16 +++++++-------- .../OnlinePlay/Playlists/PlaylistsPlayer.cs | 4 ++-- osu.Game/Screens/OsuScreen.cs | 16 +++++++-------- osu.Game/Screens/Play/Player.cs | 12 +++++------ osu.Game/Screens/Play/PlayerLoader.cs | 16 +++++++-------- osu.Game/Screens/Play/ReplayPlayerLoader.cs | 4 ++-- osu.Game/Screens/Play/SoloSpectator.cs | 4 ++-- osu.Game/Screens/Play/SoloSpectatorPlayer.cs | 4 ++-- osu.Game/Screens/Play/SpectatorPlayer.cs | 4 ++-- .../Screens/Play/SpectatorPlayerLoader.cs | 4 ++-- osu.Game/Screens/Play/SubmittingPlayer.cs | 4 ++-- osu.Game/Screens/Ranking/ResultsScreen.cs | 8 ++++---- osu.Game/Screens/ScreenWhiteBox.cs | 12 +++++------ osu.Game/Screens/Select/PlaySongSelect.cs | 4 ++-- osu.Game/Screens/Select/SongSelect.cs | 16 +++++++-------- osu.Game/Tests/Visual/EditorTestScene.cs | 4 ++-- 50 files changed, 194 insertions(+), 194 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 9f708ace70..f7140537ee 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -359,9 +359,9 @@ namespace osu.Game.Tests.Visual.Background protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); ApplyToBackground(b => ReplacesBackground.BindTo(b.StoryboardReplacesBackground)); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index ea0255ab76..a224a78531 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -389,9 +389,9 @@ namespace osu.Game.Tests.Visual.Gameplay public void ExitViaQuickExit() => PerformExit(false); - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); GameplayClockContainer.Stop(); } } diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 559e6ddd89..2ce914ba3d 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -230,7 +230,7 @@ namespace osu.Game.Tests.Visual.Navigation public int ExitAttempts { get; private set; } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { ExitAttempts++; @@ -240,7 +240,7 @@ namespace osu.Game.Tests.Visual.Navigation return true; } - return base.OnExiting(next); + return base.OnExiting(e); } } @@ -257,7 +257,7 @@ namespace osu.Game.Tests.Visual.Navigation SubScreenStack.Push(Blocker = new DialogBlockingScreen()); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (SubScreenStack.CurrentScreen != null) { @@ -265,7 +265,7 @@ namespace osu.Game.Tests.Visual.Navigation return true; } - return base.OnExiting(next); + return base.OnExiting(e); } } } diff --git a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs index c43ed744bd..4ab201ef46 100644 --- a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs +++ b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs @@ -80,10 +80,10 @@ namespace osu.Game.Tests.Visual public override bool OnBackButton() => (screenStack.CurrentScreen as OsuScreen)?.OnBackButton() ?? base.OnBackButton(); - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (screenStack.CurrentScreen == null) - return base.OnExiting(next); + return base.OnExiting(e); screenStack.Exit(); return true; diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index ca8b6f388f..89432595f3 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -209,7 +209,7 @@ namespace osu.Game.Graphics.Containers { protected override bool AllowStoryboardBackground => false; - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { this.FadeInFromZero(4000, Easing.OutQuint); } diff --git a/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs b/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs index 7e2ae405cb..6aef358b2e 100644 --- a/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs +++ b/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs @@ -8,21 +8,21 @@ namespace osu.Game.Overlays.AccountCreation { public abstract class AccountCreationScreen : Screen { - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); this.FadeOut().Delay(200).FadeIn(200); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); this.FadeIn(200); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); this.FadeOut(200); } } diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index 6cf3fb4267..1be1321d85 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -147,9 +147,9 @@ namespace osu.Game.Overlays.AccountCreation d.Colour = password.Length == 0 ? Color4.White : Interpolation.ValueAt(password.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In); } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); loadingLayer.Hide(); if (host?.OnScreenKeyboardOverlapsGameWindow != true) diff --git a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs index 3d46e9ed94..780a79f8f9 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.AccountCreation private const string help_centre_url = "/help/wiki/Help_Centre#login"; - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { if (string.IsNullOrEmpty(api?.ProvidedUsername) || game?.UseDevelopmentServer == true) { @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.AccountCreation return; } - base.OnEntering(last); + base.OnEntering(e); } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs index 98bc8d88be..c7fd248842 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs @@ -124,9 +124,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance base.LoadComplete(); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); this.FadeOut(250); } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs index 10d6f0aef5..b7b797936e 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs @@ -124,20 +124,20 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected virtual bool PerformMigration() => game?.Migrate(destination.FullName) != false; - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); this.FadeOut().Delay(250).Then().FadeIn(250); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { // block until migration is finished if (migrationTask?.IsCompleted == false) return true; - return base.OnExiting(next); + return base.OnExiting(e); } } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 4aea05fb14..86934ae514 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -30,10 +30,10 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance this.Exit(); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { taskCompletionSource.TrySetCanceled(); - return base.OnExiting(next); + return base.OnExiting(e); } } } diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index a706934cce..6084ec4b01 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens Scale = new Vector2(1 + x_movement_amount / DrawSize.X * 2); } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { if (animateOnEnter) { @@ -59,16 +59,16 @@ namespace osu.Game.Screens this.MoveToX(0, TRANSITION_LENGTH, Easing.InOutQuart); } - base.OnEntering(last); + base.OnEntering(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { this.MoveToX(-x_movement_amount, TRANSITION_LENGTH, Easing.InOutQuart); - base.OnSuspending(next); + base.OnSuspending(e); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (IsLoaded) { @@ -76,14 +76,14 @@ namespace osu.Game.Screens this.MoveToX(x_movement_amount, TRANSITION_LENGTH, Easing.OutExpo); } - return base.OnExiting(next); + return base.OnExiting(e); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { if (IsLoaded) this.MoveToX(0, TRANSITION_LENGTH, Easing.OutExpo); - base.OnResuming(last); + base.OnResuming(e); } } } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs index 9e2559cc56..d946fd41d9 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Backgrounds }; } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { Show(); } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 30c57733ed..3fde033587 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -560,16 +560,16 @@ namespace osu.Game.Screens.Edit { } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); dimBackground(); resetTrack(true); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); dimBackground(); } @@ -585,7 +585,7 @@ namespace osu.Game.Screens.Edit }); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (!ExitConfirmed) { @@ -613,12 +613,12 @@ namespace osu.Game.Screens.Edit refetchBeatmap(); - return base.OnExiting(next); + return base.OnExiting(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); clock.Stop(); refetchBeatmap(); } diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs index d9e19df350..f7e450b0e2 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs @@ -44,9 +44,9 @@ namespace osu.Game.Screens.Edit.GameplayTest protected override bool CheckModsAllowFailure() => false; // never fail. - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); // finish alpha transforms on entering to avoid gameplay starting in a half-hidden state. // the finish calls are purposefully not propagated to children to avoid messing up their state. @@ -54,13 +54,13 @@ namespace osu.Game.Screens.Edit.GameplayTest GameplayClockContainer.FinishTransforms(false, nameof(Alpha)); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { musicController.Stop(); editorState.Time = GameplayClockContainer.CurrentTime; editor.RestoreState(editorState); - return base.OnExiting(next); + return base.OnExiting(e); } } } diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs index addc79ba61..c16bb8677c 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs @@ -19,9 +19,9 @@ namespace osu.Game.Screens.Edit.GameplayTest { } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); MetadataInfo.FinishTransforms(true); } diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 09870e0bab..32ce54aa29 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -118,20 +118,20 @@ namespace osu.Game.Screens.Import fileSelector.CurrentPath.BindValueChanged(directoryChanged); } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); contentContainer.ScaleTo(0.95f).ScaleTo(1, duration, Easing.OutQuint); this.FadeInFromZero(duration); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { contentContainer.ScaleTo(0.95f, duration, Easing.OutQuint); this.FadeOut(duration, Easing.OutQuint); - return base.OnExiting(next); + return base.OnExiting(e); } private void directoryChanged(ValueChangedEvent _) diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index a72ba89dfa..52e83c9e98 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -69,9 +69,9 @@ namespace osu.Game.Screens private EFToRealmMigrator realmMigrator; - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); LoadComponentAsync(precompiler = CreateShaderPrecompiler(), AddInternal); diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 22151db0dd..24412cd85e 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -171,9 +171,9 @@ namespace osu.Game.Screens.Menu ((IBindable)currentUser).BindTo(api.LocalUser); } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); icon.RotateTo(10); icon.FadeOut(); diff --git a/osu.Game/Screens/Menu/IntroCircles.cs b/osu.Game/Screens/Menu/IntroCircles.cs index 2792d05f75..00e2de62f0 100644 --- a/osu.Game/Screens/Menu/IntroCircles.cs +++ b/osu.Game/Screens/Menu/IntroCircles.cs @@ -57,10 +57,10 @@ namespace osu.Game.Screens.Menu } } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { this.FadeOut(300); - base.OnSuspending(next); + base.OnSuspending(e); } } } diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 5575d3681f..d4072d6202 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -164,9 +164,9 @@ namespace osu.Game.Screens.Menu } } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); ensureEventuallyArrivingAtMenu(); } @@ -194,7 +194,7 @@ namespace osu.Game.Screens.Menu }, 5000); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { this.FadeIn(300); @@ -237,12 +237,12 @@ namespace osu.Game.Screens.Menu //don't want to fade out completely else we will stop running updates. Game.FadeTo(0.01f, fadeOutTime).OnComplete(_ => this.Exit()); - base.OnResuming(last); + base.OnResuming(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); initialBeatmap = null; } diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index b6b6bf2ad7..ba8314f103 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -89,9 +89,9 @@ namespace osu.Game.Screens.Menu } } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); // ensure the background is shown, even if the TriangleIntroSequence failed to do so. background.ApplyToBackground(b => b.Show()); @@ -100,9 +100,9 @@ namespace osu.Game.Screens.Menu intro.Expire(); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); background.FadeOut(100); } diff --git a/osu.Game/Screens/Menu/IntroWelcome.cs b/osu.Game/Screens/Menu/IntroWelcome.cs index 27eaa7eb3a..9a6c949cad 100644 --- a/osu.Game/Screens/Menu/IntroWelcome.cs +++ b/osu.Game/Screens/Menu/IntroWelcome.cs @@ -106,9 +106,9 @@ namespace osu.Game.Screens.Menu } } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); background.FadeOut(100); } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 08df06816b..c9ce077695 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -176,12 +176,12 @@ namespace osu.Game.Screens.Menu [Resolved] private Storage storage { get; set; } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); buttons.FadeInFromZero(500); - if (last is IntroScreen && musicController.TrackLoaded) + if (e.Last is IntroScreen && musicController.TrackLoaded) { var track = musicController.CurrentTrack; @@ -249,9 +249,9 @@ namespace osu.Game.Screens.Menu seq.OnAbort(_ => buttons.SetOsuLogo(null)); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); buttons.State = ButtonSystemState.EnteringMode; @@ -261,9 +261,9 @@ namespace osu.Game.Screens.Menu sideFlashes.FadeOut(64, Easing.OutQuint); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); ApplyToBackground(b => (b as BackgroundScreenDefault)?.Next()); @@ -273,7 +273,7 @@ namespace osu.Game.Screens.Menu musicController.EnsurePlayingSomething(); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (!exitConfirmed && dialogOverlay != null) { @@ -291,7 +291,7 @@ namespace osu.Game.Screens.Menu songTicker.Hide(); this.FadeOut(3000); - return base.OnExiting(next); + return base.OnExiting(e); } public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset) diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs index 8906bebf0e..9e964de31e 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs @@ -91,15 +91,15 @@ namespace osu.Game.Screens.OnlinePlay.Components AddInternal(background = newBackground); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); this.MoveToX(0, TRANSITION_LENGTH); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - bool result = base.OnExiting(next); + bool result = base.OnExiting(e); this.MoveToX(0); return result; } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs index 926c35c5da..52a902f5da 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge playlist.Clear(); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { // This screen never exits. return true; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index a2d3b7f4fc..ec55ae79ce 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -238,15 +238,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge #endregion - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); onReturning(); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); Debug.Assert(selectionLease != null); @@ -261,16 +261,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge onReturning(); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { onLeaving(); - return base.OnExiting(next); + return base.OnExiting(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { onLeaving(); - base.OnSuspending(next); + base.OnSuspending(e); } protected override void OnFocus(FocusEvent e) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index a382f65d84..cc1f842f8c 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -290,35 +290,35 @@ namespace osu.Game.Screens.OnlinePlay.Match protected void ShowUserModSelect() => userModsSelectOverlay.Show(); - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); beginHandlingTrack(); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { endHandlingTrack(); - base.OnSuspending(next); + base.OnSuspending(e); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); updateWorkingBeatmap(); beginHandlingTrack(); Scheduler.AddOnce(UpdateMods); Scheduler.AddOnce(updateRuleset); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { RoomManager?.PartRoom(); Mods.Value = Array.Empty(); endHandlingTrack(); - return base.OnExiting(next); + return base.OnExiting(e); } protected void StartPlay() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 2482d52492..66f6935bcc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -35,14 +35,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer transitionFromResults(); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); if (client.Room == null) return; - if (!(last is MultiplayerPlayerLoader playerLoader)) + if (!(e.Last is MultiplayerPlayerLoader playerLoader)) return; // If gameplay wasn't finished, then we have a simple path back to the idle state by aborting gameplay. diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 5cdec52bc2..a05f248d3a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -25,13 +25,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); // Upon having left a room, we don't know whether we were the only participant, and whether the room is now closed as a result of leaving it. // To work around this, temporarily remove the room and trigger an immediate listing poll. - if (last is MultiplayerMatchSubScreen match) + if (e.Last is MultiplayerMatchSubScreen match) { RoomManager.RemoveRoom(match.Room); ListingPollingComponent.PollImmediately(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 0dbf73fc5b..769873f74c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -242,14 +242,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private bool exitConfirmed; - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { // the room may not be left immediately after a disconnection due to async flow, // so checking the IsConnected status is also required. if (client.Room == null || !client.IsConnected.Value) { // room has not been created yet; exit immediately. - return base.OnExiting(next); + return base.OnExiting(e); } if (!exitConfirmed && dialogOverlay != null) @@ -268,7 +268,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return true; } - return base.OnExiting(next); + return base.OnExiting(e); } private ModSettingChangeTracker modSettingChangeTracker; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs index 772651727e..53dea83f18 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs @@ -18,10 +18,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); - player = (Player)next; + base.OnSuspending(e); + player = (Player)e.Next; } } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index c56d04d5ac..ff4225e155 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -110,7 +110,7 @@ namespace osu.Game.Screens.OnlinePlay } } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { this.FadeIn(); waves.Show(); @@ -118,35 +118,35 @@ namespace osu.Game.Screens.OnlinePlay Mods.SetDefault(); if (loungeSubScreen.IsCurrentScreen()) - loungeSubScreen.OnEntering(last); + loungeSubScreen.OnEntering(e); else loungeSubScreen.MakeCurrent(); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { this.FadeIn(250); this.ScaleTo(1, 250, Easing.OutSine); Debug.Assert(screenStack.CurrentScreen != null); - screenStack.CurrentScreen.OnResuming(last); + screenStack.CurrentScreen.OnResuming(e); - base.OnResuming(last); + base.OnResuming(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { this.ScaleTo(1.1f, 250, Easing.InSine); this.FadeOut(250); Debug.Assert(screenStack.CurrentScreen != null); - screenStack.CurrentScreen.OnSuspending(next); + screenStack.CurrentScreen.OnSuspending(e); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { var subScreen = screenStack.CurrentScreen as Drawable; - if (subScreen?.IsLoaded == true && screenStack.CurrentScreen.OnExiting(next)) + if (subScreen?.IsLoaded == true && screenStack.CurrentScreen.OnExiting(e)) return true; RoomManager.PartRoom(); @@ -155,7 +155,7 @@ namespace osu.Game.Screens.OnlinePlay this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut(); - base.OnExiting(next); + base.OnExiting(e); return false; } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 7b64784316..6a559dbb2c 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -141,7 +141,7 @@ namespace osu.Game.Screens.OnlinePlay return base.OnBackButton(); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (!itemSelected) { @@ -150,7 +150,7 @@ namespace osu.Game.Screens.OnlinePlay Mods.Value = initialMods; } - return base.OnExiting(next); + return base.OnExiting(e); } protected override ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index 3411c4afb1..07e0f60011 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -27,28 +27,28 @@ namespace osu.Game.Screens.OnlinePlay public const double DISAPPEAR_DURATION = 500; - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); this.FadeInFromZero(APPEAR_DURATION, Easing.OutQuint); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - base.OnExiting(next); + base.OnExiting(e); this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); return false; } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); this.FadeIn(APPEAR_DURATION, Easing.OutQuint); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 5a7762a3d8..5cba8676c5 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -45,9 +45,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods"); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - if (base.OnExiting(next)) + if (base.OnExiting(e)) return true; Exited?.Invoke(); diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index ed4901e1fa..4035d168e1 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -171,7 +171,7 @@ namespace osu.Game.Screens background.ApplyToBackground(action); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { if (PlayResumeSound) sampleExit?.Play(); @@ -183,19 +183,19 @@ namespace osu.Game.Screens if (trackAdjustmentStateAtSuspend != null) musicController.AllowTrackAdjustments = trackAdjustmentStateAtSuspend.Value; - base.OnResuming(last); + base.OnResuming(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); trackAdjustmentStateAtSuspend = musicController.AllowTrackAdjustments; onSuspendingLogo(); } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { applyArrivingDefaults(false); @@ -210,15 +210,15 @@ namespace osu.Game.Screens } background = backgroundStack?.CurrentScreen as BackgroundScreen; - base.OnEntering(last); + base.OnEntering(e); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (ValidForResume && logo != null) onExitingLogo(); - if (base.OnExiting(next)) + if (base.OnExiting(e)) return true; if (ownedBackground != null && backgroundStack?.CurrentScreen == ownedBackground) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f99b07c313..2d5a67758a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -917,9 +917,9 @@ namespace osu.Game.Screens.Play #region Screen Logic - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); if (!LoadedBeatmapSuccessfully) return; @@ -985,15 +985,15 @@ namespace osu.Game.Screens.Play GameplayClockContainer.Reset(true); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { screenSuspension?.RemoveAndDisposeImmediately(); fadeOut(); - base.OnSuspending(next); + base.OnSuspending(e); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { screenSuspension?.RemoveAndDisposeImmediately(); failAnimationLayer?.RemoveFilters(); @@ -1024,7 +1024,7 @@ namespace osu.Game.Screens.Play musicController.ResetTrackAdjustments(); fadeOut(); - return base.OnExiting(next); + return base.OnExiting(e); } /// diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index aea39bc8ff..494ab51a10 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -210,9 +210,9 @@ namespace osu.Game.Screens.Play #region Screen handling - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); ApplyToBackground(b => { @@ -236,9 +236,9 @@ namespace osu.Game.Screens.Play showBatteryWarningIfNeeded(); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); Debug.Assert(CurrentPlayer != null); @@ -254,9 +254,9 @@ namespace osu.Game.Screens.Play contentIn(); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); BackgroundBrightnessReduction = false; @@ -268,7 +268,7 @@ namespace osu.Game.Screens.Play highPassFilter.CutoffTo(0); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { cancelLoad(); ContentOut(); @@ -284,7 +284,7 @@ namespace osu.Game.Screens.Play BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); - return base.OnExiting(next); + return base.OnExiting(e); } protected override void LogoArriving(OsuLogo logo, bool resuming) diff --git a/osu.Game/Screens/Play/ReplayPlayerLoader.cs b/osu.Game/Screens/Play/ReplayPlayerLoader.cs index 9eff4cb8fc..e78f700af2 100644 --- a/osu.Game/Screens/Play/ReplayPlayerLoader.cs +++ b/osu.Game/Screens/Play/ReplayPlayerLoader.cs @@ -20,13 +20,13 @@ namespace osu.Game.Screens.Play Score = score.ScoreInfo; } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { // these will be reverted thanks to PlayerLoader's lease. Mods.Value = Score.Mods; Ruleset.Value = Score.Ruleset; - base.OnEntering(last); + base.OnEntering(e); } } } diff --git a/osu.Game/Screens/Play/SoloSpectator.cs b/osu.Game/Screens/Play/SoloSpectator.cs index a0b07fcbd9..202527f308 100644 --- a/osu.Game/Screens/Play/SoloSpectator.cs +++ b/osu.Game/Screens/Play/SoloSpectator.cs @@ -249,10 +249,10 @@ namespace osu.Game.Screens.Play beatmapDownloader.Download(beatmapSet); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { previewTrackManager.StopAnyPlaying(this); - return base.OnExiting(next); + return base.OnExiting(e); } } } diff --git a/osu.Game/Screens/Play/SoloSpectatorPlayer.cs b/osu.Game/Screens/Play/SoloSpectatorPlayer.cs index 969a5bf2b4..5b601083c2 100644 --- a/osu.Game/Screens/Play/SoloSpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SoloSpectatorPlayer.cs @@ -24,11 +24,11 @@ namespace osu.Game.Screens.Play SpectatorClient.OnUserBeganPlaying += userBeganPlaying; } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { SpectatorClient.OnUserBeganPlaying -= userBeganPlaying; - return base.OnExiting(next); + return base.OnExiting(e); } private void userBeganPlaying(int userId, SpectatorState state) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index c0682952c3..09bec9b89f 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -91,11 +91,11 @@ namespace osu.Game.Screens.Play DrawableRuleset?.SetReplayScore(score); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { SpectatorClient.OnNewFrames -= userSentFrames; - return base.OnExiting(next); + return base.OnExiting(e); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/SpectatorPlayerLoader.cs b/osu.Game/Screens/Play/SpectatorPlayerLoader.cs index 10cc36c9a9..9ca5475ee4 100644 --- a/osu.Game/Screens/Play/SpectatorPlayerLoader.cs +++ b/osu.Game/Screens/Play/SpectatorPlayerLoader.cs @@ -20,13 +20,13 @@ namespace osu.Game.Screens.Play Score = score.ScoreInfo; } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { // these will be reverted thanks to PlayerLoader's lease. Mods.Value = Score.Mods; Ruleset.Value = Score.Ruleset; - base.OnEntering(last); + base.OnEntering(e); } } } diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index b1f2bccddf..b62dc1e5a6 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -115,9 +115,9 @@ namespace osu.Game.Screens.Play await submitScore(score).ConfigureAwait(false); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - bool exiting = base.OnExiting(next); + bool exiting = base.OnExiting(e); if (LoadedBeatmapSuccessfully) submitScore(Score.DeepClone()); diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index cb842ce4a0..98514cd846 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -231,9 +231,9 @@ namespace osu.Game.Screens.Ranking lastFetchCompleted = true; }); - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); ApplyToBackground(b => { @@ -244,9 +244,9 @@ namespace osu.Game.Screens.Ranking bottomPanel.FadeTo(1, 250); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - if (base.OnExiting(next)) + if (base.OnExiting(e)) return true; this.FadeOut(100); diff --git a/osu.Game/Screens/ScreenWhiteBox.cs b/osu.Game/Screens/ScreenWhiteBox.cs index 8b38b67f5c..3a9e7b8f18 100644 --- a/osu.Game/Screens/ScreenWhiteBox.cs +++ b/osu.Game/Screens/ScreenWhiteBox.cs @@ -28,25 +28,25 @@ namespace osu.Game.Screens protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg2"); - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { message.TextContainer.MoveTo(new Vector2(DrawSize.X / 16, 0), transition_time, Easing.OutExpo); this.FadeOut(transition_time, Easing.OutExpo); - return base.OnExiting(next); + return base.OnExiting(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); message.TextContainer.MoveTo(new Vector2(-(DrawSize.X / 16), 0), transition_time, Easing.OutExpo); this.FadeOut(transition_time, Easing.OutExpo); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); message.TextContainer.MoveTo(Vector2.Zero, transition_time, Easing.OutExpo); this.FadeIn(transition_time, Easing.OutExpo); diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 4a991ffc66..ec8b2e029a 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -109,9 +109,9 @@ namespace osu.Game.Screens.Select } } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); if (playerLoader != null) { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index dc505063d1..928978cd08 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -543,9 +543,9 @@ namespace osu.Game.Screens.Select } } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); this.FadeInFromZero(250); FilterControl.Activate(); @@ -591,9 +591,9 @@ namespace osu.Game.Screens.Select logo.FadeOut(logo_transition / 2, Easing.Out); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); // required due to https://github.com/ppy/osu-framework/issues/3218 ModSelect.SelectedMods.Disabled = false; @@ -622,7 +622,7 @@ namespace osu.Game.Screens.Select FilterControl.Activate(); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { // Handle the case where FinaliseSelection is never called (ie. when a screen is pushed externally). // Without this, it's possible for a transfer to happen while we are not the current screen. @@ -640,12 +640,12 @@ namespace osu.Game.Screens.Select this.FadeOut(250); FilterControl.Deactivate(); - base.OnSuspending(next); + base.OnSuspending(e); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - if (base.OnExiting(next)) + if (base.OnExiting(e)) return true; beatmapInfoWedge.Hide(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index ca12bc73fe..46f31ae53b 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -118,7 +118,7 @@ namespace osu.Game.Tests.Visual public new bool HasUnsavedChanges => base.HasUnsavedChanges; - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { // For testing purposes allow the screen to exit without saving on second attempt. if (!ExitConfirmed && dialogOverlay?.CurrentDialog is PromptForSaveDialog saveDialog) @@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual return true; } - return base.OnExiting(next); + return base.OnExiting(e); } public TestEditor(EditorLoader loader = null) From 749b86f0f41c7bed292d3eff81b40ceaf5b20fc4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 00:48:41 +0900 Subject: [PATCH 0482/1276] Use new framework exposed event instead of schedule --- osu.Game/Overlays/SettingsPanel.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 7cf233a061..a5a6f9bce7 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -284,14 +284,7 @@ namespace osu.Game.Overlays public string SearchTerm { get => SearchContainer.SearchTerm; - set - { - SearchContainer.SearchTerm = value; - - // Schedule required as search term takes a frame to update. - // Without this sections may not be in the correct state to ascertain CurrentSection. - Schedule(InvalidateScrollPosition); - } + set => SearchContainer.SearchTerm = value; } protected override FlowContainer CreateScrollContentContainer() @@ -310,6 +303,8 @@ namespace osu.Game.Overlays Colour = colourProvider.Background4, RelativeSizeAxes = Axes.Both }; + + SearchContainer.FilterCompleted += InvalidateScrollPosition; } protected override void UpdateAfterChildren() From 574007c07a15efd125bfa6c7076428747fe89e54 Mon Sep 17 00:00:00 2001 From: 63411 <62799417+molneya@users.noreply.github.com> Date: Fri, 22 Apr 2022 02:02:23 +0800 Subject: [PATCH 0483/1276] Optimise holdAddition calculation --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index d5d5d5751d..04d1329eec 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -62,7 +62,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills holdEndTimes[column] = endTime; // The hold addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1 - holdAddition *= 1 / (1 + Math.Exp(0.5 * (24 - closestEndTime))); + // Nerfs the hold addition by half if the closest release is 24ms away + if (holdAddition > 0) + holdAddition *= 1 / (1 + Math.Exp(0.5 * (24 - closestEndTime))); // Increase individual strain in own column individualStrains[column] += 2.0 * holdFactor; From 8fe4443029420bf4d1df46eda7c3a73b4018279a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Apr 2022 22:29:23 +0200 Subject: [PATCH 0484/1276] Update screen method signatures to match framework API changes --- .../FirstRunSetup/FirstRunSetupScreen.cs | 16 ++++++++-------- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index b2284a999d..eb4b97069c 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -33,39 +33,39 @@ namespace osu.Game.Overlays.FirstRunSetup }; } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); this .FadeInFromZero(500) .MoveToX(offset) .MoveToX(0, 500, Easing.OutQuint); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); this .FadeInFromZero(500) .MoveToX(0, 500, Easing.OutQuint); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { this .FadeOut(100) .MoveToX(offset, 500, Easing.OutQuint); - return base.OnExiting(next); + return base.OnExiting(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { this .FadeOut(100) .MoveToX(-offset, 500, Easing.OutQuint); - base.OnSuspending(next); + base.OnSuspending(e); } } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index e632170975..d9a612ea26 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -97,9 +97,9 @@ namespace osu.Game.Overlays.FirstRunSetup private class PinnedMainMenu : MainMenu { - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); Buttons.ReturnToTopOnIdle = false; Buttons.State = ButtonSystemState.TopLevel; From 32722adba90c3b0f3da0a80438693cff8d39c1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Apr 2022 22:59:37 +0200 Subject: [PATCH 0485/1276] Allow mod panels to be clicked in incompatible state --- .../Mods/IncompatibilityDisplayingModPanel.cs | 39 ++----------------- osu.Game/Overlays/Mods/ModPanel.cs | 20 ++++++---- 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs index 38781455fa..aeb983d352 100644 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs @@ -1,15 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Cursor; -using osu.Framework.Input.Events; using osu.Game.Rulesets.Mods; using osu.Game.Utils; @@ -42,41 +39,13 @@ namespace osu.Game.Overlays.Mods && !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod)); } + protected override Colour4 BackgroundColour => incompatible.Value ? (Colour4)ColourProvider.Background6 : base.BackgroundColour; + protected override Colour4 ForegroundColour => incompatible.Value ? (Colour4)ColourProvider.Background5 : base.ForegroundColour; + protected override void UpdateState() { - Action = incompatible.Value ? () => { } : (Action)Active.Toggle; - - if (incompatible.Value) - { - Colour4 backgroundColour = ColourProvider.Background6; - Colour4 textBackgroundColour = ColourProvider.Background5; - - Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(backgroundColour, textBackgroundColour), TRANSITION_DURATION, Easing.OutQuint); - Background.FadeColour(backgroundColour, TRANSITION_DURATION, Easing.OutQuint); - - SwitchContainer.ResizeWidthTo(IDLE_SWITCH_WIDTH, TRANSITION_DURATION, Easing.OutQuint); - SwitchContainer.FadeColour(Colour4.Gray, TRANSITION_DURATION, Easing.OutQuint); - MainContentContainer.TransformTo(nameof(Padding), new MarginPadding - { - Left = IDLE_SWITCH_WIDTH, - Right = CORNER_RADIUS - }, TRANSITION_DURATION, Easing.OutQuint); - - TextBackground.FadeColour(textBackgroundColour, TRANSITION_DURATION, Easing.OutQuint); - TextFlow.FadeColour(Colour4.White.Opacity(0.5f), TRANSITION_DURATION, Easing.OutQuint); - return; - } - - SwitchContainer.FadeColour(Colour4.White, TRANSITION_DURATION, Easing.OutQuint); base.UpdateState(); - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - if (incompatible.Value) - return true; // bypasses base call purposely in order to not play out the intermediate state animation. - - return base.OnMouseDown(e); + SwitchContainer.FadeColour(incompatible.Value ? Colour4.Gray : Colour4.White, TRANSITION_DURATION, Easing.OutQuint); } #region IHasCustomTooltip diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index 7ae325bde7..f2a97da3b2 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -203,20 +203,24 @@ namespace osu.Game.Overlays.Mods base.OnMouseUp(e); } + protected virtual Colour4 BackgroundColour => Active.Value ? activeColour.Darken(0.3f) : (Colour4)ColourProvider.Background3; + protected virtual Colour4 ForegroundColour => Active.Value ? activeColour : (Colour4)ColourProvider.Background2; + protected virtual Colour4 TextColour => Active.Value ? (Colour4)ColourProvider.Background6 : Colour4.White; + protected virtual void UpdateState() { float targetWidth = Active.Value ? EXPANDED_SWITCH_WIDTH : IDLE_SWITCH_WIDTH; double transitionDuration = TRANSITION_DURATION; - Colour4 textBackgroundColour = Active.Value ? activeColour : (Colour4)ColourProvider.Background2; - Colour4 mainBackgroundColour = Active.Value ? activeColour.Darken(0.3f) : (Colour4)ColourProvider.Background3; - Colour4 textColour = Active.Value ? (Colour4)ColourProvider.Background6 : Colour4.White; + Colour4 backgroundColour = BackgroundColour; + Colour4 foregroundColour = ForegroundColour; + Colour4 textColour = TextColour; // Hover affects colour of button background if (IsHovered) { - textBackgroundColour = textBackgroundColour.Lighten(0.1f); - mainBackgroundColour = mainBackgroundColour.Lighten(0.1f); + backgroundColour = backgroundColour.Lighten(0.1f); + foregroundColour = foregroundColour.Lighten(0.1f); } // Mouse down adds a halfway tween of the movement @@ -226,15 +230,15 @@ namespace osu.Game.Overlays.Mods transitionDuration *= 4; } - Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(mainBackgroundColour, textBackgroundColour), transitionDuration, Easing.OutQuint); - Background.FadeColour(mainBackgroundColour, transitionDuration, Easing.OutQuint); + Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(backgroundColour, foregroundColour), transitionDuration, Easing.OutQuint); + Background.FadeColour(backgroundColour, transitionDuration, Easing.OutQuint); SwitchContainer.ResizeWidthTo(targetWidth, transitionDuration, Easing.OutQuint); MainContentContainer.TransformTo(nameof(Padding), new MarginPadding { Left = targetWidth, Right = CORNER_RADIUS }, transitionDuration, Easing.OutQuint); - TextBackground.FadeColour(textBackgroundColour, transitionDuration, Easing.OutQuint); + TextBackground.FadeColour(foregroundColour, transitionDuration, Easing.OutQuint); TextFlow.FadeColour(textColour, transitionDuration, Easing.OutQuint); } From b7c11cdb8e2736df2ec96e187bb2adb549507276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Apr 2022 23:34:43 +0200 Subject: [PATCH 0486/1276] Deselect old incompatible mods if any on user mod select screen --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 27 ++++++++++--------- osu.Game/Overlays/Mods/UserModSelectScreen.cs | 21 +++++++++++++++ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 693c85fafc..8a83071109 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -168,7 +168,7 @@ namespace osu.Game.Overlays.Mods foreach (var column in columnFlow) { - column.SelectedMods.BindValueChanged(_ => updateBindableFromSelection()); + column.SelectedMods.BindValueChanged(updateBindableFromSelection); } customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); @@ -237,33 +237,36 @@ namespace osu.Game.Overlays.Mods TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic); } - private bool selectionBindableSyncInProgress; - private void updateSelectionFromBindable() { - if (selectionBindableSyncInProgress) - return; - - selectionBindableSyncInProgress = true; - + // note that selectionBindableSyncInProgress is purposefully not checked here. + // this is because in the case of mod selection in solo gameplay, a user selection of a mod can actually lead to deselection of other incompatible mods. + // to synchronise state correctly, updateBindableFromSelection() computes the final mods (including incompatibility rules) and updates SelectedMods, + // and this method then runs unconditionally again to make sure the new visual selection accurately reflects the final set of selected mods. + // selectionBindableSyncInProgress ensures that mutual infinite recursion does not happen after that unconditional call. foreach (var column in columnFlow) column.SelectedMods.Value = SelectedMods.Value.Where(mod => mod.Type == column.ModType).ToArray(); - - selectionBindableSyncInProgress = false; } - private void updateBindableFromSelection() + private bool selectionBindableSyncInProgress; + + private void updateBindableFromSelection(ValueChangedEvent> modSelectionChange) { if (selectionBindableSyncInProgress) return; selectionBindableSyncInProgress = true; - SelectedMods.Value = columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray(); + SelectedMods.Value = ComputeNewModsFromSelection( + modSelectionChange.NewValue.Except(modSelectionChange.OldValue), + modSelectionChange.OldValue.Except(modSelectionChange.NewValue)); selectionBindableSyncInProgress = false; } + protected virtual IReadOnlyList ComputeNewModsFromSelection(IEnumerable addedMods, IEnumerable removedMods) + => columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray(); + protected override void PopIn() { const double fade_in_duration = 400; diff --git a/osu.Game/Overlays/Mods/UserModSelectScreen.cs b/osu.Game/Overlays/Mods/UserModSelectScreen.cs index 81943da514..ed0a07521b 100644 --- a/osu.Game/Overlays/Mods/UserModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/UserModSelectScreen.cs @@ -1,8 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using osu.Game.Rulesets.Mods; +using osu.Game.Utils; using osuTK.Input; namespace osu.Game.Overlays.Mods @@ -11,6 +14,24 @@ namespace osu.Game.Overlays.Mods { protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys); + protected override IReadOnlyList ComputeNewModsFromSelection(IEnumerable addedMods, IEnumerable removedMods) + { + IEnumerable modsAfterRemoval = SelectedMods.Value.Except(removedMods).ToList(); + + // the preference is that all new mods should override potential incompatible old mods. + // in general that's a bit difficult to compute if more than one mod is added at a time, + // so be conservative and just remove all mods that aren't compatible with any one added mod. + foreach (var addedMod in addedMods) + { + if (!ModUtils.CheckCompatibleSet(modsAfterRemoval.Append(addedMod), out var invalidMods)) + modsAfterRemoval = modsAfterRemoval.Except(invalidMods); + + modsAfterRemoval = modsAfterRemoval.Append(addedMod).ToList(); + } + + return modsAfterRemoval.ToList(); + } + private class UserModColumn : ModColumn { public UserModColumn(ModType modType, bool allowBulkSelection, [CanBeNull] Key[] toggleKeys = null) From cd898344cb60663f0c300cd524c2d8e16d3d984c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Apr 2022 23:49:40 +0200 Subject: [PATCH 0487/1276] Add test coverage of new incompatibility behaviour --- .../UserInterface/TestSceneModSelectScreen.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 4a738cb29d..514538161e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -89,6 +90,27 @@ namespace osu.Game.Tests.Visual.UserInterface changeRuleset(3); } + [Test] + public void TestIncompatibilityToggling() + { + createScreen(); + changeRuleset(0); + + AddStep("activate DT", () => getPanelForMod(typeof(OsuModDoubleTime)).TriggerClick()); + AddAssert("DT active", () => SelectedMods.Value.Single().GetType() == typeof(OsuModDoubleTime)); + + AddStep("activate NC", () => getPanelForMod(typeof(OsuModNightcore)).TriggerClick()); + AddAssert("only NC active", () => SelectedMods.Value.Single().GetType() == typeof(OsuModNightcore)); + + AddStep("activate HR", () => getPanelForMod(typeof(OsuModHardRock)).TriggerClick()); + AddAssert("NC+HR active", () => SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModNightcore)) + && SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModHardRock))); + + AddStep("activate MR", () => getPanelForMod(typeof(OsuModMirror)).TriggerClick()); + AddAssert("NC+MR active", () => SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModNightcore)) + && SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModMirror))); + } + [Test] public void TestCustomisationToggleState() { @@ -136,5 +158,8 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert($"customisation toggle is {(disabled ? "" : "not ")}disabled", () => getToggle().Active.Disabled == disabled); AddAssert($"customisation toggle is {(active ? "" : "not ")}active", () => getToggle().Active.Value == active); } + + private ModPanel getPanelForMod(Type modType) + => modSelectScreen.ChildrenOfType().Single(panel => panel.Mod.GetType() == modType); } } From e9bd87545e47af17f8226f3850b906a147217883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 22 Apr 2022 00:07:00 +0200 Subject: [PATCH 0488/1276] Fix flaky test in free mod select test scene --- .../Visual/Multiplayer/TestSceneFreeModSelectScreen.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs index 0db05e3a6a..b5f901e51d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -21,8 +21,11 @@ namespace osu.Game.Tests.Visual.Multiplayer { State = { Value = Visibility.Visible } }); + AddUntilStep("all column content loaded", + () => freeModSelectScreen.ChildrenOfType().Any() + && freeModSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); - AddAssert("all visible mods are playable", + AddUntilStep("all visible mods are playable", () => this.ChildrenOfType() .Where(panel => panel.IsPresent) .All(panel => panel.Mod.HasImplementation && panel.Mod.UserPlayable)); From a9ea0ab91b962e10806ea14698064b13e4d617d0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 07:55:39 +0300 Subject: [PATCH 0489/1276] Add support for fetching user guest participations --- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 1 + osu.Game/Online/API/Requests/Responses/APIUser.cs | 3 +++ .../Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs | 3 +++ 3 files changed, 7 insertions(+) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index ffb3a3be9b..205fdc9f2b 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -29,6 +29,7 @@ namespace osu.Game.Online.API.Requests Ranked, Loved, Pending, + Guest, Graveyard } } diff --git a/osu.Game/Online/API/Requests/Responses/APIUser.cs b/osu.Game/Online/API/Requests/Responses/APIUser.cs index a87f0811a1..41f486c709 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUser.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUser.cs @@ -148,6 +148,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"pending_beatmapset_count")] public int PendingBeatmapsetCount; + [JsonProperty(@"guest_beatmapset_count")] + public int GuestBeatmapsetCount; + [JsonProperty(@"scores_best_count")] public int ScoresBestCount; diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 0424fe8bd9..8224cd5eb5 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -53,6 +53,9 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps case BeatmapSetType.Pending: return user.PendingBeatmapsetCount; + case BeatmapSetType.Guest: + return user.GuestBeatmapsetCount; + default: return 0; } From 2c21bd89e74a6f8330f23d2d98b64a4138f510b8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 07:56:01 +0300 Subject: [PATCH 0490/1276] Display beatmap guest participations in user profile --- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index af6ab4aad1..64e5f3e52e 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -22,6 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections new PaginatedBeatmapContainer(BeatmapSetType.Ranked, User, UsersStrings.ShowExtraBeatmapsRankedTitle), new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, UsersStrings.ShowExtraBeatmapsLovedTitle), new PaginatedBeatmapContainer(BeatmapSetType.Pending, User, UsersStrings.ShowExtraBeatmapsPendingTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Guest, User, "Guest Participation Beatmaps"), new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, UsersStrings.ShowExtraBeatmapsGraveyardTitle) }; } From 97b4a2a105c26c4a6b56eabec85b4e0df2c424a3 Mon Sep 17 00:00:00 2001 From: 63411 <62799417+molneya@users.noreply.github.com> Date: Fri, 22 Apr 2022 12:59:00 +0800 Subject: [PATCH 0491/1276] Use better initial value for closestEndTime --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 04d1329eec..f295af5f4d 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills var maniaCurrent = (ManiaDifficultyHitObject)current; double endTime = maniaCurrent.EndTime; int column = maniaCurrent.BaseObject.Column; - double closestEndTime = 100; + double closestEndTime = endTime - maniaCurrent.LastObject.StartTime; // Lowest value we can assume with the current information double holdFactor = 1.0; // Factor to all additional strains in case something else is held double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly From 56bbfa58e5cb08c499e6806a91725a5c186abafc Mon Sep 17 00:00:00 2001 From: 63411 <62799417+molneya@users.noreply.github.com> Date: Fri, 22 Apr 2022 13:01:41 +0800 Subject: [PATCH 0492/1276] Add missing absolute value of closestEndTime --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index f295af5f4d..ba964a4edd 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills var maniaCurrent = (ManiaDifficultyHitObject)current; double endTime = maniaCurrent.EndTime; int column = maniaCurrent.BaseObject.Column; - double closestEndTime = endTime - maniaCurrent.LastObject.StartTime; // Lowest value we can assume with the current information + double closestEndTime = Math.Abs(endTime - maniaCurrent.LastObject.StartTime); // Lowest value we can assume with the current information double holdFactor = 1.0; // Factor to all additional strains in case something else is held double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly From 8b55d3855a7112bab2e41fad4c62fc17916d5fb8 Mon Sep 17 00:00:00 2001 From: 63411 <62799417+molneya@users.noreply.github.com> Date: Fri, 22 Apr 2022 13:27:59 +0800 Subject: [PATCH 0493/1276] Use isOverlapping bool to determine holdAddition --- .../Difficulty/Skills/Strain.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index ba964a4edd..a5b9675836 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -41,13 +41,13 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills double holdFactor = 1.0; // Factor to all additional strains in case something else is held double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly + bool isOverlapping = false; // Fill up the holdEndTimes array for (int i = 0; i < holdEndTimes.Length; ++i) { - // If there is at least one other overlapping end or note, then we get an addition - if (Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1)) - holdAddition = 1.0; + // The current note is overlapped if a previous note or end is overlapping the current note body + isOverlapping |= Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1); // We give a slight bonus to everything if something is held meanwhile if (Precision.DefinitelyBigger(holdEndTimes[i], endTime, 1)) @@ -61,10 +61,10 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills holdEndTimes[column] = endTime; - // The hold addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1 - // Nerfs the hold addition by half if the closest release is 24ms away - if (holdAddition > 0) - holdAddition *= 1 / (1 + Math.Exp(0.5 * (24 - closestEndTime))); + // The hold addition is given if there was an overlap, however it is only valid if there are no other note with a similar ending. + // Releasing multiple notes is just as easy as releasing 1. Nerfs the hold addition by half if the closest release is 24ms away. + if (isOverlapping) + holdAddition = 1 / (1 + Math.Exp(0.5 * (24 - closestEndTime))); // Increase individual strain in own column individualStrains[column] += 2.0 * holdFactor; From 893a95df408fce4c3a0b280d248c9008670b6f18 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 09:17:30 +0300 Subject: [PATCH 0494/1276] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 6b06305bf9..8d79eb94a8 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c57acb3b7f..c6c18f6061 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index d7f89b802b..64af0d70f3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From 40d823bf693da2a1af210b7b0e5f3d3218f7b717 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 09:18:42 +0300 Subject: [PATCH 0495/1276] Use localised string for guest participation beatmaps header --- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 64e5f3e52e..4db00df1b5 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections new PaginatedBeatmapContainer(BeatmapSetType.Ranked, User, UsersStrings.ShowExtraBeatmapsRankedTitle), new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, UsersStrings.ShowExtraBeatmapsLovedTitle), new PaginatedBeatmapContainer(BeatmapSetType.Pending, User, UsersStrings.ShowExtraBeatmapsPendingTitle), - new PaginatedBeatmapContainer(BeatmapSetType.Guest, User, "Guest Participation Beatmaps"), + new PaginatedBeatmapContainer(BeatmapSetType.Guest, User, UsersStrings.ShowExtraBeatmapsGuestTitle), new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, UsersStrings.ShowExtraBeatmapsGraveyardTitle) }; } From 82a1d1cc2eab41622c5b69a2867a0a25521e97f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 15:22:58 +0900 Subject: [PATCH 0496/1276] Fix multiplier display not blocking input --- osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 66fd6a202d..1d848fe456 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Mods Height = HEIGHT; AutoSizeAxes = Axes.X; - InternalChild = new Container + InternalChild = new InputBlockingContainer { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, From 33d516eecb0b11541c733de1797b9706c8c69763 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 09:28:27 +0300 Subject: [PATCH 0497/1276] Move guest participation beatmap up to below loved --- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 4db00df1b5..6b93c24a78 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -21,8 +21,8 @@ namespace osu.Game.Overlays.Profile.Sections new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, UsersStrings.ShowExtraBeatmapsFavouriteTitle), new PaginatedBeatmapContainer(BeatmapSetType.Ranked, User, UsersStrings.ShowExtraBeatmapsRankedTitle), new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, UsersStrings.ShowExtraBeatmapsLovedTitle), - new PaginatedBeatmapContainer(BeatmapSetType.Pending, User, UsersStrings.ShowExtraBeatmapsPendingTitle), new PaginatedBeatmapContainer(BeatmapSetType.Guest, User, UsersStrings.ShowExtraBeatmapsGuestTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Pending, User, UsersStrings.ShowExtraBeatmapsPendingTitle), new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, UsersStrings.ShowExtraBeatmapsGraveyardTitle) }; } From 06594ca0c8aa9635f3403db0575f322ad0c34d59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 15:30:12 +0900 Subject: [PATCH 0498/1276] Refine assertion to ensure correct section is current, rather than any section --- osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 8d91b55877..cdeaafd828 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Settings .All(f => f.FilterTerms.Any(t => t.Contains("scaling"))) )); - AddAssert("ensure section is current", () => settings.CurrentSection.Value != null); + AddAssert("ensure section is current", () => settings.CurrentSection.Value is GraphicsSection); } [Test] From f282e5b0133785d8c9e6474cbe04bf5c79dc4811 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 15:32:23 +0900 Subject: [PATCH 0499/1276] Remove one more unnecessary schedule --- osu.Game/Graphics/Containers/SectionsContainer.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 540ca85809..823ba33216 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -195,11 +195,8 @@ namespace osu.Game.Graphics.Containers protected void InvalidateScrollPosition() { - Schedule(() => - { - lastKnownScroll = null; - lastClickedSection = null; - }); + lastKnownScroll = null; + lastClickedSection = null; } protected override void UpdateAfterChildren() From c44f7d9f93c83c74eb25b30c73247506cc474766 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 15:56:03 +0900 Subject: [PATCH 0500/1276] Fix high star ratings not being easily visible on tooltips Resolves issue mentioned in https://github.com/ppy/osu/discussions/17920. --- .../Drawables/DifficultyIconTooltip.cs | 59 +++++-------------- 1 file changed, 15 insertions(+), 44 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs index aba01a1294..5479644772 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; @@ -16,11 +15,11 @@ namespace osu.Game.Beatmaps.Drawables { internal class DifficultyIconTooltip : VisibilityContainer, ITooltip { - private readonly OsuSpriteText difficultyName, starRating; - private readonly Box background; - private readonly FillFlowContainer difficultyFlow; + private OsuSpriteText difficultyName; + private StarRatingDisplay starRating; - public DifficultyIconTooltip() + [BackgroundDependencyLoader] + private void load(OsuColour colours) { AutoSizeAxes = Axes.Both; Masking = true; @@ -28,9 +27,10 @@ namespace osu.Game.Beatmaps.Drawables Children = new Drawable[] { - background = new Box + new Box { Alpha = 0.9f, + Colour = colours.Gray3, RelativeSizeAxes = Axes.Both }, new FillFlowContainer @@ -40,6 +40,7 @@ namespace osu.Game.Beatmaps.Drawables AutoSizeEasing = Easing.OutQuint, Direction = FillDirection.Vertical, Padding = new MarginPadding(10), + Spacing = new Vector2(5), Children = new Drawable[] { difficultyName = new OsuSpriteText @@ -48,57 +49,27 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), }, - difficultyFlow = new FillFlowContainer + starRating = new StarRatingDisplay(default, StarRatingDisplaySize.Small) { - AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - starRating = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), - }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Left = 4 }, - Icon = FontAwesome.Solid.Star, - Size = new Vector2(12), - }, - } } } } }; } - [Resolved] - private OsuColour colours { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - background.Colour = colours.Gray3; - } - - private readonly IBindable starDifficulty = new Bindable(); + private DifficultyIconTooltipContent displayedContent; public void SetContent(DifficultyIconTooltipContent content) { - difficultyName.Text = content.BeatmapInfo.DifficultyName; + if (displayedContent != null) + starRating.Current.UnbindFrom(displayedContent.Difficulty); - starDifficulty.UnbindAll(); - starDifficulty.BindTo(content.Difficulty); - starDifficulty.BindValueChanged(difficulty => - { - starRating.Text = $"{difficulty.NewValue.Stars:0.##}"; - difficultyFlow.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars); - }, true); + displayedContent = content; + + starRating.Current.BindTarget = displayedContent.Difficulty; + difficultyName.Text = displayedContent.BeatmapInfo.DifficultyName; } public void Move(Vector2 pos) => Position = pos; From 14e17c8b7b4892c8485ff907a6790d1d5b041708 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 16:22:12 +0900 Subject: [PATCH 0501/1276] Update `TestIncompatibilityDisplay` in line with new functionality --- .../Visual/UserInterface/TestSceneModPanel.cs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs index 95323e5dfa..f56d9c8a91 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs @@ -47,12 +47,22 @@ namespace osu.Game.Tests.Visual.UserInterface { IncompatibilityDisplayingModPanel panel = null; - AddStep("create panel with DT", () => Child = panel = new IncompatibilityDisplayingModPanel(new OsuModDoubleTime()) + AddStep("create panel with DT", () => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.None, - Width = 300 + Child = panel = new IncompatibilityDisplayingModPanel(new OsuModDoubleTime()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.None, + Width = 300, + }; + + panel.Active.BindValueChanged(active => + { + SelectedMods.Value = active.NewValue + ? Array.Empty() + : new[] { panel.Mod }; + }); }); clickPanel(); @@ -63,11 +73,6 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("set incompatible mod", () => SelectedMods.Value = new[] { new OsuModHalfTime() }); - clickPanel(); - AddAssert("panel not active", () => !panel.Active.Value); - - AddStep("reset mods", () => SelectedMods.Value = Array.Empty()); - clickPanel(); AddAssert("panel active", () => panel.Active.Value); From 7258a097489655632d89def6cdbac09cd54f3d3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 18:04:07 +0900 Subject: [PATCH 0502/1276] Allow `SettingsSection`s to exit without a parent `SettingsPanel` --- osu.Game/Overlays/Settings/SettingsSection.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 28c42a0e47..dc3af9104f 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -44,7 +45,7 @@ namespace osu.Game.Overlays.Settings public bool FilteringActive { get; set; } - [Resolved] + [Resolved(canBeNull: true)] private SettingsPanel settingsPanel { get; set; } protected SettingsSection() @@ -117,7 +118,7 @@ namespace osu.Game.Overlays.Settings }, }); - selectedSection = settingsPanel.CurrentSection.GetBoundCopy(); + selectedSection = settingsPanel?.CurrentSection.GetBoundCopy() ?? new Bindable(this); selectedSection.BindValueChanged(_ => updateContentFade(), true); } @@ -138,7 +139,10 @@ namespace osu.Game.Overlays.Settings protected override bool OnClick(ClickEvent e) { if (!isCurrentSection) + { + Debug.Assert(settingsPanel != null); settingsPanel.SectionsContainer.ScrollTo(this); + } return base.OnClick(e); } From 2a043aa6de7ae64990a6069e7cb0402d4e8f06fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 18:04:43 +0900 Subject: [PATCH 0503/1276] Add a method of finding and applying settings with classic default value --- osu.Game/Overlays/Settings/ISettingsItem.cs | 6 ++++ osu.Game/Overlays/Settings/SettingsItem.cs | 36 ++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/ISettingsItem.cs b/osu.Game/Overlays/Settings/ISettingsItem.cs index e7afa48502..e6b1e68741 100644 --- a/osu.Game/Overlays/Settings/ISettingsItem.cs +++ b/osu.Game/Overlays/Settings/ISettingsItem.cs @@ -9,5 +9,11 @@ namespace osu.Game.Overlays.Settings public interface ISettingsItem : IDrawable, IDisposable { event Action SettingChanged; + + /// + /// Apply the default values of a setting item, if the setting item specifies a "classic" default via . + /// + /// Whether to apply the classic value. If false, the standard default is applied. + void ApplyClassicDefault(bool useClassicDefault); } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 1c5668479f..845a7f5cdf 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -30,6 +30,8 @@ namespace osu.Game.Overlays.Settings /// public object SettingSourceObject { get; internal set; } + public const string CLASSIC_DEFAULT_SEARCH_TERM = @"has-classic-default"; + private IHasCurrentValue controlWithCurrent => Control as IHasCurrentValue; protected override Container Content => FlowContent; @@ -96,7 +98,23 @@ namespace osu.Game.Overlays.Settings set => controlWithCurrent.Current = value; } - public virtual IEnumerable FilterTerms => Keywords == null ? new[] { LabelText.ToString() } : new List(Keywords) { LabelText.ToString() }.ToArray(); + public virtual IEnumerable FilterTerms + { + get + { + var keywords = new List(Keywords ?? Array.Empty()) + { + LabelText.ToString() + }; + + if (GetClassicDefault != null) + { + keywords.Add(CLASSIC_DEFAULT_SEARCH_TERM); + } + + return keywords; + } + } public IEnumerable Keywords { get; set; } @@ -108,6 +126,22 @@ namespace osu.Game.Overlays.Settings public event Action SettingChanged; + /// + /// An action which when invoked will apply a classic default value to this setting. + /// + public Func GetClassicDefault { get; set; } + + public void ApplyClassicDefault(bool useClassicDefault) + { + if (GetClassicDefault != null) + { + if (useClassicDefault) + Current.Value = GetClassicDefault(); + else + Current.SetDefault(); + } + } + protected SettingsItem() { RelativeSizeAxes = Axes.X; From e0b9ab022de2f610b294a53c065ae07d4e81f999 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 18:05:24 +0900 Subject: [PATCH 0504/1276] Add classic default values against setting controls --- osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs | 1 + osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 1 + .../Overlays/Settings/Sections/UserInterface/GeneralSettings.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index a4c0381d16..33db74c530 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Osu.UI }, new SettingsCheckbox { + GetClassicDefault = () => false, LabelText = "Snaking out sliders", Current = config.GetBindable(OsuRulesetSetting.SnakingOutSliders) }, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index d4e4fd571d..3ca677ef7b 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -21,6 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { new SettingsEnumDropdown { + GetClassicDefault = () => ScoringMode.Classic, LabelText = GameplaySettingsStrings.ScoreDisplayMode, Current = config.GetBindable(OsuSetting.ScoreDisplayMode), Keywords = new[] { "scoring" } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 6e1558f7d7..06f2c9794c 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -37,6 +37,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface }, new SettingsSlider { + GetClassicDefault = () => 0, LabelText = UserInterfaceStrings.HoldToConfirmActivationTime, Current = config.GetBindable(OsuSetting.UIHoldActivationDelay), Keywords = new[] { @"delay" }, From 1caee3876802ca5ac8aa81c9bb932f8a558192ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 18:05:52 +0900 Subject: [PATCH 0505/1276] Add first-run "behaviour" screen to allow users a choice of more familiar UX --- .../TestSceneFirstRunScreenBehaviour.cs | 19 ++++ .../FirstRunSetupOverlayStrings.cs | 28 ++++- .../Overlays/FirstRunSetup/ScreenBehaviour.cs | 104 ++++++++++++++++++ osu.Game/Overlays/FirstRunSetupOverlay.cs | 1 + 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs create mode 100644 osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs new file mode 100644 index 0000000000..d7f0c5ba03 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Screens; +using osu.Game.Overlays.FirstRunSetup; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneFirstRunScreenBehaviour : OsuManualInputManagerTestScene + { + public TestSceneFirstRunScreenBehaviour() + { + AddStep("load screen", () => + { + Child = new ScreenStack(new ScreenBehaviour()); + }); + } + } +} diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs index 001de93c16..15b8d0ba67 100644 --- a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -17,7 +17,8 @@ namespace osu.Game.Localisation /// /// "Click to resume first-run setup at any point" /// - public static LocalisableString ClickToResumeFirstRunSetupAtAnyPoint => new TranslatableString(getKey(@"click_to_resume_first_run_setup_at_any_point"), @"Click to resume first-run setup at any point"); + public static LocalisableString ClickToResumeFirstRunSetupAtAnyPoint => + new TranslatableString(getKey(@"click_to_resume_first_run_setup_at_any_point"), @"Click to resume first-run setup at any point"); /// /// "First-run setup" @@ -53,6 +54,31 @@ osu! is a very configurable game, and diving straight into the settings can some /// public static LocalisableString Next(LocalisableString nextStepDescription) => new TranslatableString(getKey(@"next"), @"Next ({0})", nextStepDescription); + /// + /// "Behaviour" + /// + public static LocalisableString Behaviour => new TranslatableString(getKey(@"behaviour"), @"Behaviour"); + + /// + /// "Some new defaults for game behaviours have been implemented, with the aim of improving the game experience and making it more accessible to everyone. + /// + /// We recommend you give the new defaults a try, but if you'd like to have things feel more like classic versions of osu!, you can easily apply some sane defaults below." + /// + public static LocalisableString BehaviourDescription => new TranslatableString(getKey(@"behaviour_description"), + @"Some new defaults for game behaviours have been implemented, with the aim of improving the game experience and making it more accessible to everyone. + +We recommend you give the new defaults a try, but if you'd like to have things feel more like classic versions of osu!, you can easily apply some sane defaults below."); + + /// + /// "New defaults" + /// + public static LocalisableString NewDefaults => new TranslatableString(getKey(@"new_defaults"), @"New defaults"); + + /// + /// "Classic defaults" + /// + public static LocalisableString ClassicDefaults => new TranslatableString(getKey(@"classic_defaults"), @"Classic defaults"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs new file mode 100644 index 0000000000..61177c821b --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs @@ -0,0 +1,104 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; +using osu.Game.Overlays.Settings; +using osu.Game.Overlays.Settings.Sections; + +namespace osu.Game.Overlays.FirstRunSetup +{ + public class ScreenBehaviour : FirstRunSetupScreen + { + private SearchContainer searchContainer; + + [BackgroundDependencyLoader] + private void load() + { + Content.Children = new Drawable[] + { + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24)) + { + Text = FirstRunSetupOverlayStrings.BehaviourDescription, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 10), + new Dimension(), + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new[] + { + new TriangleButton + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + Text = FirstRunSetupOverlayStrings.NewDefaults, + RelativeSizeAxes = Axes.X, + Action = applyStandard, + }, + Empty(), + new DangerousTriangleButton + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Text = FirstRunSetupOverlayStrings.ClassicDefaults, + RelativeSizeAxes = Axes.X, + Action = applyClassic + } + }, + }, + }, + searchContainer = new SearchContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new SettingsSection[] + { + new GeneralSection(), + new SkinSection(), + new UserInterfaceSection(), + new GameplaySection(), + new RulesetSection(), + new AudioSection(), + new GraphicsSection(), + new OnlineSection(), + new MaintenanceSection(), + new DebugSection(), + }, + SearchTerm = SettingsItem.CLASSIC_DEFAULT_SEARCH_TERM, + } + }; + } + + private void applyClassic() + { + foreach (var i in searchContainer.ChildrenOfType()) + i.ApplyClassicDefault(true); + } + + private void applyStandard() + { + foreach (var i in searchContainer.ChildrenOfType()) + i.ApplyClassicDefault(false); + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index a12fec4507..26b1eb2d45 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -70,6 +70,7 @@ namespace osu.Game.Overlays { new FirstRunStep(typeof(ScreenWelcome), FirstRunSetupOverlayStrings.WelcomeTitle), new FirstRunStep(typeof(ScreenUIScale), GraphicsSettingsStrings.UIScaling), + new FirstRunStep(typeof(ScreenBehaviour), FirstRunSetupOverlayStrings.Behaviour), }; private Container stackContainer = null!; From bcdd1fb183b0b612290f23c2129c3948d2210262 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 18:37:40 +0900 Subject: [PATCH 0506/1276] Fix nested song select in first-run dialog fiddling with global audio --- osu.Game/Screens/Select/SongSelect.cs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 928978cd08..2a1ed2a7a8 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -50,6 +50,12 @@ namespace osu.Game.Screens.Select public FilterControl FilterControl { get; private set; } + /// + /// Whether this song select instance should take control of the global track, + /// applying looping and preview offsets. + /// + protected virtual bool ControlGlobalMusic => true; + protected virtual bool ShowFooter => true; protected virtual bool DisplayStableImportPrompt => legacyImportManager?.SupportsImportFromStable == true; @@ -604,15 +610,18 @@ namespace osu.Game.Screens.Select BeatmapDetails.Refresh(); beginLooping(); - music.ResetTrackAdjustments(); if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending) { updateComponentFromBeatmap(Beatmap.Value); - // restart playback on returning to song select, regardless. - // not sure this should be a permanent thing (we may want to leave a user pause paused even on returning) - music.Play(requestedByUser: true); + if (ControlGlobalMusic) + { + // restart playback on returning to song select, regardless. + // not sure this should be a permanent thing (we may want to leave a user pause paused even on returning) + music.ResetTrackAdjustments(); + music.Play(requestedByUser: true); + } } this.FadeIn(250); @@ -663,6 +672,9 @@ namespace osu.Game.Screens.Select private void beginLooping() { + if (!ControlGlobalMusic) + return; + Debug.Assert(!isHandlingLooping); isHandlingLooping = true; @@ -733,6 +745,9 @@ namespace osu.Game.Screens.Select /// private void ensurePlayingSelected() { + if (!ControlGlobalMusic) + return; + ITrack track = music.CurrentTrack; bool isNewTrack = !lastTrack.TryGetTarget(out var last) || last != track; From 5f3ff51f7e1f1c416b2be768b7433aabf7fbb5be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 21:24:58 +0900 Subject: [PATCH 0507/1276] Revert "Remove one more unnecessary schedule" This reverts commit f282e5b0133785d8c9e6474cbe04bf5c79dc4811. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 823ba33216..540ca85809 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -195,8 +195,11 @@ namespace osu.Game.Graphics.Containers protected void InvalidateScrollPosition() { - lastKnownScroll = null; - lastClickedSection = null; + Schedule(() => + { + lastKnownScroll = null; + lastClickedSection = null; + }); } protected override void UpdateAfterChildren() From 0dc3d1041fe22d4c0bbc245327b6a2cd46f49e36 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 19:23:54 +0300 Subject: [PATCH 0508/1276] Revert "Revert "Remove one more unnecessary schedule"" This reverts commit 5f3ff51f7e1f1c416b2be768b7433aabf7fbb5be. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 540ca85809..823ba33216 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -195,11 +195,8 @@ namespace osu.Game.Graphics.Containers protected void InvalidateScrollPosition() { - Schedule(() => - { - lastKnownScroll = null; - lastClickedSection = null; - }); + lastKnownScroll = null; + lastClickedSection = null; } protected override void UpdateAfterChildren() From 2961c383f68891bdc954ecab928fda81cad765ba Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 20:04:04 +0300 Subject: [PATCH 0509/1276] Add failing test case --- .../Visual/Settings/TestSceneSettingsPanel.cs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index cdeaafd828..377873f64a 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -33,18 +33,21 @@ namespace osu.Game.Tests.Visual.Settings State = { Value = Visibility.Visible } }); }); + + AddStep("reset mouse", () => InputManager.MoveMouseTo(settings)); } [Test] - public void TestQuickFiltering() + public void TestFiltering([Values] bool beforeLoad) { - AddStep("set filter", () => - { - settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"; - }); + if (beforeLoad) + AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); AddUntilStep("wait for items to load", () => settings.SectionsContainer.ChildrenOfType().Any()); + if (!beforeLoad) + AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); + AddAssert("ensure all items match filter", () => settings.SectionsContainer .ChildrenOfType().Where(f => f.IsPresent) .All(section => @@ -56,6 +59,15 @@ namespace osu.Game.Tests.Visual.Settings )); AddAssert("ensure section is current", () => settings.CurrentSection.Value is GraphicsSection); + AddAssert("ensure section is placed first", () => settings.CurrentSection.Value.Y == 0); + } + + [Test] + public void TestFilterAfterLoad() + { + AddUntilStep("wait for items to load", () => settings.SectionsContainer.ChildrenOfType().Any()); + + AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); } [Test] From 4c7c611218fbf0b4e73aca928f2e1b2081f9d370 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 20:34:00 +0300 Subject: [PATCH 0510/1276] Fix settings overlay not invalidating presence on filter change --- osu.Game/Overlays/Settings/SettingsItem.cs | 18 +++++++++++++++-- osu.Game/Overlays/Settings/SettingsSection.cs | 20 ++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 1c5668479f..7795b7b4c5 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -100,9 +100,23 @@ namespace osu.Game.Overlays.Settings public IEnumerable Keywords { get; set; } - public override bool IsPresent => base.IsPresent && MatchingFilter; + private bool matchingFilter; - public bool MatchingFilter { get; set; } = true; + public bool MatchingFilter + { + get => matchingFilter; + set + { + bool wasPresent = IsPresent; + + matchingFilter = value; + + if (IsPresent != wasPresent) + Invalidate(Invalidation.Presence); + } + } + + public override bool IsPresent => base.IsPresent && MatchingFilter; public bool FilteringActive { get; set; } diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 28c42a0e47..694823314e 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -21,8 +21,6 @@ namespace osu.Game.Overlays.Settings protected FillFlowContainer FlowContent; protected override Container Content => FlowContent; - public override bool IsPresent => base.IsPresent && MatchingFilter; - private IBindable selectedSection; private Box dim; @@ -40,7 +38,23 @@ namespace osu.Game.Overlays.Settings private const int header_size = 24; private const int border_size = 4; - public bool MatchingFilter { get; set; } = true; + private bool matchingFilter; + + public bool MatchingFilter + { + get => matchingFilter; + set + { + bool wasPresent = IsPresent; + + matchingFilter = value; + + if (IsPresent != wasPresent) + Invalidate(Invalidation.Presence); + } + } + + public override bool IsPresent => base.IsPresent && MatchingFilter; public bool FilteringActive { get; set; } From 7be25963121626e9f024787d1c24f13409e7952b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 23:50:58 +0300 Subject: [PATCH 0511/1276] Fix `MatchingFilter` being false by default --- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- osu.Game/Overlays/Settings/SettingsSection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 7795b7b4c5..f7824d79e7 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -100,7 +100,7 @@ namespace osu.Game.Overlays.Settings public IEnumerable Keywords { get; set; } - private bool matchingFilter; + private bool matchingFilter = true; public bool MatchingFilter { diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 694823314e..b5f3d8e003 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Settings private const int header_size = 24; private const int border_size = 4; - private bool matchingFilter; + private bool matchingFilter = true; public bool MatchingFilter { From 56358ef19ed9ebe69a74b25eff50e537b5e63fe2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 00:35:45 +0300 Subject: [PATCH 0512/1276] Fix legacy skin hit animation lookup falling back to `LookupName` --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index f7d5581621..aed6026d69 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -390,7 +390,7 @@ namespace osu.Game.Skinning return new LegacyJudgementPieceOld(resultComponent.Component, createDrawable); } - break; + return null; } return this.GetAnimation(component.LookupName, false, false); From 69e2e309717382f6ae63d5b24205a249f089b562 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 01:06:35 +0300 Subject: [PATCH 0513/1276] Remove legacy skin component fallback lookup Has always been a source of hiding actual issues like in https://github.com/ppy/osu/issues/17745 (`sliderfollowcircle` looked up from fallback because there's no transformer), and https://github.com/ppy/osu/issues/17934 (because there's a texture `ok` and that matches the component name `HitResult.Ok`). --- osu.Game/Skinning/LegacySkin.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index aed6026d69..1b3b0eea18 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -391,9 +391,10 @@ namespace osu.Game.Skinning } return null; - } - return this.GetAnimation(component.LookupName, false, false); + default: + throw new ArgumentOutOfRangeException(nameof(component)); + } } private Texture? getParticleTexture(HitResult result) From 61078910a64de149e999f970146837b3334d8bfa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 01:08:53 +0300 Subject: [PATCH 0514/1276] Handle all skin component types explicitly --- .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 3 +++ .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 5 +++-- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 6 ++++++ .../Skinning/Legacy/TaikoLegacySkinTransformer.cs | 7 +++++++ osu.Game/Skinning/DefaultSkin.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 5 ++++- osu.Game/Skinning/SkinnableSprite.cs | 2 +- .../Skinning/UnsupportedSkinComponentException.cs | 15 +++++++++++++++ 8 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Skinning/UnsupportedSkinComponentException.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index b2a555f89d..04b522b404 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -90,6 +90,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy return new LegacyHitExplosion(); return null; + + default: + throw new UnsupportedSkinComponentException(component); } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 431bd77402..315b4444c2 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -116,9 +116,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy case ManiaSkinComponents.StageForeground: return new LegacyStageForeground(); - } - break; + default: + throw new UnsupportedSkinComponentException(component); + } } return base.GetDrawableComponent(component); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 900ad6f6d3..572185e6e1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -35,6 +35,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case OsuSkinComponents.FollowPoint: return this.GetAnimation(component.LookupName, true, true, true, startAtCurrentTime: false); + case OsuSkinComponents.SliderScorePoint: + return this.GetAnimation(component.LookupName, false, false); + case OsuSkinComponents.SliderFollowCircle: var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true); if (followCircle != null) @@ -123,6 +126,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case OsuSkinComponents.ApproachCircle: return new LegacyApproachCircle(); + + default: + throw new UnsupportedSkinComponentException(component); } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs index bbc8f0abea..af5921b0fb 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs @@ -57,6 +57,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy case TaikoSkinComponents.DrumRollTick: return this.GetAnimation("sliderscorepoint", false, false); + case TaikoSkinComponents.Swell: + // todo: support taiko legacy swell (https://github.com/ppy/osu/issues/13601). + return null; + case TaikoSkinComponents.HitTarget: if (GetTexture("taikobigcircle") != null) return new TaikoLegacyHitTarget(); @@ -119,6 +123,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy case TaikoSkinComponents.Mascot: return new DrawableTaikoMascot(); + + default: + throw new UnsupportedSkinComponentException(component); } } diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 119b0ec9ad..fb24084659 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -155,7 +155,7 @@ namespace osu.Game.Skinning return skinnableTargetWrapper; } - break; + return null; } switch (component.LookupName) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 1b3b0eea18..b65ba8b04c 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -392,8 +392,11 @@ namespace osu.Game.Skinning return null; + case SkinnableSprite.SpriteComponent sprite: + return this.GetAnimation(sprite.LookupName, false, false); + default: - throw new ArgumentOutOfRangeException(nameof(component)); + throw new UnsupportedSkinComponentException(component); } } diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index 4b4d7fe2c6..af9ff0f1d7 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -65,7 +65,7 @@ namespace osu.Game.Skinning public bool UsesFixedAnchor { get; set; } - private class SpriteComponent : ISkinComponent + public class SpriteComponent : ISkinComponent { public string LookupName { get; set; } diff --git a/osu.Game/Skinning/UnsupportedSkinComponentException.cs b/osu.Game/Skinning/UnsupportedSkinComponentException.cs new file mode 100644 index 0000000000..643f29c3db --- /dev/null +++ b/osu.Game/Skinning/UnsupportedSkinComponentException.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Skinning +{ + public class UnsupportedSkinComponentException : Exception + { + public UnsupportedSkinComponentException(ISkinComponent component) + : base($@"Unsupported component type: {component.GetType()}(""{component.LookupName}"").") + { + } + } +} From 89519086c89fd8897e65816677f1ca088e743907 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 03:16:58 +0300 Subject: [PATCH 0515/1276] Fix `SkinnableTestScene` unnecessarily adding a base raw skin layer --- osu.Game/Tests/Visual/SkinnableTestScene.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 2e1ca09fe4..296ed80e37 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -74,11 +74,15 @@ namespace osu.Game.Tests.Visual createdDrawables.Add(created); - SkinProvidingContainer mainProvider; Container childContainer; OutlineBox outlineBox; SkinProvidingContainer skinProvider; + ISkin provider = skin; + + if (provider is LegacySkin legacyProvider) + provider = Ruleset.Value.CreateInstance().CreateLegacySkinProvider(legacyProvider, beatmap); + var children = new Container { RelativeSizeAxes = Axes.Both, @@ -107,12 +111,10 @@ namespace osu.Game.Tests.Visual Children = new Drawable[] { outlineBox = new OutlineBox(), - (mainProvider = new SkinProvidingContainer(skin)).WithChild( - skinProvider = new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider, beatmap)) - { - Child = created, - } - ) + skinProvider = new SkinProvidingContainer(provider) + { + Child = created, + } } }, } @@ -130,7 +132,7 @@ namespace osu.Game.Tests.Visual { bool autoSize = created.RelativeSizeAxes == Axes.None; - foreach (var c in new[] { mainProvider, childContainer, skinProvider }) + foreach (var c in new[] { childContainer, skinProvider }) { c.RelativeSizeAxes = Axes.None; c.AutoSizeAxes = Axes.None; From 610d61dce9dcc1be01acf3dce37ea254b6125942 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 03:17:18 +0300 Subject: [PATCH 0516/1276] Fix `TestSceneSliderApplication` not applying legacy skin transformer --- osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index d673b7a6ac..a40ae611d8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -70,7 +70,9 @@ namespace osu.Game.Rulesets.Osu.Tests var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo()); tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1"; - Child = new SkinProvidingContainer(tintingSkin) + var provider = Ruleset.Value.CreateInstance().CreateLegacySkinProvider(tintingSkin, Beatmap.Value.Beatmap); + + Child = new SkinProvidingContainer(provider) { RelativeSizeAxes = Axes.Both, Child = dho = new DrawableSlider(prepareObject(new Slider From 6cca56ab77d0317a2dba6cfc114f9c3bd105d248 Mon Sep 17 00:00:00 2001 From: 63411 <62799417+molneya@users.noreply.github.com> Date: Sat, 23 Apr 2022 10:46:12 +0800 Subject: [PATCH 0517/1276] Move release_threshold to const --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index a5b9675836..31550a8105 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills { private const double individual_decay_base = 0.125; private const double overall_decay_base = 0.30; + private const double release_threshold = 24; protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 1; @@ -62,9 +63,17 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills holdEndTimes[column] = endTime; // The hold addition is given if there was an overlap, however it is only valid if there are no other note with a similar ending. - // Releasing multiple notes is just as easy as releasing 1. Nerfs the hold addition by half if the closest release is 24ms away. + // Releasing multiple notes is just as easy as releasing 1. Nerfs the hold addition by half if the closest release is release_threshold away. + // holdAddition + // ^ + // 1.0 + - - - - - -+----------- + // | / + // 0.5 + - - - - -/ Sigmoid Curve + // | /| + // 0.0 +--------+-+---------------> Release Difference / ms + // release_threshold if (isOverlapping) - holdAddition = 1 / (1 + Math.Exp(0.5 * (24 - closestEndTime))); + holdAddition = 1 / (1 + Math.Exp(0.5 * (release_threshold - closestEndTime))); // Increase individual strain in own column individualStrains[column] += 2.0 * holdFactor; From 27f3499330536b0ab2e86e600478a751fcb1e9d5 Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 23 Apr 2022 00:17:00 -0300 Subject: [PATCH 0518/1276] Add joystick/gamepad deadzone setting Also splits joystick/gamepad into a new sub-section. --- .../Localisation/JoystickSettingsStrings.cs | 21 +++++++ .../Sections/Input/JoystickSettings.cs | 62 +++++++++++++++++++ .../Settings/Sections/InputSection.cs | 5 +- 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Localisation/JoystickSettingsStrings.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs diff --git a/osu.Game/Localisation/JoystickSettingsStrings.cs b/osu.Game/Localisation/JoystickSettingsStrings.cs new file mode 100644 index 0000000000..6ae8bb66dc --- /dev/null +++ b/osu.Game/Localisation/JoystickSettingsStrings.cs @@ -0,0 +1,21 @@ +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class JoystickSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.JoystickSettings"; + + /// + /// "Joystick / Gamepad" + /// + public static LocalisableString JoystickGamepad => new TranslatableString(getKey(@"joystick_gamepad"), @"Joystick / Gamepad"); + + /// + /// "Deadzone Threshold" + /// + public static LocalisableString DeadzoneThreshold => new TranslatableString(getKey(@"deadzone_threshold"), @"Deadzone Threshold"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs new file mode 100644 index 0000000000..fd1dbc876a --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -0,0 +1,62 @@ +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Input.Handlers.Joystick; +using osu.Framework.Localisation; +using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; + +namespace osu.Game.Overlays.Settings.Sections.Input +{ + public class JoystickSettings : SettingsSubsection + { + private readonly JoystickHandler joystickHandler; + + protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; + private readonly BindableNumber deadzoneThreshold = new BindableNumber(); + private readonly Bindable enabled = new BindableBool(true); + public JoystickSettings(JoystickHandler joystickHandler) + { + this.joystickHandler = joystickHandler; + } + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = CommonStrings.Enabled, + Current = enabled + }, + new DeadzoneSetting + { + LabelText = JoystickSettingsStrings.DeadzoneThreshold, + Current = deadzoneThreshold + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + enabled.BindTo(joystickHandler.Enabled); + deadzoneThreshold.BindTo(joystickHandler.DeadzoneThreshold); + enabled.BindValueChanged(e => deadzoneThreshold.Disabled = !e.NewValue, true); + } + + private class DeadzoneSetting : SettingsSlider + { + public DeadzoneSetting() + { + KeyboardStep = 0.005f; + TransferValueOnCommit = true; + } + } + + private class DeadzoneSlider : OsuSliderBar + { + public override LocalisableString TooltipText => Current.Disabled ? "" : base.TooltipText; + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index d282ba5318..d2c5d2fcf7 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -68,7 +68,10 @@ namespace osu.Game.Overlays.Settings.Sections break; // whitelist the handlers which should be displayed to avoid any weird cases of users touching settings they shouldn't. - case JoystickHandler _: + case JoystickHandler jh: + section = new JoystickSettings(jh); + break; + case MidiHandler _: section = new HandlerSection(handler); break; From 99f276a570950a93f5fdf9022aad10e356da7ae7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Apr 2022 13:30:23 +0900 Subject: [PATCH 0519/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8d79eb94a8..82dec74855 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c6c18f6061..325e834fa5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 64af0d70f3..8775442be2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 23ef1e194db1ef1fdaefe0664622547c59c3caed Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 23 Apr 2022 01:52:59 -0300 Subject: [PATCH 0520/1276] Code Quality Whoops. --- .../Localisation/JoystickSettingsStrings.cs | 3 + .../Sections/Input/JoystickSettings.cs | 85 ++++++++++--------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/osu.Game/Localisation/JoystickSettingsStrings.cs b/osu.Game/Localisation/JoystickSettingsStrings.cs index 6ae8bb66dc..ee2096ef89 100644 --- a/osu.Game/Localisation/JoystickSettingsStrings.cs +++ b/osu.Game/Localisation/JoystickSettingsStrings.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using osu.Framework.Localisation; namespace osu.Game.Localisation diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs index fd1dbc876a..aa6d602b6d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -11,52 +14,50 @@ namespace osu.Game.Overlays.Settings.Sections.Input public class JoystickSettings : SettingsSubsection { private readonly JoystickHandler joystickHandler; - - protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; - private readonly BindableNumber deadzoneThreshold = new BindableNumber(); - private readonly Bindable enabled = new BindableBool(true); - public JoystickSettings(JoystickHandler joystickHandler) + protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; + private readonly BindableNumber deadzoneThreshold = new BindableNumber(); + private readonly Bindable enabled = new BindableBool(true); + public JoystickSettings(JoystickHandler joystickHandler) + { + this.joystickHandler = joystickHandler; + } + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] { - this.joystickHandler = joystickHandler; - } - [BackgroundDependencyLoader] - private void load() - { - Children = new Drawable[] + new SettingsCheckbox { - new SettingsCheckbox - { - LabelText = CommonStrings.Enabled, - Current = enabled - }, - new DeadzoneSetting - { - LabelText = JoystickSettingsStrings.DeadzoneThreshold, - Current = deadzoneThreshold - }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - enabled.BindTo(joystickHandler.Enabled); - deadzoneThreshold.BindTo(joystickHandler.DeadzoneThreshold); - enabled.BindValueChanged(e => deadzoneThreshold.Disabled = !e.NewValue, true); - } - - private class DeadzoneSetting : SettingsSlider - { - public DeadzoneSetting() + LabelText = CommonStrings.Enabled, + Current = enabled + }, + new DeadzoneSetting { - KeyboardStep = 0.005f; - TransferValueOnCommit = true; - } - } - - private class DeadzoneSlider : OsuSliderBar + LabelText = JoystickSettingsStrings.DeadzoneThreshold, + Current = deadzoneThreshold + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + enabled.BindTo(joystickHandler.Enabled); + deadzoneThreshold.BindTo(joystickHandler.DeadzoneThreshold); + enabled.BindValueChanged(e => deadzoneThreshold.Disabled = !e.NewValue, true); + } + + private class DeadzoneSetting : SettingsSlider + { + public DeadzoneSetting() { - public override LocalisableString TooltipText => Current.Disabled ? "" : base.TooltipText; + KeyboardStep = 0.005f; + TransferValueOnCommit = true; } + } + private class DeadzoneSlider : OsuSliderBar + { + public override LocalisableString TooltipText => Current.Disabled ? "" : base.TooltipText; + } } } \ No newline at end of file From 6a87dfdabb8c56d54fffb2355181a9cde95e4eee Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 23 Apr 2022 02:14:41 -0300 Subject: [PATCH 0521/1276] More Code Quality Double whoops. --- osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs index aa6d602b6d..aaa8048019 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -17,10 +17,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; private readonly BindableNumber deadzoneThreshold = new BindableNumber(); private readonly Bindable enabled = new BindableBool(true); + public JoystickSettings(JoystickHandler joystickHandler) { this.joystickHandler = joystickHandler; } + [BackgroundDependencyLoader] private void load() { @@ -55,6 +57,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input TransferValueOnCommit = true; } } + private class DeadzoneSlider : OsuSliderBar { public override LocalisableString TooltipText => Current.Disabled ? "" : base.TooltipText; From 0133ee962da47e25fcc6255a94833f34db73955b Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 23 Apr 2022 02:23:07 -0300 Subject: [PATCH 0522/1276] Change JoystickSettingsStrings.cs text Co-authored-by: Salman Ahmed --- osu.Game/Localisation/JoystickSettingsStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/JoystickSettingsStrings.cs b/osu.Game/Localisation/JoystickSettingsStrings.cs index ee2096ef89..410cd0a6f5 100644 --- a/osu.Game/Localisation/JoystickSettingsStrings.cs +++ b/osu.Game/Localisation/JoystickSettingsStrings.cs @@ -17,7 +17,7 @@ namespace osu.Game.Localisation /// /// "Deadzone Threshold" /// - public static LocalisableString DeadzoneThreshold => new TranslatableString(getKey(@"deadzone_threshold"), @"Deadzone Threshold"); + public static LocalisableString DeadzoneThreshold => new TranslatableString(getKey(@"deadzone_threshold"), @"Deadzone"); private static string getKey(string key) => $@"{prefix}:{key}"; } From d97dc22e79021f94249a99cae827d1d25003bac7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Apr 2022 21:23:41 +0900 Subject: [PATCH 0523/1276] Add missing dependencies for behaviour screen test --- .../Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs index d7f0c5ba03..9747b5cc53 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs @@ -1,13 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Screens; +using osu.Game.Overlays; using osu.Game.Overlays.FirstRunSetup; namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneFirstRunScreenBehaviour : OsuManualInputManagerTestScene { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + public TestSceneFirstRunScreenBehaviour() { AddStep("load screen", () => From 5d0842ac447f2912872e6e37d029305baf69b0b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Apr 2022 21:53:47 +0900 Subject: [PATCH 0524/1276] Add a few more classic defaults to missed settings --- osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs | 1 + osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs | 1 + .../Settings/Sections/UserInterface/SongSelectSettings.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs index 5029c6a617..6978c77aad 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { + GetClassicDefault = () => false, LabelText = GameplaySettingsStrings.AlwaysPlayFirstComboBreak, Current = config.GetBindable(OsuSetting.AlwaysPlayFirstComboBreak) } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs index ba9779d650..285cdfa3f8 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs @@ -30,6 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { + GetClassicDefault = () => false, LabelText = GameplaySettingsStrings.ShowHealthDisplayWhenCantFail, Current = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail), Keywords = new[] { "hp", "bar" } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs index 6290046987..213234db4f 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs @@ -32,6 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { new SettingsCheckbox { + GetClassicDefault = () => true, LabelText = UserInterfaceStrings.RightMouseScroll, Current = config.GetBindable(OsuSetting.SongSelectRightMouseScroll), }, From 4bf11df57c9ac2ac19646f2ac83a94b75be2279c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 19:11:54 +0300 Subject: [PATCH 0525/1276] Change `SpriteComponent` accessibility to `internal` --- osu.Game/Skinning/SkinnableSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index af9ff0f1d7..21b34fcd27 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -65,7 +65,7 @@ namespace osu.Game.Skinning public bool UsesFixedAnchor { get; set; } - public class SpriteComponent : ISkinComponent + internal class SpriteComponent : ISkinComponent { public string LookupName { get; set; } From 5addcbf460d569a385c01b1db5a0918d8343d89c Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 23 Apr 2022 13:16:20 -0300 Subject: [PATCH 0526/1276] Changed KeyboardStep to 0.01 --- osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs index aaa8048019..ad01c25ac9 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public DeadzoneSetting() { - KeyboardStep = 0.005f; + KeyboardStep = 0.01f; TransferValueOnCommit = true; } } From e89441951cab3b2f3142d3b15c535c6cf867cf45 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 20 Apr 2022 22:00:37 +0200 Subject: [PATCH 0527/1276] Allow any key to trigger the initial osu! cookie --- osu.Game/Screens/Menu/ButtonSystem.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 885f4903b0..b48aef330a 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -196,11 +196,8 @@ namespace osu.Game.Screens.Menu if (State == ButtonSystemState.Initial) { - if (buttonsTopLevel.Any(b => e.Key == b.TriggerKey)) - { - logo?.TriggerClick(); - return true; - } + logo?.TriggerClick(); + return true; } return base.OnKeyDown(e); From cdfef088464046ee468ca016579d8362441a8c3e Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sat, 23 Apr 2022 18:44:50 +0200 Subject: [PATCH 0528/1276] Add tests for shortcut keys --- .../UserInterface/TestSceneButtonSystem.cs | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index 1bb5cadc6a..1a879e2e70 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs @@ -10,11 +10,12 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Screens.Menu; using osuTK; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneButtonSystem : OsuTestScene + public class TestSceneButtonSystem : OsuManualInputManagerTestScene { private OsuLogo logo; private ButtonSystem buttons; @@ -64,6 +65,66 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Enter mode", performEnterMode); } + [TestCase(Key.P, true)] + [TestCase(Key.M, true)] + [TestCase(Key.L, true)] + [TestCase(Key.E, false)] + [TestCase(Key.D, false)] + [TestCase(Key.Q, false)] + [TestCase(Key.O, false)] + public void TestShortcutKeys(Key key, bool entersPlay) + { + int activationCount = -1; + AddStep("set up action", () => + { + activationCount = 0; + void action() => activationCount++; + + switch (key) + { + case Key.P: + buttons.OnSolo = action; + break; + + case Key.M: + buttons.OnMultiplayer = action; + break; + + case Key.L: + buttons.OnPlaylists = action; + break; + + case Key.E: + buttons.OnEdit = action; + break; + + case Key.D: + buttons.OnBeatmapListing = action; + break; + + case Key.Q: + buttons.OnExit = action; + break; + + case Key.O: + buttons.OnSettings = action; + break; + } + }); + + AddStep($"press {key}", () => InputManager.Key(key)); + AddAssert("state is top level", () => buttons.State == ButtonSystemState.TopLevel); + + if (entersPlay) + { + AddStep("press P", () => InputManager.Key(Key.P)); + AddAssert("state is play", () => buttons.State == ButtonSystemState.Play); + } + + AddStep($"press {key}", () => InputManager.Key(key)); + AddAssert("action triggered", () => activationCount == 1); + } + private void performEnterMode() { buttons.State = ButtonSystemState.EnteringMode; From 684d88ba7586eb361091f8e235b35c9dc3af54c5 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sat, 23 Apr 2022 19:01:55 +0200 Subject: [PATCH 0529/1276] Add full OsuGame tests These tests ensure the expected behaviour is not broken in the future. --- .../TestSceneButtonSystemNavigation.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 osu.Game.Tests/Visual/Navigation/TestSceneButtonSystemNavigation.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneButtonSystemNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneButtonSystemNavigation.cs new file mode 100644 index 0000000000..8c96ec699f --- /dev/null +++ b/osu.Game.Tests/Visual/Navigation/TestSceneButtonSystemNavigation.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Select; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Navigation +{ + public class TestSceneButtonSystemNavigation : OsuGameTestScene + { + private ButtonSystem buttons => ((MainMenu)Game.ScreenStack.CurrentScreen).ChildrenOfType().Single(); + + [Test] + public void TestGlobalActionHasPriority() + { + AddAssert("state is initial", () => buttons.State == ButtonSystemState.Initial); + + // triggering the cookie in the initial state with any key should only happen if no other action is bound to that key. + // here, F10 is bound to GlobalAction.ToggleGameplayMouseButtons. + AddStep("press F10", () => InputManager.Key(Key.F10)); + AddAssert("state is initial", () => buttons.State == ButtonSystemState.Initial); + + AddStep("press P", () => InputManager.Key(Key.P)); + AddAssert("state is top level", () => buttons.State == ButtonSystemState.TopLevel); + } + + [Test] + public void TestShortcutKeys() + { + AddAssert("state is initial", () => buttons.State == ButtonSystemState.Initial); + + AddStep("press P", () => InputManager.Key(Key.P)); + AddAssert("state is top level", () => buttons.State == ButtonSystemState.TopLevel); + + AddStep("press P", () => InputManager.Key(Key.P)); + AddAssert("state is play", () => buttons.State == ButtonSystemState.Play); + + AddStep("press P", () => InputManager.Key(Key.P)); + AddAssert("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); + } + } +} From daadc3fc21c35824de511b930ffa06a1fe85c00a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 23:14:14 +0300 Subject: [PATCH 0530/1276] Simplfiy and share notification dimsiss method across test scenes Might as well inline, but I feel like it will require adding the same comment across all usages, because just `State.Value = Hidden` in an until step feels off, dunno. --- .../TestSceneChangeAndUseGameplayBindings.cs | 9 +-------- .../Navigation/TestSceneMouseWheelVolumeAdjust.cs | 9 +-------- .../Navigation/TestSceneScreenNavigation.cs | 15 +++------------ osu.Game/Tests/Visual/OsuGameTestScene.cs | 7 +++++++ 4 files changed, 12 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs b/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs index 347b4b6c54..b7a74dcd27 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs @@ -59,8 +59,7 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for player", () => { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); + DismissAnyNotifications(); return player != null; }); @@ -73,12 +72,6 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("key counter did increase", () => keyCounter.CountPresses == 1); } - private void clickMouseInCentre() - { - InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre); - InputManager.Click(MouseButton.Left); - } - private KeyBindingsSubsection osuBindingSubsection => keyBindingPanel .ChildrenOfType() .FirstOrDefault(s => s.Ruleset.ShortName == "osu"); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs b/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs index 22a00a3e5a..2662b3930c 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs @@ -89,18 +89,11 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for player", () => { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); + DismissAnyNotifications(); return (player = Game.ScreenStack.CurrentScreen as Player) != null; }); AddUntilStep("wait for play time active", () => !player.IsBreakTime.Value); } - - private void clickMouseInCentre() - { - InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre); - InputManager.Click(MouseButton.Left); - } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 0b7242eee0..296a184f4b 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -155,8 +155,7 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for player", () => { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); + DismissAnyNotifications(); return (player = Game.ScreenStack.CurrentScreen as Player) != null; }); @@ -280,8 +279,7 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for player", () => { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); + DismissAnyNotifications(); return (player = Game.ScreenStack.CurrentScreen as Player) != null; }); @@ -596,8 +594,7 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for player", () => { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); + DismissAnyNotifications(); return (player = Game.ScreenStack.CurrentScreen as Player) != null; }); @@ -607,12 +604,6 @@ namespace osu.Game.Tests.Visual.Navigation return () => player; } - private void clickMouseInCentre() - { - InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre); - InputManager.Click(MouseButton.Left); - } - private void pushEscape() => AddStep("Press escape", () => InputManager.Key(Key.Escape)); diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index 7a6f8c8cfb..1b8f8bfd07 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -23,7 +23,9 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Menu; +using osu.Game.Screens.Play; using osuTK.Graphics; +using osuTK.Input; using IntroSequence = osu.Game.Configuration.IntroSequence; namespace osu.Game.Tests.Visual @@ -106,6 +108,11 @@ namespace osu.Game.Tests.Visual protected void ConfirmAtMainMenu() => AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded); + /// + /// Dismisses any notifications pushed which block from interacting with the game (or block screens from loading, e.g. ). + /// + protected void DismissAnyNotifications() => Game.Notifications.State.Value = Visibility.Hidden; + public class TestOsuGame : OsuGame { public new const float SIDE_OVERLAY_OFFSET_RATIO = OsuGame.SIDE_OVERLAY_OFFSET_RATIO; From 6ab188f8f9c52651a64dc26b27534d47a228a133 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 23:15:31 +0300 Subject: [PATCH 0531/1276] Split skin editor scene library test to own file Rather than stuffing more random cases to `TestSceneScreenNavigation`. --- .../Navigation/TestSceneScreenNavigation.cs | 71 -------------- .../TestSceneSkinEditorSceneLibrary.cs | 97 +++++++++++++++++++ 2 files changed, 97 insertions(+), 71 deletions(-) create mode 100644 osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 296a184f4b..a1f41d4caf 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -16,7 +15,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Leaderboards; using osu.Game.Overlays; using osu.Game.Overlays.Mods; -using osu.Game.Overlays.Settings; using osu.Game.Overlays.Toolbar; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; @@ -24,12 +22,10 @@ using osu.Game.Scoring; using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.Play; -using osu.Game.Screens.Play.HUD.HitErrorMeters; using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Screens.Select.Options; -using osu.Game.Skinning.Editor; using osu.Game.Tests.Beatmaps.IO; using osuTK; using osuTK.Input; @@ -71,73 +67,6 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); } - [Test] - public void TestEditComponentDuringGameplay() - { - Screens.Select.SongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestPlaySongSelect()); - AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); - - AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); - - AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); - - SkinEditor skinEditor = null; - - AddStep("open skin editor", () => - { - InputManager.PressKey(Key.ControlLeft); - InputManager.PressKey(Key.ShiftLeft); - InputManager.Key(Key.S); - InputManager.ReleaseKey(Key.ControlLeft); - InputManager.ReleaseKey(Key.ShiftLeft); - }); - - AddUntilStep("get skin editor", () => (skinEditor = Game.ChildrenOfType().FirstOrDefault()) != null); - - AddStep("Click gameplay scene button", () => - { - skinEditor.ChildrenOfType().First(b => b.Text == "Gameplay").TriggerClick(); - }); - - AddUntilStep("wait for player", () => - { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); - return Game.ScreenStack.CurrentScreen is Player; - }); - - BarHitErrorMeter hitErrorMeter = null; - - AddUntilStep("select bar hit error blueprint", () => - { - var blueprint = skinEditor.ChildrenOfType().FirstOrDefault(b => b.Item is BarHitErrorMeter); - - if (blueprint == null) - return false; - - hitErrorMeter = (BarHitErrorMeter)blueprint.Item; - skinEditor.SelectedComponents.Clear(); - skinEditor.SelectedComponents.Add(blueprint.Item); - return true; - }); - - AddAssert("value is default", () => hitErrorMeter.JudgementLineThickness.IsDefault); - - AddStep("hover first slider", () => - { - InputManager.MoveMouseTo( - skinEditor.ChildrenOfType().First() - .ChildrenOfType>().First() - .ChildrenOfType>().First() - ); - }); - - AddStep("adjust slider via keyboard", () => InputManager.Key(Key.Left)); - - AddAssert("value is less than default", () => hitErrorMeter.JudgementLineThickness.Value < hitErrorMeter.JudgementLineThickness.Default); - } - [Test] public void TestRetryCountIncrements() { diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs new file mode 100644 index 0000000000..ed3eae528f --- /dev/null +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs @@ -0,0 +1,97 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Extensions; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Testing; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD.HitErrorMeters; +using osu.Game.Skinning.Editor; +using osu.Game.Tests.Beatmaps.IO; +using osuTK.Input; +using static osu.Game.Tests.Visual.Navigation.TestSceneScreenNavigation; + +namespace osu.Game.Tests.Visual.Navigation +{ + public class TestSceneSkinEditorSceneLibrary : OsuGameTestScene + { + private SkinEditor skinEditor; + + public override void SetUpSteps() + { + base.SetUpSteps(); + + Screens.Select.SongSelect songSelect = null; + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); + AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); + + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + + AddStep("open skin editor", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.S); + InputManager.ReleaseKey(Key.ControlLeft); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + + AddUntilStep("get skin editor", () => (skinEditor = Game.ChildrenOfType().FirstOrDefault()) != null); + } + + [Test] + public void TestEditComponentDuringGameplay() + { + switchToGameplayScene(); + + BarHitErrorMeter hitErrorMeter = null; + + AddUntilStep("select bar hit error blueprint", () => + { + var blueprint = skinEditor.ChildrenOfType().FirstOrDefault(b => b.Item is BarHitErrorMeter); + + if (blueprint == null) + return false; + + hitErrorMeter = (BarHitErrorMeter)blueprint.Item; + skinEditor.SelectedComponents.Clear(); + skinEditor.SelectedComponents.Add(blueprint.Item); + return true; + }); + + AddAssert("value is default", () => hitErrorMeter.JudgementLineThickness.IsDefault); + + AddStep("hover first slider", () => + { + InputManager.MoveMouseTo( + skinEditor.ChildrenOfType().First() + .ChildrenOfType>().First() + .ChildrenOfType>().First() + ); + }); + + AddStep("adjust slider via keyboard", () => InputManager.Key(Key.Left)); + + AddAssert("value is less than default", () => hitErrorMeter.JudgementLineThickness.Value < hitErrorMeter.JudgementLineThickness.Default); + } + + private void switchToGameplayScene() + { + AddStep("Click gameplay scene button", () => skinEditor.ChildrenOfType().First(b => b.Text == "Gameplay").TriggerClick()); + + AddUntilStep("wait for player", () => + { + DismissAnyNotifications(); + return Game.ScreenStack.CurrentScreen is Player; + }); + } + } +} From 60e76d62c1231ce81e1c7725136fb160f8d4e750 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 23:16:06 +0300 Subject: [PATCH 0532/1276] Add failing test cases --- .../TestSceneSkinEditorSceneLibrary.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs index ed3eae528f..448d6b1e68 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs @@ -83,6 +83,46 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("value is less than default", () => hitErrorMeter.JudgementLineThickness.Value < hitErrorMeter.JudgementLineThickness.Default); } + [Test] + public void TestAutoplayCompatibleModsRetainedOnEnteringGameplay() + { + AddStep("select DT", () => Game.SelectedMods.Value = new Mod[] { new OsuModDoubleTime() }); + + switchToGameplayScene(); + + AddAssert("DT still selected", () => ((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Single() is OsuModDoubleTime); + } + + [Test] + public void TestAutoplayIncompatibleModsRemovedOnEnteringGameplay() + { + AddStep("select no fail and spun out", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModSpunOut() }); + + switchToGameplayScene(); + + AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any()); + } + + [Test] + public void TestDuplicateAutoplayModRemovedOnEnteringGameplay() + { + AddStep("select autoplay", () => Game.SelectedMods.Value = new Mod[] { new OsuModAutoplay() }); + + switchToGameplayScene(); + + AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any()); + } + + [Test] + public void TestCinemaModRemovedOnEnteringGameplay() + { + AddStep("select cinema", () => Game.SelectedMods.Value = new Mod[] { new OsuModCinema() }); + + switchToGameplayScene(); + + AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any()); + } + private void switchToGameplayScene() { AddStep("Click gameplay scene button", () => skinEditor.ChildrenOfType().First(b => b.Text == "Gameplay").TriggerClick()); From 0ecda46d93261dce4ebae52eaec3da768c16da1e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 23:16:32 +0300 Subject: [PATCH 0533/1276] Deselect mods incompatible with "Autoplay" when entering gameplay via scene library --- osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs index 4507526806..2124ba9b6d 100644 --- a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs +++ b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -18,6 +20,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens; using osu.Game.Screens.Play; using osu.Game.Screens.Select; +using osu.Game.Utils; using osuTK; namespace osu.Game.Skinning.Editor @@ -34,6 +37,9 @@ namespace osu.Game.Skinning.Editor [Resolved] private IBindable ruleset { get; set; } + [Resolved] + private Bindable> mods { get; set; } + public SkinEditorSceneLibrary() { Height = BUTTON_HEIGHT + padding * 2; @@ -95,6 +101,10 @@ namespace osu.Game.Skinning.Editor return; var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod(); + + if (!ModUtils.CheckCompatibleSet(mods.Value.Append(replayGeneratingMod), out var invalid)) + mods.Value = mods.Value.Except(invalid).ToArray(); + if (replayGeneratingMod != null) screen.Push(new PlayerLoader(() => new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods)))); }, new[] { typeof(Player), typeof(SongSelect) }) From e8cf4466b0caee3478ca109329b237608748b12a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 23:19:03 +0300 Subject: [PATCH 0534/1276] Make sections container test scene more usable Head busted the moment I opened this test scene. Cleaned it up a bit to make it easier to test changes. --- .../TestSceneSectionsContainer.cs | 146 ++++++++++++++---- 1 file changed, 115 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs index 2312c57af2..fc2b5e5e09 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs @@ -3,45 +3,79 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneSectionsContainer : OsuManualInputManagerTestScene { - private readonly SectionsContainer container; + private SectionsContainer container; private float custom; - private const float header_height = 100; - public TestSceneSectionsContainer() + private const float header_expandable_height = 300; + private const float header_fixed_height = 100; + + [SetUpSteps] + public void SetUpSteps() { - container = new SectionsContainer + AddStep("setup container", () => { - RelativeSizeAxes = Axes.Y, - Width = 300, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - FixedHeader = new Box + container = new SectionsContainer { - Alpha = 0.5f, + RelativeSizeAxes = Axes.Y, Width = 300, - Height = header_height, - Colour = Color4.Red - } - }; - container.SelectedSection.ValueChanged += section => - { - if (section.OldValue != null) - section.OldValue.Selected = false; - if (section.NewValue != null) - section.NewValue.Selected = true; - }; - Add(container); + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + }; + + container.SelectedSection.ValueChanged += section => + { + if (section.OldValue != null) + section.OldValue.Selected = false; + if (section.NewValue != null) + section.NewValue.Selected = true; + }; + + Child = container; + }); + + AddToggleStep("disable expandable header", v => container.ExpandableHeader = v + ? null + : new TestBox(@"Expandable Header") + { + RelativeSizeAxes = Axes.X, + Height = header_expandable_height, + BackgroundColour = new OsuColour().GreySky, + }); + + AddToggleStep("disable fixed header", v => container.FixedHeader = v + ? null + : new TestBox(@"Fixed Header") + { + RelativeSizeAxes = Axes.X, + Height = header_fixed_height, + BackgroundColour = new OsuColour().Red.Opacity(0.5f), + }); + + AddToggleStep("disable footer", v => container.Footer = v + ? null + : new TestBox("Footer") + { + RelativeSizeAxes = Axes.X, + Height = 200, + BackgroundColour = new OsuColour().Green4, + }); } [Test] @@ -71,7 +105,6 @@ namespace osu.Game.Tests.Visual.UserInterface { const int sections_count = 11; float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f }; - AddStep("clear", () => container.Clear()); AddStep("fill with sections", () => { for (int i = 0; i < sections_count; i++) @@ -84,9 +117,9 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[scrollIndex]); AddUntilStep("section top is visible", () => { - float scrollPosition = container.ChildrenOfType().First().Current; - float sectionTop = container.Children[scrollIndex].BoundingBox.Top; - return scrollPosition < sectionTop; + var scrollContainer = container.ChildrenOfType().Single(); + float sectionPosition = scrollContainer.GetChildPosInContent(container.Children[scrollIndex]); + return scrollContainer.Current < sectionPosition; }); } @@ -101,15 +134,27 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[sections_count - 1]); } - private static readonly ColourInfo selected_colour = ColourInfo.GradientVertical(Color4.Yellow, Color4.Gold); + private static readonly ColourInfo selected_colour = ColourInfo.GradientVertical(new OsuColour().Orange2, new OsuColour().Orange3); private static readonly ColourInfo default_colour = ColourInfo.GradientVertical(Color4.White, Color4.DarkGray); private void append(float multiplier) { - container.Add(new TestSection + float fixedHeaderHeight = container.FixedHeader?.Height ?? 0; + float expandableHeaderHeight = container.ExpandableHeader?.Height ?? 0; + + float totalHeaderHeight = expandableHeaderHeight + fixedHeaderHeight; + float effectiveHeaderHeight = totalHeaderHeight; + + // if we're in the "next page" of the sections container, + // height of the expandable header should not be accounted. + var scrollContent = container.ChildrenOfType().Single().ScrollContent; + if (totalHeaderHeight + scrollContent.Height >= Content.DrawHeight) + effectiveHeaderHeight -= expandableHeaderHeight; + + container.Add(new TestSection($"Section #{container.Children.Count + 1}") { Width = 300, - Height = (container.ChildSize.Y - header_height) * multiplier, + Height = (Content.DrawHeight - effectiveHeaderHeight) * multiplier, Colour = default_colour }); } @@ -120,11 +165,50 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.ScrollVerticalBy(direction); } - private class TestSection : Box + private class TestSection : TestBox { public bool Selected { - set => Colour = value ? selected_colour : default_colour; + set => BackgroundColour = value ? selected_colour : default_colour; + } + + public TestSection(string label) + : base(label) + { + BackgroundColour = default_colour; + } + } + + private class TestBox : Container + { + private readonly Box background; + private readonly OsuSpriteText text; + + public ColourInfo BackgroundColour + { + set + { + background.Colour = value; + text.Colour = OsuColour.ForegroundTextColourFor(value.AverageColour); + } + } + + public TestBox(string label) + { + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = label, + Font = OsuFont.Default.With(size: 36), + } + }; } } } From 05736fb84ce8404070d38180efa663a5a3d67dd6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 24 Apr 2022 01:09:24 +0300 Subject: [PATCH 0535/1276] Add failing test case for navigation via PageUp/PageDown --- .../TestSceneSectionsContainer.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs index fc2b5e5e09..1f3736bd9b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs @@ -134,6 +134,35 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[sections_count - 1]); } + [Test] + public void TestNavigation() + { + AddRepeatStep("add sections", () => append(1f), 3); + AddUntilStep("wait for load", () => container.Children.Any()); + + AddStep("hover sections container", () => InputManager.MoveMouseTo(container)); + AddStep("press page down", () => InputManager.Key(Key.PageDown)); + AddUntilStep("scrolled one page down", () => + { + var scroll = container.ChildrenOfType().First(); + return Precision.AlmostEquals(scroll.Current, Content.DrawHeight - header_fixed_height, 1f); + }); + + AddStep("press page down", () => InputManager.Key(Key.PageDown)); + AddUntilStep("scrolled two pages down", () => + { + var scroll = container.ChildrenOfType().First(); + return Precision.AlmostEquals(scroll.Current, (Content.DrawHeight - header_fixed_height) * 2, 1f); + }); + + AddStep("press page up", () => InputManager.Key(Key.PageUp)); + AddUntilStep("scrolled one page up", () => + { + var scroll = container.ChildrenOfType().First(); + return Precision.AlmostEquals(scroll.Current, Content.DrawHeight - header_fixed_height, 1f); + }); + } + private static readonly ColourInfo selected_colour = ColourInfo.GradientVertical(new OsuColour().Orange2, new OsuColour().Orange3); private static readonly ColourInfo default_colour = ColourInfo.GradientVertical(Color4.White, Color4.DarkGray); From 3700c607d8fc9844d406496a8c6ff0d843aa9d06 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 06:03:54 +0300 Subject: [PATCH 0536/1276] Fix sections scroll container not handling fixed header in scrolls --- osu.Game/Graphics/Containers/SectionsContainer.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 540ca85809..9fa8b352cf 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -149,13 +149,11 @@ namespace osu.Game.Graphics.Containers { lastKnownScroll = null; - float fixedHeaderSize = FixedHeader?.BoundingBox.Height ?? 0; - // implementation similar to ScrollIntoView but a bit more nuanced. float top = scrollContainer.GetChildPosInContent(target); - float bottomScrollExtent = scrollContainer.ScrollableExtent - fixedHeaderSize; - float scrollTarget = top - fixedHeaderSize - scrollContainer.DisplayableContent * scroll_y_centre; + float bottomScrollExtent = scrollContainer.ScrollableExtent; + float scrollTarget = top - scrollContainer.DisplayableContent * scroll_y_centre; if (scrollTarget > bottomScrollExtent) scrollContainer.ScrollToEnd(); @@ -270,9 +268,13 @@ namespace osu.Game.Graphics.Containers { if (!Children.Any()) return; - var newMargin = originalSectionsMargin; + // if a fixed header is present, apply top padding for it + // to make the scroll container aware of its displayable area. + // (i.e. for page up/down to work properly) + scrollContainer.Padding = new MarginPadding { Top = FixedHeader?.LayoutSize.Y ?? 0 }; - newMargin.Top += (headerHeight ?? 0); + var newMargin = originalSectionsMargin; + newMargin.Top += (ExpandableHeader?.LayoutSize.Y ?? 0); newMargin.Bottom += (footerHeight ?? 0); scrollContentContainer.Margin = newMargin; From 70a93c0e7eb66c3316e5c4d0a38a96dc7ebc0db7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 24 Apr 2022 07:08:53 +0300 Subject: [PATCH 0537/1276] Move `DistanceSpacingMultiplier` bindable to its own `IDistanceSnapProvider` interface --- .../Editor/TestSceneManiaBeatSnapGrid.cs | 3 --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 4 ++-- .../Visual/Editing/TestSceneDistanceSnapGrid.cs | 4 ++-- osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs | 17 +++++++++++++++++ osu.Game/Rulesets/Edit/IPositionSnapProvider.cs | 10 ++-------- .../Edit/Compose/Components/DistanceSnapGrid.cs | 2 +- 6 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs index f32a1a823e..50be13c4e0 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Framework.Timing; @@ -99,8 +98,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor set => InternalChild = value; } - public override IBindable DistanceSpacingMultiplier => throw new System.NotImplementedException(); - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { throw new System.NotImplementedException(); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 38a442d48b..2ba30c5f74 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [Cached] private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); - [Cached(typeof(IPositionSnapProvider))] + [Cached(typeof(IDistanceSnapProvider))] private readonly SnapProvider snapProvider = new SnapProvider(); private TestOsuDistanceSnapGrid grid; @@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor } } - private class SnapProvider : IPositionSnapProvider + private class SnapProvider : IDistanceSnapProvider { public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, null); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 69fd642143..b9cfa84a5d 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Editing [Cached(typeof(EditorBeatmap))] private readonly EditorBeatmap editorBeatmap; - [Cached(typeof(IPositionSnapProvider))] + [Cached(typeof(IDistanceSnapProvider))] private readonly SnapProvider snapProvider = new SnapProvider(); public TestSceneDistanceSnapGrid() @@ -160,7 +160,7 @@ namespace osu.Game.Tests.Visual.Editing => (Vector2.Zero, 0); } - private class SnapProvider : IPositionSnapProvider + private class SnapProvider : IDistanceSnapProvider { public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, null); diff --git a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs new file mode 100644 index 0000000000..70921a6170 --- /dev/null +++ b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Edit +{ + public interface IDistanceSnapProvider : IPositionSnapProvider + { + /// + /// The spacing multiplier applied to beat snap distances. + /// + /// + IBindable DistanceSpacingMultiplier { get; } + } +} diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index e4adbba47d..944619a3ca 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Bindables; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osuTK; @@ -10,12 +8,6 @@ namespace osu.Game.Rulesets.Edit { public interface IPositionSnapProvider { - /// - /// The spacing multiplier applied to beat snap distances. - /// - /// - IBindable DistanceSpacingMultiplier { get; } - /// /// Given a position, find a valid time and position snap. /// @@ -38,6 +30,8 @@ namespace osu.Game.Rulesets.Edit /// /// An object to be used as a reference point for this operation. /// The distance between two points residing in the timing point that are one beat length apart. + // todo: this, and other distance-related methods below it, should move to IDistanceSnapProvider. + // needs thought on how to properly share implementation, since not all composers inherit IDistanceSnapProvider. float GetBeatSnapDistanceAt(HitObject referenceObject); /// diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index f9e4ef086c..9dd6cec8ce 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected OsuColour Colours { get; private set; } [Resolved] - protected IPositionSnapProvider SnapProvider { get; private set; } + protected IDistanceSnapProvider SnapProvider { get; private set; } [Resolved] private EditorBeatmap beatmap { get; set; } From 8ed39009fd5301396d5d98be2a98c9dd6e3a3d87 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 24 Apr 2022 07:10:30 +0300 Subject: [PATCH 0538/1276] Encapsulate distance spacing control handling to a "distance toolbox composite" Encapsulated in a way which can allow further extensibility for the right-side area of toolboxes. --- .../Edit/CatchHitObjectComposer.cs | 5 +- .../Edit/ManiaHitObjectComposer.cs | 2 - .../Editor/TestSceneObjectObjectSnap.cs | 4 +- .../Editor/TestSceneSliderSnapping.cs | 3 +- .../Edit/OsuHitObjectComposer.cs | 12 +- .../Edit/OsuToolboxComposite.cs | 119 ++++++++++++++++++ .../Edit/TaikoHitObjectComposer.cs | 2 - .../Overlays/Settings/Sections/SizeSlider.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 99 +-------------- 9 files changed, 137 insertions(+), 111 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 19595de3b1..b3e6fe382a 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -24,6 +24,9 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Edit { + /// + /// todo: should inherit once it supports distance spacing properly. + /// public class CatchHitObjectComposer : HitObjectComposer { private const float distance_snap_radius = 50; @@ -32,8 +35,6 @@ namespace osu.Game.Rulesets.Catch.Edit private readonly Bindable distanceSnapToggle = new Bindable(); - protected override bool SupportsDistanceSpacing => false; - private InputManager inputManager; public CatchHitObjectComposer(CatchRuleset ruleset) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 5752f9f014..2baec95c94 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Mania.Edit private ManiaBeatSnapGrid beatSnapGrid; private InputManager inputManager; - protected override bool SupportsDistanceSpacing => false; - public ManiaHitObjectComposer(Ruleset ruleset) : base(ruleset) { diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs index aa0e35102e..954bb53dd5 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs @@ -7,7 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Tests.Beatmaps; @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("seek to first control point", () => EditorClock.Seek(Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First().Time)); AddStep("set distance spacing to 1", () => { - var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; + var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; distanceSpacing.Value = 1; }); } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index 13ce340362..2632e556c6 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -11,7 +11,6 @@ using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Input.Bindings; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; @@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor }); AddStep("set distance spacing to 1", () => { - var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; + var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; distanceSpacing.Value = 1; }); } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index e7eabdc748..a52c916ca0 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -24,9 +24,12 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Edit { - public class OsuHitObjectComposer : HitObjectComposer + [Cached(typeof(IDistanceSnapProvider))] + public class OsuHitObjectComposer : HitObjectComposer, IDistanceSnapProvider { - protected override bool SupportsDistanceSpacing => true; + private OsuToolboxComposite osuToolboxComposite; + + public IBindable DistanceSpacingMultiplier => osuToolboxComposite.DistanceSpacing; public OsuHitObjectComposer(Ruleset ruleset) : base(ruleset) @@ -76,6 +79,8 @@ namespace osu.Game.Rulesets.Osu.Edit } }); + AddInternal(osuToolboxComposite = new OsuToolboxComposite()); + selectedHitObjects = EditorBeatmap.SelectedHitObjects.GetBoundCopy(); selectedHitObjects.CollectionChanged += (_, __) => updateDistanceSnapGrid(); @@ -130,6 +135,9 @@ namespace osu.Game.Rulesets.Osu.Edit } } + public override float GetBeatSnapDistanceAt(HitObject referenceObject) + => (float)(base.GetBeatSnapDistanceAt(referenceObject) * DistanceSpacingMultiplier.Value); + public override SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) { if (snapToVisibleBlueprints(screenSpacePosition, out var snapResult)) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs b/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs new file mode 100644 index 0000000000..b0298e4034 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs @@ -0,0 +1,119 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings.Sections; +using osu.Game.Rulesets.Edit; +using osu.Game.Screens.Edit; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Osu.Edit +{ + /// + /// A toolbox composite for osu!-specific controls. + /// + // todo: once catch supports distance spacing, the control here should move out to a base "DistancingRulesetToolboxComposite" class or something better. + public class OsuToolboxComposite : CompositeDrawable + { + private ExpandingToolboxContainer expandingContainer; + private ExpandableSlider> distanceSpacingSlider; + + private readonly Bindable distanceSpacing = new BindableDouble(1.0) + { + MinValue = 0.1, + MaxValue = 6.0, + Precision = 0.01, + }; + + public IBindable DistanceSpacing => distanceSpacing; + + private bool distanceSpacingScrollActive; + + [Resolved] + private EditorBeatmap editorBeatmap { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + InternalChild = expandingContainer = new ExpandingToolboxContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Child = new EditorToolboxGroup("snapping") + { + Child = distanceSpacingSlider = new ExpandableSlider> + { + Current = { BindTarget = distanceSpacing }, + KeyboardStep = 0.1f, + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + distanceSpacing.Value = editorBeatmap.BeatmapInfo.DistanceSpacing; + distanceSpacing.BindValueChanged(v => + { + distanceSpacingSlider.ContractedLabelText = $"D. S. ({v.NewValue:0.##x})"; + distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; + editorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; + }, true); + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.AltPressed && e.Key == Key.D && !e.Repeat) + { + expandingContainer.Expanded.Value = true; + distanceSpacingScrollActive = true; + return true; + } + + return base.OnKeyDown(e); + } + + protected override void OnKeyUp(KeyUpEvent e) + { + if (distanceSpacingScrollActive && (!e.AltPressed || e.Key == Key.D)) + { + expandingContainer.Expanded.Value = false; + distanceSpacingScrollActive = false; + } + } + + protected override bool OnScroll(ScrollEvent e) + { + if (distanceSpacingScrollActive) + { + distanceSpacing.Value += e.ScrollDelta.Y * (e.IsPrecise ? 0.01f : 0.1f); + return true; + } + + return base.OnScroll(e); + } + + private class ExpandingToolboxContainer : ExpandingContainer + { + public ExpandingToolboxContainer() + : base(130, 250) + { + RelativeSizeAxes = Axes.Y; + Padding = new MarginPadding { Left = 10 }; + + FillFlow.Spacing = new Vector2(10); + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs index af9bc6c3a3..161799c980 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs @@ -11,8 +11,6 @@ namespace osu.Game.Rulesets.Taiko.Edit { public class TaikoHitObjectComposer : HitObjectComposer { - protected override bool SupportsDistanceSpacing => false; - public TaikoHitObjectComposer(TaikoRuleset ruleset) : base(ruleset) { diff --git a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs index 26db1aa755..c8a46162af 100644 --- a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs +++ b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Settings.Sections /// /// A slider intended to show a "size" multiplier number, where 1x is 1.0. /// - internal class SizeSlider : OsuSliderBar + public class SizeSlider : OsuSliderBar where T : struct, IEquatable, IComparable, IConvertible, IFormattable { public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", NumberFormatInfo.CurrentInfo); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index ab82fbad04..8d2bd1e5d1 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -14,8 +14,6 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Settings.Sections; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; @@ -58,26 +56,6 @@ namespace osu.Game.Rulesets.Edit [Resolved] protected IBeatSnapProvider BeatSnapProvider { get; private set; } - /// - /// Whether this composer supports a "distance spacing" multiplier for distance snap grids. - /// - /// - /// Setting this to displays a "distance spacing" slider and allows this composer to configure the value of . - /// - protected abstract bool SupportsDistanceSpacing { get; } - - private readonly BindableDouble distanceSpacing = new BindableDouble - { - Default = 1.0, - MinValue = 0.1, - MaxValue = 6.0, - Precision = 0.01, - }; - - public override IBindable DistanceSpacingMultiplier => distanceSpacing; - - private SnappingToolboxContainer snappingToolboxContainer; - protected ComposeBlueprintContainer BlueprintContainer { get; private set; } private DrawableEditorRulesetWrapper drawableRulesetWrapper; @@ -161,36 +139,6 @@ namespace osu.Game.Rulesets.Edit }, }; - distanceSpacing.Value = EditorBeatmap.BeatmapInfo.DistanceSpacing; - - if (SupportsDistanceSpacing) - { - ExpandableSlider> distanceSpacingSlider; - - AddInternal(snappingToolboxContainer = new SnappingToolboxContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Child = new EditorToolboxGroup("snapping") - { - Child = distanceSpacingSlider = new ExpandableSlider> - { - Current = { BindTarget = distanceSpacing }, - KeyboardStep = 0.01f, - } - } - }); - - distanceSpacing.BindValueChanged(v => - { - distanceSpacingSlider.ContractedLabelText = $"D. S. ({v.NewValue:0.##x})"; - distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; - EditorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; - }, true); - } - else - distanceSpacing.Disabled = true; - toolboxCollection.Items = CompositionTools .Prepend(new SelectTool()) .Select(t => new RadioButton(t.Name, () => toolSelected(t), t.CreateIcon)) @@ -265,17 +213,8 @@ namespace osu.Game.Rulesets.Edit #region Tool selection logic - private bool distanceSpacingScrollActive; - protected override bool OnKeyDown(KeyDownEvent e) { - if (SupportsDistanceSpacing && e.AltPressed && e.Key == Key.D && !e.Repeat) - { - snappingToolboxContainer.Expanded.Value = true; - distanceSpacingScrollActive = true; - return true; - } - if (e.ControlPressed || e.AltPressed || e.SuperPressed) return false; @@ -305,28 +244,6 @@ namespace osu.Game.Rulesets.Edit return base.OnKeyDown(e); } - protected override void OnKeyUp(KeyUpEvent e) - { - if (distanceSpacingScrollActive && (e.Key == Key.AltLeft || e.Key == Key.AltRight || e.Key == Key.D)) - { - snappingToolboxContainer.Expanded.Value = false; - distanceSpacingScrollActive = false; - } - - base.OnKeyUp(e); - } - - protected override bool OnScroll(ScrollEvent e) - { - if (distanceSpacingScrollActive) - { - distanceSpacing.Value += e.ScrollDelta.Y * (e.IsPrecise ? 0.01f : 0.1f); - return true; - } - - return base.OnScroll(e); - } - private bool checkLeftToggleFromKey(Key key, out int index) { if (key < Key.Number1 || key > Key.Number9) @@ -468,7 +385,7 @@ namespace osu.Game.Rulesets.Edit public override float GetBeatSnapDistanceAt(HitObject referenceObject) { - return (float)(100 * referenceObject.DifficultyControlPoint.SliderVelocity * EditorBeatmap.Difficulty.SliderMultiplier * distanceSpacing.Value / BeatSnapProvider.BeatDivisor); + return (float)(100 * referenceObject.DifficultyControlPoint.SliderVelocity * EditorBeatmap.Difficulty.SliderMultiplier / BeatSnapProvider.BeatDivisor); } public override float DurationToDistance(HitObject referenceObject, double duration) @@ -517,18 +434,6 @@ namespace osu.Game.Rulesets.Edit FillFlow.Spacing = new Vector2(10); } } - - private class SnappingToolboxContainer : ExpandingContainer - { - public SnappingToolboxContainer() - : base(130, 250) - { - RelativeSizeAxes = Axes.Y; - Padding = new MarginPadding { Left = 10 }; - - FillFlow.Spacing = new Vector2(10); - } - } } /// @@ -563,8 +468,6 @@ namespace osu.Game.Rulesets.Edit #region IPositionSnapProvider - public abstract IBindable DistanceSpacingMultiplier { get; } - public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); public virtual SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => From 0738cd028f5824cf6f9a0511ee9321d8e6876000 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 24 Apr 2022 08:53:10 +0300 Subject: [PATCH 0539/1276] Give snapping toolbox enough hover delay for interaction with expand button With 0 delay, if the `SettingsToolboxGroup` has been contracted via button, there's no chance to be able to expand it via button again. This is temporary at best just to keep things working somewhat, not sure if the expansion logic will still be required in editor redesign, so deciding to roll with it for now. --- osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs b/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs index b0298e4034..542f98207e 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs @@ -106,6 +106,8 @@ namespace osu.Game.Rulesets.Osu.Edit private class ExpandingToolboxContainer : ExpandingContainer { + protected override double HoverExpansionDelay => 250; + public ExpandingToolboxContainer() : base(130, 250) { From 2200067c52759a32eb1adfa12fd79347b7f51200 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 16:31:20 +0900 Subject: [PATCH 0540/1276] Display deadzone as percentage and simplify surrounding code --- .../Sections/Input/JoystickSettings.cs | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs index ad01c25ac9..c136ca6a19 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -6,18 +6,20 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Handlers.Joystick; using osu.Framework.Localisation; -using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { public class JoystickSettings : SettingsSubsection { - private readonly JoystickHandler joystickHandler; protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; - private readonly BindableNumber deadzoneThreshold = new BindableNumber(); + + private readonly JoystickHandler joystickHandler; + private readonly Bindable enabled = new BindableBool(true); + private SettingsSlider deadzoneSlider; + public JoystickSettings(JoystickHandler joystickHandler) { this.joystickHandler = joystickHandler; @@ -33,10 +35,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = CommonStrings.Enabled, Current = enabled }, - new DeadzoneSetting + deadzoneSlider = new SettingsSlider { LabelText = JoystickSettingsStrings.DeadzoneThreshold, - Current = deadzoneThreshold + KeyboardStep = 0.01f, + DisplayAsPercentage = true, + Current = joystickHandler.DeadzoneThreshold, }, }; } @@ -44,23 +48,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input protected override void LoadComplete() { base.LoadComplete(); + enabled.BindTo(joystickHandler.Enabled); - deadzoneThreshold.BindTo(joystickHandler.DeadzoneThreshold); - enabled.BindValueChanged(e => deadzoneThreshold.Disabled = !e.NewValue, true); - } - - private class DeadzoneSetting : SettingsSlider - { - public DeadzoneSetting() - { - KeyboardStep = 0.01f; - TransferValueOnCommit = true; - } - } - - private class DeadzoneSlider : OsuSliderBar - { - public override LocalisableString TooltipText => Current.Disabled ? "" : base.TooltipText; + enabled.BindValueChanged(e => deadzoneSlider.Current.Disabled = !e.NewValue, true); } } -} \ No newline at end of file +} From 1b2467d3ed7df7e2c2171947e606b296ce48983c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 16:35:41 +0900 Subject: [PATCH 0541/1276] Disable first run screen from appearing on startup until it is more complete --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 1 + osu.Game/Overlays/FirstRunSetupOverlay.cs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index e925859d71..31c4d66784 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -66,6 +66,7 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] + [Ignore("Enable when first run setup is being displayed on first run.")] public void TestDoesntOpenOnSecondRun() { AddStep("set first run", () => LocalConfig.SetValue(OsuSetting.ShowFirstRunSetup, true)); diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index c4e3626996..dc1ae2be37 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -157,7 +157,8 @@ namespace osu.Game.Overlays config.BindWith(OsuSetting.ShowFirstRunSetup, showFirstRunSetup); - if (showFirstRunSetup.Value) Show(); + // TODO: uncomment when happy with the whole flow. + // if (showFirstRunSetup.Value) Show(); } public override bool OnPressed(KeyBindingPressEvent e) @@ -289,7 +290,8 @@ namespace osu.Game.Overlays } else { - showFirstRunSetup.Value = false; + // TODO: uncomment when happy with the whole flow. + // showFirstRunSetup.Value = false; currentStepIndex = null; Hide(); } From 999b4505d139ae7779958690de8d130b180a09fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 16:37:11 +0900 Subject: [PATCH 0542/1276] Remove localisation of "hide" string to fix incorrect case --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index c6037d1bd6..9772b1feb3 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -244,7 +244,7 @@ namespace osu.Game.Screens.Select.Carousel } if (hideRequested != null) - items.Add(new OsuMenuItem(CommonStrings.ButtonsHide, MenuItemType.Destructive, () => hideRequested(beatmapInfo))); + items.Add(new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested(beatmapInfo))); return items.ToArray(); } From 66b47d22d78bc45d2e890e0aaa7ac565b1fb2e82 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 17:11:25 +0900 Subject: [PATCH 0543/1276] Also fix case of login form username/password --- osu.Game/Overlays/Login/LoginForm.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Login/LoginForm.cs b/osu.Game/Overlays/Login/LoginForm.cs index c31416e078..502f0cd22e 100644 --- a/osu.Game/Overlays/Login/LoginForm.cs +++ b/osu.Game/Overlays/Login/LoginForm.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; @@ -51,14 +52,14 @@ namespace osu.Game.Overlays.Login { username = new OsuTextBox { - PlaceholderText = UsersStrings.LoginUsername, + PlaceholderText = UsersStrings.LoginUsername.ToLower(), RelativeSizeAxes = Axes.X, Text = api?.ProvidedUsername ?? string.Empty, TabbableContentContainer = this }, password = new OsuPasswordTextBox { - PlaceholderText = UsersStrings.LoginPassword, + PlaceholderText = UsersStrings.LoginPassword.ToLower(), RelativeSizeAxes = Axes.X, TabbableContentContainer = this, }, From d92c9056005839fc6410c5de133fb7ee8a9c3533 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 18:15:35 +0900 Subject: [PATCH 0544/1276] Actually use exposed property --- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index d9a612ea26..ef48d9ced5 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.FirstRunSetup new Drawable[] { new SampleScreenContainer(new PinnedMainMenu()), - new SampleScreenContainer(new PlaySongSelect()), + new SampleScreenContainer(new NestedSongSelect()), }, // TODO: add more screens here in the future (gameplay / results) // requires a bit more consideration to isolate their behaviour from the "parent" game. @@ -95,6 +95,11 @@ namespace osu.Game.Overlays.FirstRunSetup } } + private class NestedSongSelect : PlaySongSelect + { + protected override bool ControlGlobalMusic => false; + } + private class PinnedMainMenu : MainMenu { public override void OnEntering(ScreenTransitionEvent e) From 51aa17eb524e70899f09c1058842e0d55f58109b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 22:17:01 +0900 Subject: [PATCH 0545/1276] Fix context menus not working --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 18 +++++++++++++++++- osu.Game/Graphics/UserInterface/OsuMenu.cs | 18 +++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index b1d4691938..20fa7d5148 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -130,7 +131,22 @@ namespace osu.Game.Graphics.UserInterface BackgroundColourSelected = SelectionColour }; - protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); + protected override ScrollContainer CreateScrollContainer(Direction direction) => new DropdownScrollContainer(direction); + + // Hotfix for https://github.com/ppy/osu/issues/17961 + public class DropdownScrollContainer : OsuScrollContainer + { + public DropdownScrollContainer(Direction direction) + : base(direction) + { + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + base.OnMouseDown(e); + return true; + } + } #region DrawableOsuDropdownMenuItem diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index a16adcbd57..bfdfd32fb3 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osuTK; @@ -81,7 +82,22 @@ namespace osu.Game.Graphics.UserInterface return new DrawableOsuMenuItem(item); } - protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); + protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuMenuScrollContainer(direction); + + // Hotfix for https://github.com/ppy/osu/issues/17961 + public class OsuMenuScrollContainer : OsuScrollContainer + { + public OsuMenuScrollContainer(Direction direction) + : base(direction) + { + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + base.OnMouseDown(e); + return true; + } + } protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical) { From 7e9d51f33f31a21f883826a755cf3f39c67b307b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Apr 2022 18:37:35 +0900 Subject: [PATCH 0546/1276] Fix windows taskbar context menu not popping up correctly Closes https://github.com/ppy/osu/issues/17965. Note that this will cause a second osu! icon to appear after running the application (after any update). As per the inline comment, this will eventually be resolved via https://github.com/clowd/Clowd.Squirrel/issues/24. I do think having context menus working is more important than duplicate icons. Note that for anyone who already has a pinned taskbar icon, it will need to be manually unpinned and repinned after a future update to actually fix this issue. Thanks to @caesay for help with investigation. --- osu.Desktop/Program.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index e317a44bc3..eb9045d9ce 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -123,7 +123,12 @@ namespace osu.Desktop tools.RemoveUninstallerRegistryEntry(); }, onEveryRun: (version, tools, firstRun) => { - tools.SetProcessAppUserModelId(); + // While setting the `ProcessAppUserModelId` fixes duplicate icons/shortcuts on the taskbar, it currently + // causes the right-click context menu to function incorrectly. + // + // This may turn out to be non-required after an alternative solution is implemented. + // see https://github.com/clowd/Clowd.Squirrel/issues/24 + // tools.SetProcessAppUserModelId(); }); } From 7ac3ba132ce70a2ba150c6567decde647cb62f3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Apr 2022 18:55:12 +0900 Subject: [PATCH 0547/1276] Remove unused using statements --- .../Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs | 1 - osu.Game/Tests/Visual/OsuGameTestScene.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs index 448d6b1e68..d3aeba2c0f 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Linq; using NUnit.Framework; using osu.Framework.Extensions; diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index 1b8f8bfd07..6e4adb4d4c 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -25,7 +25,6 @@ using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osuTK.Graphics; -using osuTK.Input; using IntroSequence = osu.Game.Configuration.IntroSequence; namespace osu.Game.Tests.Visual From d2780c2c50557b1bdbfc1bec444ae9814dd08e80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Apr 2022 19:06:47 +0900 Subject: [PATCH 0548/1276] Add a touch more detail to the unsupported skin component exception --- osu.Game/Skinning/UnsupportedSkinComponentException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/UnsupportedSkinComponentException.cs b/osu.Game/Skinning/UnsupportedSkinComponentException.cs index 643f29c3db..7f0dd51d5b 100644 --- a/osu.Game/Skinning/UnsupportedSkinComponentException.cs +++ b/osu.Game/Skinning/UnsupportedSkinComponentException.cs @@ -8,7 +8,7 @@ namespace osu.Game.Skinning public class UnsupportedSkinComponentException : Exception { public UnsupportedSkinComponentException(ISkinComponent component) - : base($@"Unsupported component type: {component.GetType()}(""{component.LookupName}"").") + : base($@"Unsupported component type: {component.GetType()} (lookup: ""{component.LookupName}"").") { } } From 5e48b37569a549cd8388985a6efc7b4eca5be72f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Apr 2022 23:58:12 +0900 Subject: [PATCH 0549/1276] Show "Get Started" text on first run button --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index dc1ae2be37..87e62cbddf 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -304,12 +304,15 @@ namespace osu.Game.Overlays BackButton.Enabled.Value = currentStepIndex > 0; NextButton.Enabled.Value = currentStepIndex != null; - if (currentStepIndex != null) - { - NextButton.Text = currentStepIndex + 1 < steps.Length - ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description) - : CommonStrings.Finish; - } + if (currentStepIndex == null) + return; + + if (currentStepIndex == 0) + NextButton.Text = FirstRunSetupOverlayStrings.GetStarted; + else if (currentStepIndex < steps.Length - 1) + NextButton.Text = FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description); + else + NextButton.Text = CommonStrings.Finish; } private class FirstRunStep From ebc8429495c1cde762d2273ed2a7c2fb37476f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Apr 2022 19:13:19 +0200 Subject: [PATCH 0550/1276] Dim offscreen columns & scroll to them if clicked --- osu.Game/Overlays/Mods/ModColumn.cs | 3 + osu.Game/Overlays/Mods/ModSelectScreen.cs | 138 ++++++++++++++++++---- 2 files changed, 115 insertions(+), 26 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 1157c0c0c6..fed2f9aff6 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -53,6 +53,9 @@ namespace osu.Game.Overlays.Mods } public Bindable> SelectedMods = new Bindable>(Array.Empty()); + public Bindable Active = new BindableBool(true); + + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => base.ReceivePositionalInputAtSubTree(screenSpacePos) && Active.Value; protected virtual ModPanel CreateModPanel(Mod mod) => new ModPanel(mod); diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 8a83071109..ca3609aa75 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -13,7 +13,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Layout; +using osu.Framework.Utils; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; @@ -59,7 +61,8 @@ namespace osu.Game.Overlays.Mods private DifficultyMultiplierDisplay? multiplierDisplay; private ModSettingsArea modSettingsArea = null!; - private FillFlowContainer columnFlow = null!; + private OsuScrollContainer columnScroll = null!; + private ColumnFlowContainer columnFlow = null!; [BackgroundDependencyLoader] private void load() @@ -83,6 +86,9 @@ namespace osu.Game.Overlays.Mods } }); + // initialise the scroll early, as we will want to pass it to its children in the hierarchy initialisation below. + columnScroll = new OsuScrollContainer(Direction.Horizontal); + MainAreaContent.AddRange(new Drawable[] { new Container @@ -95,13 +101,13 @@ namespace osu.Game.Overlays.Mods RelativePositionAxes = Axes.Both, Children = new Drawable[] { - new OsuScrollContainer(Direction.Horizontal) + columnScroll.With(col => { - RelativeSizeAxes = Axes.Both, - Masking = false, - ClampExtension = 100, - ScrollbarOverlapsContent = false, - Child = columnFlow = new ModColumnContainer + col.RelativeSizeAxes = Axes.Both; + col.Masking = false; + col.ClampExtension = 100; + col.ScrollbarOverlapsContent = false; + col.Child = columnFlow = new ColumnFlowContainer { Direction = FillDirection.Horizontal, Shear = new Vector2(SHEAR, 0), @@ -111,14 +117,14 @@ namespace osu.Game.Overlays.Mods Margin = new MarginPadding { Right = 70 }, Children = new[] { - CreateModColumn(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), - CreateModColumn(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), - CreateModColumn(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), - CreateModColumn(ModType.Conversion), - CreateModColumn(ModType.Fun) + createModColumnContent(columnScroll, ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), + createModColumnContent(columnScroll, ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), + createModColumnContent(columnScroll, ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), + createModColumnContent(columnScroll, ModType.Conversion), + createModColumnContent(columnScroll, ModType.Fun) } - } - } + }; + }) } } }); @@ -153,6 +159,13 @@ namespace osu.Game.Overlays.Mods } } + private ColumnDimContainer createModColumnContent(OsuScrollContainer scroll, ModType modType, Key[]? toggleKeys = null) + => new ColumnDimContainer(scroll, CreateModColumn(modType, toggleKeys)) + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y + }; + protected override void LoadComplete() { base.LoadComplete(); @@ -166,7 +179,7 @@ namespace osu.Game.Overlays.Mods updateSelectionFromBindable(); }, true); - foreach (var column in columnFlow) + foreach (var column in columnFlow.Columns) { column.SelectedMods.BindValueChanged(updateBindableFromSelection); } @@ -191,7 +204,7 @@ namespace osu.Game.Overlays.Mods private void updateAvailableMods() { - foreach (var column in columnFlow) + foreach (var column in columnFlow.Columns) column.Filter = isValidMod; } @@ -244,7 +257,7 @@ namespace osu.Game.Overlays.Mods // to synchronise state correctly, updateBindableFromSelection() computes the final mods (including incompatibility rules) and updates SelectedMods, // and this method then runs unconditionally again to make sure the new visual selection accurately reflects the final set of selected mods. // selectionBindableSyncInProgress ensures that mutual infinite recursion does not happen after that unconditional call. - foreach (var column in columnFlow) + foreach (var column in columnFlow.Columns) column.SelectedMods.Value = SelectedMods.Value.Where(mod => mod.Type == column.ModType).ToArray(); } @@ -265,7 +278,7 @@ namespace osu.Game.Overlays.Mods } protected virtual IReadOnlyList ComputeNewModsFromSelection(IEnumerable addedMods, IEnumerable removedMods) - => columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray(); + => columnFlow.Columns.SelectMany(column => column.SelectedMods.Value).ToArray(); protected override void PopIn() { @@ -280,7 +293,8 @@ namespace osu.Game.Overlays.Mods for (int i = 0; i < columnFlow.Count; i++) { - columnFlow[i].TopLevelContent + columnFlow[i].Column + .TopLevelContent .Delay(i * 30) .MoveToY(0, fade_in_duration, Easing.OutQuint) .FadeIn(fade_in_duration, Easing.OutQuint); @@ -301,27 +315,30 @@ namespace osu.Game.Overlays.Mods { const float distance = 700; - columnFlow[i].TopLevelContent + columnFlow[i].Column + .TopLevelContent .MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint) .FadeOut(fade_out_duration, Easing.OutQuint); } } - private class ModColumnContainer : FillFlowContainer + private class ColumnFlowContainer : FillFlowContainer { + public IEnumerable Columns => Children.Select(dimWrapper => dimWrapper.Column); + private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize); - public ModColumnContainer() + public ColumnFlowContainer() { AddLayout(drawSizeLayout); } - public override void Add(ModColumn column) + public override void Add(ColumnDimContainer dimContainer) { - base.Add(column); + base.Add(dimContainer); - Debug.Assert(column != null); - column.Shear = Vector2.Zero; + Debug.Assert(dimContainer != null); + dimContainer.Column.Shear = Vector2.Zero; } protected override void Update() @@ -341,6 +358,75 @@ namespace osu.Game.Overlays.Mods } } + private class ColumnDimContainer : Container + { + public ModColumn Column { get; } + + private readonly ScrollContainer parentScroll; + private readonly Bindable isFullyOnScreen = new BindableBool(true); + + [Resolved] + private OsuColour colours { get; set; } = null!; + + public ColumnDimContainer(ScrollContainer parentScroll, ModColumn column) + { + this.parentScroll = parentScroll; + Child = Column = column; + column.Active.BindTo(isFullyOnScreen); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + isFullyOnScreen.BindValueChanged(_ => updateDim(), true); + FinishTransforms(); + } + + private void updateDim() + { + Colour4 targetColour; + + if (isFullyOnScreen.Value) + targetColour = Colour4.White; + else + targetColour = IsHovered ? colours.GrayC : colours.Gray8; + + this.FadeColour(targetColour, 300, Easing.OutQuint); + } + + protected override void Update() + { + float leftX = DrawPosition.X; + // DrawWidth does not include shear effects, and we want to know the full extents of the columns post-shear, + // so we have to manually compensate. + float rightX = DrawPosition.X + DrawWidth + DrawHeight * SHEAR; + + isFullyOnScreen.Value = Precision.AlmostBigger(leftX, parentScroll.Current) + && Precision.DefinitelyBigger(parentScroll.Current + parentScroll.DrawWidth, rightX); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (!isFullyOnScreen.Value) + parentScroll.ScrollTo(DrawPosition.X - 70); + + return true; + } + + protected override bool OnHover(HoverEvent e) + { + base.OnHover(e); + updateDim(); + return isFullyOnScreen.Value; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + updateDim(); + } + } + private class ClickToReturnContainer : Container { public BindableBool HandleMouse { get; } = new BindableBool(); From 09c08248c4a54bd89582247c3dfee500186b2908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Apr 2022 19:20:50 +0200 Subject: [PATCH 0551/1276] Fix bounce when scrolling to first/last column --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index ca3609aa75..f12f55a3a7 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -408,7 +408,7 @@ namespace osu.Game.Overlays.Mods protected override bool OnMouseDown(MouseDownEvent e) { if (!isFullyOnScreen.Value) - parentScroll.ScrollTo(DrawPosition.X - 70); + parentScroll.ScrollTo(Math.Clamp(DrawPosition.X - 70, 0, parentScroll.ScrollableExtent)); return true; } From e384e074fb9243521974d6af45051e8567aa6c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Apr 2022 19:27:27 +0200 Subject: [PATCH 0552/1276] Fix asymmetric margin of column scroll --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index f12f55a3a7..324fc68885 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -114,7 +114,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Spacing = new Vector2(10, 0), - Margin = new MarginPadding { Right = 70 }, + Margin = new MarginPadding { Horizontal = 70 }, Children = new[] { createModColumnContent(columnScroll, ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), From e13d0d02ae7db2a6808c2d1834de6f19261a80d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Apr 2022 19:27:51 +0200 Subject: [PATCH 0553/1276] Use better way of calculating whether columns are fully on screen --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 324fc68885..11cffe7959 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -396,13 +396,13 @@ namespace osu.Game.Overlays.Mods protected override void Update() { - float leftX = DrawPosition.X; - // DrawWidth does not include shear effects, and we want to know the full extents of the columns post-shear, + // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, // so we have to manually compensate. - float rightX = DrawPosition.X + DrawWidth + DrawHeight * SHEAR; + var topLeft = ToSpaceOfOtherDrawable(new Vector2(-DrawHeight * SHEAR, 0), parentScroll); + var topRight = ToSpaceOfOtherDrawable(new Vector2(DrawWidth, 0), parentScroll); - isFullyOnScreen.Value = Precision.AlmostBigger(leftX, parentScroll.Current) - && Precision.DefinitelyBigger(parentScroll.Current + parentScroll.DrawWidth, rightX); + isFullyOnScreen.Value = Precision.AlmostBigger(topLeft.X, 0) + && Precision.DefinitelyBigger(parentScroll.DrawWidth, topRight.X); } protected override bool OnMouseDown(MouseDownEvent e) From 24a1eb8003c4bcddce3ea0e076d09c15a8bfe6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Apr 2022 23:41:40 +0200 Subject: [PATCH 0554/1276] Add test coverage for column dimming behaviour --- .../UserInterface/TestSceneModSelectScreen.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 514538161e..d7ae131372 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -111,6 +111,35 @@ namespace osu.Game.Tests.Visual.UserInterface && SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModMirror))); } + [Test] + public void TestDimmedState() + { + createScreen(); + changeRuleset(0); + + AddUntilStep("any column dimmed", () => this.ChildrenOfType().Any(column => !column.Active.Value)); + + ModColumn firstDimmed = null; + ModPanel firstPanel = null; + + AddStep("click first panel on dimmed column", () => + { + firstDimmed = this.ChildrenOfType().First(column => !column.Active.Value); + firstPanel = firstDimmed.ChildrenOfType().First(); + InputManager.MoveMouseTo(firstPanel); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("column undimmed", () => firstDimmed.Active.Value); + AddAssert("panel not selected", () => !firstPanel.Active.Value); + + AddStep("click panel again", () => + { + InputManager.MoveMouseTo(firstPanel); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("panel selected", () => firstPanel.Active.Value); + } + [Test] public void TestCustomisationToggleState() { From f958010d29ae5040c37b89e88f00fc29e1123d12 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Apr 2022 04:52:48 +0300 Subject: [PATCH 0555/1276] Fix `AccuracyCircle` storing accuracy values in `float` --- .../Ranking/Expanded/Accuracy/AccuracyCircle.cs | 12 ++++++------ .../Screens/Ranking/Expanded/Accuracy/RankBadge.cs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index e50520e0ca..b9248bd67e 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -212,12 +212,12 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy Padding = new MarginPadding { Vertical = -15, Horizontal = -20 }, Children = new[] { - new RankBadge(1f, getRank(ScoreRank.X)), - new RankBadge(0.95f, getRank(ScoreRank.S)), - new RankBadge(0.9f, getRank(ScoreRank.A)), - new RankBadge(0.8f, getRank(ScoreRank.B)), - new RankBadge(0.7f, getRank(ScoreRank.C)), - new RankBadge(0.35f, getRank(ScoreRank.D)), + new RankBadge(1, getRank(ScoreRank.X)), + new RankBadge(0.95, getRank(ScoreRank.S)), + new RankBadge(0.9, getRank(ScoreRank.A)), + new RankBadge(0.8, getRank(ScoreRank.B)), + new RankBadge(0.7, getRank(ScoreRank.C)), + new RankBadge(0.35, getRank(ScoreRank.D)), } }, rankText = new RankText(score.Rank) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs index 76cd408daa..d0b79aa4c7 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// /// The accuracy value corresponding to the displayed by this badge. /// - public readonly float Accuracy; + public readonly double Accuracy; private readonly ScoreRank rank; @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// /// The accuracy value corresponding to . /// The to be displayed in this . - public RankBadge(float accuracy, ScoreRank rank) + public RankBadge(double accuracy, ScoreRank rank) { Accuracy = accuracy; this.rank = rank; @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy base.Update(); // Starts at -90deg (top) and moves counter-clockwise by the accuracy - rankContainer.Position = circlePosition(-MathF.PI / 2 - (1 - Accuracy) * MathF.PI * 2); + rankContainer.Position = circlePosition(-MathF.PI / 2 - (1 - (float)Accuracy) * MathF.PI * 2); } private Vector2 circlePosition(float t) From 7d3249019887d1ee8ce62f61c7e17d92e55908e0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Apr 2022 04:55:27 +0300 Subject: [PATCH 0556/1276] Change score accuracy grading logic to be inclusive --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 1e268bb2eb..1dd1d1aeb6 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -375,13 +375,13 @@ namespace osu.Game.Rulesets.Scoring { if (acc == 1) return ScoreRank.X; - if (acc > 0.95) + if (acc >= 0.95) return ScoreRank.S; - if (acc > 0.9) + if (acc >= 0.9) return ScoreRank.A; - if (acc > 0.8) + if (acc >= 0.8) return ScoreRank.B; - if (acc > 0.7) + if (acc >= 0.7) return ScoreRank.C; return ScoreRank.D; From d310886c26e7d971e41206cbed26d0483452b958 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 12:02:38 +0900 Subject: [PATCH 0557/1276] Add test coverage of `IsPlaying` state during fail and pause --- osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs | 2 ++ osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs index 79d7bb366d..bf491db45a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs @@ -21,7 +21,9 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void AddCheckSteps() { + AddUntilStep("player is playing", () => Player.LocalUserPlaying.Value); AddUntilStep("wait for fail", () => Player.GameplayState.HasFailed); + AddAssert("player is not playing", () => !Player.LocalUserPlaying.Value); AddUntilStep("wait for multiple judgements", () => ((FailPlayer)Player).ScoreProcessor.JudgedHits > 1); AddAssert("total number of results == 1", () => { diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index a224a78531..ab5d766609 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -85,7 +85,10 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10))); pauseAndConfirm(); + AddAssert("player not playing", () => !Player.LocalUserPlaying.Value); + resumeAndConfirm(); + AddUntilStep("player playing", () => Player.LocalUserPlaying.Value); } [Test] From 8ab3636f87b434878a4d2bb536ce9a0f1d7287d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 11:19:19 +0900 Subject: [PATCH 0558/1276] Change conditions for HUD being shown to include pause/fail states As proposed in https://github.com/ppy/osu/discussions/17975. Feels pretty good to me, and probably how it should have been implemented from the start. --- osu.Game/Screens/Play/HUDOverlay.cs | 6 +++--- osu.Game/Screens/Play/Player.cs | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 676a08b434..abfed1acd0 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Play private readonly FillFlowContainer bottomRightElements; private readonly FillFlowContainer topRightElements; - internal readonly IBindable IsBreakTime = new Bindable(); + internal readonly IBindable IsPlaying = new Bindable(); private bool holdingForHUD; @@ -152,7 +152,7 @@ namespace osu.Game.Screens.Play ShowHud.BindValueChanged(visible => hideTargets.ForEach(d => d.FadeTo(visible.NewValue ? 1 : 0, FADE_DURATION, FADE_EASING))); - IsBreakTime.BindValueChanged(_ => updateVisibility()); + IsPlaying.BindValueChanged(_ => updateVisibility()); configVisibilityMode.BindValueChanged(_ => updateVisibility(), true); replayLoaded.BindValueChanged(replayLoadedValueChanged, true); @@ -218,7 +218,7 @@ namespace osu.Game.Screens.Play case HUDVisibilityMode.HideDuringGameplay: // always show during replay as we want the seek bar to be visible. - ShowHud.Value = replayLoaded.Value || IsBreakTime.Value; + ShowHud.Value = replayLoaded.Value || !IsPlaying.Value; break; case HUDVisibilityMode.Always: diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2d5a67758a..ae3eb1ed8b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -457,7 +457,7 @@ namespace osu.Game.Screens.Play private void updateGameplayState() { - bool inGameplay = !DrawableRuleset.HasReplayLoaded.Value && !DrawableRuleset.IsPaused.Value && !breakTracker.IsBreakTime.Value; + bool inGameplay = !DrawableRuleset.HasReplayLoaded.Value && !DrawableRuleset.IsPaused.Value && !breakTracker.IsBreakTime.Value && !GameplayState.HasFailed; OverlayActivationMode.Value = inGameplay ? OverlayActivation.Disabled : OverlayActivation.UserTriggered; localUserPlaying.Value = inGameplay; } @@ -812,6 +812,8 @@ namespace osu.Game.Screens.Play GameplayState.HasFailed = true; Score.ScoreInfo.Passed = false; + updateGameplayState(); + // There is a chance that we could be in a paused state as the ruleset's internal clock (see FrameStabilityContainer) // could process an extra frame after the GameplayClock is stopped. // In such cases we want the fail state to precede a user triggered pause. @@ -945,7 +947,7 @@ namespace osu.Game.Screens.Play failAnimationLayer.Background = b; }); - HUDOverlay.IsBreakTime.BindTo(breakTracker.IsBreakTime); + HUDOverlay.IsPlaying.BindTo(localUserPlaying); DimmableStoryboard.IsBreakTime.BindTo(breakTracker.IsBreakTime); DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); From b41e273086df007bb12b1521df7e31cd6bc53a04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 15:06:27 +0900 Subject: [PATCH 0559/1276] Convert function weirdness to property + bool --- .../UI/OsuSettingsSubsection.cs | 2 +- .../Sections/Gameplay/AudioSettings.cs | 2 +- .../Sections/Gameplay/GeneralSettings.cs | 2 +- .../Settings/Sections/Gameplay/HUDSettings.cs | 2 +- .../Sections/UserInterface/GeneralSettings.cs | 2 +- .../UserInterface/SongSelectSettings.cs | 2 +- osu.Game/Overlays/Settings/SettingsItem.cs | 29 +++++++++++-------- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index 33db74c530..a638019e69 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.UI }, new SettingsCheckbox { - GetClassicDefault = () => false, + ClassicDefault = false, LabelText = "Snaking out sliders", Current = config.GetBindable(OsuRulesetSetting.SnakingOutSliders) }, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs index 6978c77aad..e2e00813bd 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { - GetClassicDefault = () => false, + ClassicDefault = false, LabelText = GameplaySettingsStrings.AlwaysPlayFirstComboBreak, Current = config.GetBindable(OsuSetting.AlwaysPlayFirstComboBreak) } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 3ca677ef7b..5231ce1211 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { new SettingsEnumDropdown { - GetClassicDefault = () => ScoringMode.Classic, + ClassicDefault = ScoringMode.Classic, LabelText = GameplaySettingsStrings.ScoreDisplayMode, Current = config.GetBindable(OsuSetting.ScoreDisplayMode), Keywords = new[] { "scoring" } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs index 285cdfa3f8..829977e9b6 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { - GetClassicDefault = () => false, + ClassicDefault = false, LabelText = GameplaySettingsStrings.ShowHealthDisplayWhenCantFail, Current = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail), Keywords = new[] { "hp", "bar" } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 06f2c9794c..7fc049915e 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface }, new SettingsSlider { - GetClassicDefault = () => 0, + ClassicDefault = 0, LabelText = UserInterfaceStrings.HoldToConfirmActivationTime, Current = config.GetBindable(OsuSetting.UIHoldActivationDelay), Keywords = new[] { @"delay" }, diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs index 213234db4f..b91b5c5243 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { new SettingsCheckbox { - GetClassicDefault = () => true, + ClassicDefault = true, LabelText = UserInterfaceStrings.RightMouseScroll, Current = config.GetBindable(OsuSetting.SongSelectRightMouseScroll), }, diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 845a7f5cdf..0b9a553c2c 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -107,10 +107,8 @@ namespace osu.Game.Overlays.Settings LabelText.ToString() }; - if (GetClassicDefault != null) - { + if (hasClassicDefault) keywords.Add(CLASSIC_DEFAULT_SEARCH_TERM); - } return keywords; } @@ -126,20 +124,27 @@ namespace osu.Game.Overlays.Settings public event Action SettingChanged; + private T classicDefault; + private bool hasClassicDefault; + /// - /// An action which when invoked will apply a classic default value to this setting. + /// A "classic" default value for this setting. /// - public Func GetClassicDefault { get; set; } + public T ClassicDefault + { + set + { + classicDefault = value; + hasClassicDefault = true; + } + } public void ApplyClassicDefault(bool useClassicDefault) { - if (GetClassicDefault != null) - { - if (useClassicDefault) - Current.Value = GetClassicDefault(); - else - Current.SetDefault(); - } + if (hasClassicDefault && useClassicDefault) + Current.Value = classicDefault; + else + Current.SetDefault(); } protected SettingsItem() From 71aa7c813e24fe7636b7c938cd2e9ada83038240 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 15:33:39 +0900 Subject: [PATCH 0560/1276] Fix regressed logic for applying classic defaults --- osu.Game/Overlays/Settings/SettingsItem.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 0b9a553c2c..db9fbfcbb4 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -141,7 +141,10 @@ namespace osu.Game.Overlays.Settings public void ApplyClassicDefault(bool useClassicDefault) { - if (hasClassicDefault && useClassicDefault) + if (!hasClassicDefault) + return; + + if (useClassicDefault) Current.Value = classicDefault; else Current.SetDefault(); From 37ae39f5feb847926d6842e795523977268326a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 15:48:40 +0900 Subject: [PATCH 0561/1276] Move "Next" localisation to common strings --- osu.Game/Localisation/CommonStrings.cs | 5 +++++ osu.Game/Localisation/FirstRunSetupOverlayStrings.cs | 5 ----- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 6da2e4d272..52e9811cf7 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -14,6 +14,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Back => new TranslatableString(getKey(@"back"), @"Back"); + /// + /// "Next" + /// + public static LocalisableString Next => new TranslatableString(getKey(@"next"), @"Next"); + /// /// "Finish" /// diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs index 001de93c16..ebce9e1ce1 100644 --- a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -48,11 +48,6 @@ osu! is a very configurable game, and diving straight into the settings can some /// public static LocalisableString UIScaleDescription => new TranslatableString(getKey(@"ui_scale_description"), @"The size of the osu! user interface can be adjusted to your liking."); - /// - /// "Next ({0})" - /// - public static LocalisableString Next(LocalisableString nextStepDescription) => new TranslatableString(getKey(@"next"), @"Next ({0})", nextStepDescription); - private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 87e62cbddf..5700fa3763 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -310,7 +310,7 @@ namespace osu.Game.Overlays if (currentStepIndex == 0) NextButton.Text = FirstRunSetupOverlayStrings.GetStarted; else if (currentStepIndex < steps.Length - 1) - NextButton.Text = FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description); + NextButton.Text = new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Next, steps[currentStepIndex.Value + 1].Description); else NextButton.Text = CommonStrings.Finish; } From 2c836e2d6312fc0a162f322163385c484cb19a94 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 15:55:32 +0900 Subject: [PATCH 0562/1276] Fix scrollbar overlapping content --- osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index eb4b97069c..8fcfdbf689 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -22,6 +22,7 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuScrollContainer(Direction.Vertical) { RelativeSizeAxes = Axes.Both, + ScrollbarOverlapsContent = false, Child = Content = new FillFlowContainer { Spacing = new Vector2(20), From a078440012482f4b575f74eab6dbee0484c3541f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 16:03:15 +0900 Subject: [PATCH 0563/1276] Show last step on back button as well --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 5700fa3763..27a057bf09 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -133,7 +133,7 @@ namespace osu.Game.Overlays { BackButton = new DangerousTriangleButton { - Width = 200, + Width = 300, Text = CommonStrings.Back, Action = showPreviousStep, Enabled = { Value = false }, @@ -307,12 +307,22 @@ namespace osu.Game.Overlays if (currentStepIndex == null) return; - if (currentStepIndex == 0) + bool isFirstStep = currentStepIndex == 0; + bool isLastStep = currentStepIndex == steps.Length - 1; + + if (isFirstStep) + { + BackButton.Text = CommonStrings.Back; NextButton.Text = FirstRunSetupOverlayStrings.GetStarted; - else if (currentStepIndex < steps.Length - 1) - NextButton.Text = new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Next, steps[currentStepIndex.Value + 1].Description); + } else - NextButton.Text = CommonStrings.Finish; + { + BackButton.Text = new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Back, steps[currentStepIndex.Value - 1].Description); + + NextButton.Text = isLastStep + ? CommonStrings.Finish + : new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Next, steps[currentStepIndex.Value + 1].Description); + } } private class FirstRunStep From 01fdb771ee4844ddd724548f1188bbdcd4705241 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 16:22:41 +0900 Subject: [PATCH 0564/1276] Add header text to first run setup screens --- .../FirstRunSetup/FirstRunSetupScreen.cs | 30 +++++++++++++++---- .../Overlays/FirstRunSetup/ScreenUIScale.cs | 1 + .../Overlays/FirstRunSetup/ScreenWelcome.cs | 6 +++- osu.Game/Overlays/FirstRunSetupOverlay.cs | 24 ++++----------- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index eb4b97069c..282ba52ddc 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -1,10 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osuTK; namespace osu.Game.Overlays.FirstRunSetup @@ -15,19 +19,33 @@ namespace osu.Game.Overlays.FirstRunSetup protected FillFlowContainer Content { get; private set; } - protected FirstRunSetupScreen() + [BackgroundDependencyLoader] + private void load(OverlayColourProvider overlayColourProvider) { + const float header_size = 40; + const float spacing = 20; + InternalChildren = new Drawable[] { new OsuScrollContainer(Direction.Vertical) { RelativeSizeAxes = Axes.Both, - Child = Content = new FillFlowContainer + Children = new Drawable[] { - Spacing = new Vector2(20), - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, + new OsuSpriteText + { + Text = this.GetLocalisableDescription(), + Font = OsuFont.Default.With(size: header_size), + Colour = overlayColourProvider.Light1, + }, + Content = new FillFlowContainer + { + Y = header_size + spacing, + Spacing = new Vector2(spacing), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + } }, } }; diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index ef48d9ced5..1bd82f6d99 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -27,6 +27,7 @@ using osuTK; namespace osu.Game.Overlays.FirstRunSetup { + [LocalisableDescription(typeof(GraphicsSettingsStrings), nameof(GraphicsSettingsStrings.UIScaling))] public class ScreenUIScale : FirstRunSetupScreen { [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs index 39da180f40..10e15a7555 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -1,16 +1,20 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Localisation; namespace osu.Game.Overlays.FirstRunSetup { + [LocalisableDescription(typeof(FirstRunSetupOverlayStrings), nameof(FirstRunSetupOverlayStrings.WelcomeTitle))] public class ScreenWelcome : FirstRunSetupScreen { - public ScreenWelcome() + [BackgroundDependencyLoader] + private void load() { Content.Children = new Drawable[] { diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index dc1ae2be37..7d5f4acee7 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -7,12 +7,12 @@ using System; using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Configuration; using osu.Game.Graphics; @@ -56,10 +56,10 @@ namespace osu.Game.Overlays /// public FirstRunSetupScreen? CurrentScreen => (FirstRunSetupScreen?)stack?.CurrentScreen; - private readonly FirstRunStep[] steps = + private readonly Type[] steps = { - new FirstRunStep(typeof(ScreenWelcome), FirstRunSetupOverlayStrings.WelcomeTitle), - new FirstRunStep(typeof(ScreenUIScale), GraphicsSettingsStrings.UIScaling), + typeof(ScreenWelcome), + typeof(ScreenUIScale) }; private Container stackContainer = null!; @@ -286,7 +286,7 @@ namespace osu.Game.Overlays if (currentStepIndex < steps.Length) { - stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value].ScreenType)); + stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value])); } else { @@ -307,21 +307,9 @@ namespace osu.Game.Overlays if (currentStepIndex != null) { NextButton.Text = currentStepIndex + 1 < steps.Length - ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description) + ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].GetLocalisableDescription()) : CommonStrings.Finish; } } - - private class FirstRunStep - { - public readonly Type ScreenType; - public readonly LocalisableString Description; - - public FirstRunStep(Type screenType, LocalisableString description) - { - ScreenType = screenType; - Description = description; - } - } } } From 280cd048f662090d5bdde09191daa9ff978190dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 17:30:28 +0900 Subject: [PATCH 0565/1276] Fix joystick settings changing enabled state of config level bindables Just copied what we had in place for the mouse sensitivity setting. For tablet settings I believe I avoided this by just hiding the settings when not enabled. Might be a better way forward, but this is simplest for now. Run `TestSceneSettingsPanel` twice to get a crash without this change. --- .../Sections/Input/JoystickSettings.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs index c136ca6a19..60849cd6d4 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -20,6 +20,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input private SettingsSlider deadzoneSlider; + private Bindable handlerDeadzone; + + private Bindable localDeadzone; + public JoystickSettings(JoystickHandler joystickHandler) { this.joystickHandler = joystickHandler; @@ -28,6 +32,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input [BackgroundDependencyLoader] private void load() { + // use local bindable to avoid changing enabled state of game host's bindable. + handlerDeadzone = joystickHandler.DeadzoneThreshold.GetBoundCopy(); + localDeadzone = handlerDeadzone.GetUnboundCopy(); + Children = new Drawable[] { new SettingsCheckbox @@ -40,7 +48,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = JoystickSettingsStrings.DeadzoneThreshold, KeyboardStep = 0.01f, DisplayAsPercentage = true, - Current = joystickHandler.DeadzoneThreshold, + Current = localDeadzone, }, }; } @@ -51,6 +59,17 @@ namespace osu.Game.Overlays.Settings.Sections.Input enabled.BindTo(joystickHandler.Enabled); enabled.BindValueChanged(e => deadzoneSlider.Current.Disabled = !e.NewValue, true); + + handlerDeadzone.BindValueChanged(val => + { + bool disabled = localDeadzone.Disabled; + + localDeadzone.Disabled = false; + localDeadzone.Value = val.NewValue; + localDeadzone.Disabled = disabled; + }, true); + + localDeadzone.BindValueChanged(val => handlerDeadzone.Value = val.NewValue); } } } From c52c08fe9785e35735ae1dcacd4db5cbe4463bc6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 16:41:50 +0900 Subject: [PATCH 0566/1276] Rename test to increase scope --- ...stSceneShearedToggleButton.cs => TestSceneShearedButtons.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/UserInterface/{TestSceneShearedToggleButton.cs => TestSceneShearedButtons.cs} (97%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs similarity index 97% rename from osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs index b5109aa58d..ff83547087 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedToggleButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs @@ -11,7 +11,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneShearedToggleButton : OsuManualInputManagerTestScene + public class TestSceneShearedButtons : OsuManualInputManagerTestScene { [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); From 9f33e627de61afbe107efc6557c48c8d06331572 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 17:31:55 +0900 Subject: [PATCH 0567/1276] Split out `ShearedButton` from `ShearedToggleButton` --- .../Graphics/UserInterface/ShearedButton.cs | 174 ++++++++++++++++++ .../UserInterface/ShearedToggleButton.cs | 131 ++----------- 2 files changed, 190 insertions(+), 115 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/ShearedButton.cs diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs new file mode 100644 index 0000000000..5114e15e2e --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -0,0 +1,174 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Graphics.UserInterface +{ + public class ShearedButton : OsuClickableContainer + { + public LocalisableString Text + { + get => text.Text; + set => text.Text = value; + } + + public Colour4 DarkerColour + { + set + { + darkerColour = value; + Scheduler.AddOnce(updateState); + } + } + + public Colour4 LighterColour + { + set + { + lighterColour = value; + Scheduler.AddOnce(updateState); + } + } + + public Colour4 TextColour + { + set + { + textColour = value; + Scheduler.AddOnce(updateState); + } + } + + [Resolved] + protected OverlayColourProvider ColourProvider { get; private set; } = null!; + + private readonly Box background; + private readonly OsuSpriteText text; + + private const float shear = 0.2f; + + private Colour4? darkerColour; + private Colour4? lighterColour; + private Colour4? textColour; + + /// + /// Creates a new + /// + /// + /// The width of the button. + /// + /// If a non- value is provided, this button will have a fixed width equal to the provided value. + /// If a value is provided (or the argument is omitted entirely), the button will autosize in width to fit the text. + /// + /// + public ShearedButton(float? width = null) + { + Height = 50; + Padding = new MarginPadding { Horizontal = shear * 50 }; + + Content.CornerRadius = 7; + Content.Shear = new Vector2(shear, 0); + Content.Masking = true; + Content.BorderThickness = 2; + Content.Anchor = Content.Origin = Anchor.Centre; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.TorusAlternate.With(size: 17), + Shear = new Vector2(-shear, 0) + } + }; + + if (width != null) + { + Width = width.Value; + } + else + { + AutoSizeAxes = Axes.X; + text.Margin = new MarginPadding { Horizontal = 15 }; + } + } + + protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet); + + protected override void LoadComplete() + { + base.LoadComplete(); + + Enabled.BindValueChanged(_ => Scheduler.AddOnce(updateState)); + + updateState(); + FinishTransforms(true); + } + + protected override bool OnHover(HoverEvent e) + { + Scheduler.AddOnce(updateState); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + Scheduler.AddOnce(updateState); + base.OnHoverLost(e); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + Content.ScaleTo(0.8f, 2000, Easing.OutQuint); + return base.OnMouseDown(e); + } + + protected override void OnMouseUp(MouseUpEvent e) + { + Content.ScaleTo(1, 1000, Easing.OutElastic); + base.OnMouseUp(e); + } + + private void updateState() + { + var colourDark = darkerColour ?? ColourProvider.Background3; + var colourLight = lighterColour ?? ColourProvider.Background1; + var colourText = textColour ?? ColourProvider.Content1; + + if (!Enabled.Value) + { + colourDark = colourDark.Darken(0.3f); + colourLight = colourLight.Darken(0.3f); + } + else if (IsHovered) + { + colourDark = colourDark.Lighten(0.3f); + colourLight = colourLight.Lighten(0.3f); + } + + background.FadeColour(colourDark, 150, Easing.OutQuint); + Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(colourDark, colourLight), 150, Easing.OutQuint); + + if (!Enabled.Value) + colourText = colourText.Opacity(0.6f); + + text.FadeColour(colourText, 150, Easing.OutQuint); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs b/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs index aed3be20a0..c7273320fc 100644 --- a/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs @@ -8,39 +8,18 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Framework.Localisation; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; -using osuTK; namespace osu.Game.Graphics.UserInterface { - public class ShearedToggleButton : OsuClickableContainer + public class ShearedToggleButton : ShearedButton { - public BindableBool Active { get; } = new BindableBool(); - - public LocalisableString Text - { - get => text.Text; - set => text.Text = value; - } - - private readonly Box background; - private readonly OsuSpriteText text; - private Sample? sampleOff; private Sample? sampleOn; - private const float shear = 0.2f; - - [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; + /// + /// Whether this button is currently toggled to an active state. + /// + public BindableBool Active { get; } = new BindableBool(); /// /// Creates a new @@ -53,40 +32,8 @@ namespace osu.Game.Graphics.UserInterface /// /// public ShearedToggleButton(float? width = null) + : base(width) { - Height = 50; - Padding = new MarginPadding { Horizontal = shear * 50 }; - - Content.CornerRadius = 7; - Content.Shear = new Vector2(shear, 0); - Content.Masking = true; - Content.BorderThickness = 2; - Content.Anchor = Content.Origin = Anchor.Centre; - - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both - }, - text = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.TorusAlternate.With(size: 17), - Shear = new Vector2(-shear, 0) - } - }; - - if (width != null) - { - Width = width.Value; - } - else - { - AutoSizeAxes = Axes.X; - text.Margin = new MarginPadding { Horizontal = 15 }; - } } [BackgroundDependencyLoader] @@ -100,70 +47,24 @@ namespace osu.Game.Graphics.UserInterface protected override void LoadComplete() { - base.LoadComplete(); - Active.BindValueChanged(_ => { - updateState(); + updateActiveState(); playSample(); }); - Active.BindDisabledChanged(disabled => - { - updateState(); - Action = disabled ? (Action?)null : Active.Toggle; - }, true); - FinishTransforms(true); + updateActiveState(); + + Active.BindDisabledChanged(disabled => Action = disabled ? (Action?)null : Active.Toggle, true); + + base.LoadComplete(); } - protected override bool OnHover(HoverEvent e) + private void updateActiveState() { - updateState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateState(); - base.OnHoverLost(e); - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - Content.ScaleTo(0.8f, 2000, Easing.OutQuint); - return base.OnMouseDown(e); - } - - protected override void OnMouseUp(MouseUpEvent e) - { - Content.ScaleTo(1, 1000, Easing.OutElastic); - base.OnMouseUp(e); - } - - private void updateState() - { - var darkerColour = Active.Value ? colourProvider.Highlight1 : colourProvider.Background3; - var lighterColour = Active.Value ? colourProvider.Colour0 : colourProvider.Background1; - - if (Active.Disabled) - { - darkerColour = darkerColour.Darken(0.3f); - lighterColour = lighterColour.Darken(0.3f); - } - else if (IsHovered) - { - darkerColour = darkerColour.Lighten(0.3f); - lighterColour = lighterColour.Lighten(0.3f); - } - - background.FadeColour(darkerColour, 150, Easing.OutQuint); - Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(darkerColour, lighterColour), 150, Easing.OutQuint); - - var textColour = Active.Value ? colourProvider.Background6 : colourProvider.Content1; - if (Active.Disabled) - textColour = textColour.Opacity(0.6f); - - text.FadeColour(textColour, 150, Easing.OutQuint); + DarkerColour = Active.Value ? ColourProvider.Highlight1 : ColourProvider.Background3; + LighterColour = Active.Value ? ColourProvider.Colour0 : ColourProvider.Background1; + TextColour = Active.Value ? ColourProvider.Background6 : ColourProvider.Content1; } private void playSample() From 4e260e741077cba720196c425a3e130f8bebbc79 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 17:32:20 +0900 Subject: [PATCH 0568/1276] Fix all buttons gamewide not animating properly on click Not sure how or when this happened. --- osu.Game/Graphics/UserInterface/OsuButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 7c1e8d90a0..29a797bd78 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -108,7 +108,7 @@ namespace osu.Game.Graphics.UserInterface if (Enabled.Value) { Debug.Assert(backgroundColour != null); - Background.FlashColour(backgroundColour.Value, 200); + Background.FlashColour(backgroundColour.Value.Lighten(0.4f), 200); } return base.OnClick(e); From 6864623bacac5d1c9ab488c84f29ac88a06cc115 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 17:32:02 +0900 Subject: [PATCH 0569/1276] Add `ShearedButton` test coverage --- .../UserInterface/TestSceneShearedButtons.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs index ff83547087..4540528918 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs @@ -16,6 +16,38 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + [Test] + public void TestShearedButton() + { + ShearedButton button = null; + bool actionFired = false; + + AddStep("create button", () => + { + actionFired = false; + + Child = button = new ShearedButton(200) + { + LighterColour = Colour4.FromHex("#FF86DD"), + DarkerColour = Colour4.FromHex("#DE31AE"), + TextColour = Colour4.White, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Press me", + Height = 80, + Action = () => actionFired = true, + }; + }); + + AddStep("set disabled", () => button.Enabled.Value = false); + AddStep("press button", () => button.TriggerClick()); + AddAssert("action not fired", () => !actionFired); + + AddStep("set enabled", () => button.Enabled.Value = true); + AddStep("press button", () => button.TriggerClick()); + AddAssert("action fired", () => actionFired); + } + [Test] public void TestShearedToggleButton() { From 4bc4ff179a5a3f883af152090c07eacff4b57346 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 18:03:34 +0900 Subject: [PATCH 0570/1276] Add flash effect on click --- .../Graphics/UserInterface/ShearedButton.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index 5114e15e2e..e9120a3b7d 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -62,6 +62,8 @@ namespace osu.Game.Graphics.UserInterface private Colour4? lighterColour; private Colour4? textColour; + private readonly Box flashLayer; + /// /// Creates a new /// @@ -95,7 +97,14 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.Centre, Font = OsuFont.TorusAlternate.With(size: 17), Shear = new Vector2(-shear, 0) - } + }, + flashLayer = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.White.Opacity(0.9f), + Blending = BlendingParameters.Additive, + Alpha = 0, + }, }; if (width != null) @@ -121,6 +130,14 @@ namespace osu.Game.Graphics.UserInterface FinishTransforms(true); } + protected override bool OnClick(ClickEvent e) + { + if (Enabled.Value) + flashLayer.FadeOutFromOne(800, Easing.OutQuint); + + return base.OnClick(e); + } + protected override bool OnHover(HoverEvent e) { Scheduler.AddOnce(updateState); From cd197a938046249950bbf395eb2c55277b2305cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 18:11:03 +0900 Subject: [PATCH 0571/1276] Reduce the hover lightening effect slightly --- osu.Game/Graphics/UserInterface/ShearedButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index e9120a3b7d..f995da9e4b 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -175,8 +175,8 @@ namespace osu.Game.Graphics.UserInterface } else if (IsHovered) { - colourDark = colourDark.Lighten(0.3f); - colourLight = colourLight.Lighten(0.3f); + colourDark = colourDark.Lighten(0.2f); + colourLight = colourLight.Lighten(0.2f); } background.FadeColour(colourDark, 150, Easing.OutQuint); From 52a8f3848d5989e38615bbbaaa8e1c5d892199fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 18:14:57 +0900 Subject: [PATCH 0572/1276] Expose font size and add big button test --- .../UserInterface/TestSceneShearedButtons.cs | 43 +++++++++++++------ .../Graphics/UserInterface/ShearedButton.cs | 6 +++ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs index 4540528918..5a4eeef4d9 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs @@ -16,8 +16,9 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); - [Test] - public void TestShearedButton() + [TestCase(false)] + [TestCase(true)] + public void TestShearedButton(bool bigButton) { ShearedButton button = null; bool actionFired = false; @@ -26,17 +27,35 @@ namespace osu.Game.Tests.Visual.UserInterface { actionFired = false; - Child = button = new ShearedButton(200) + if (bigButton) { - LighterColour = Colour4.FromHex("#FF86DD"), - DarkerColour = Colour4.FromHex("#DE31AE"), - TextColour = Colour4.White, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = "Press me", - Height = 80, - Action = () => actionFired = true, - }; + Child = button = new ShearedButton(400) + { + LighterColour = Colour4.FromHex("#FFFFFF"), + DarkerColour = Colour4.FromHex("#FFCC22"), + TextColour = Colour4.Black, + TextSize = 36, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Let's GO!", + Height = 80, + Action = () => actionFired = true, + }; + } + else + { + Child = button = new ShearedButton(200) + { + LighterColour = Colour4.FromHex("#FF86DD"), + DarkerColour = Colour4.FromHex("#DE31AE"), + TextColour = Colour4.White, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Press me", + Height = 80, + Action = () => actionFired = true, + }; + } }); AddStep("set disabled", () => button.Enabled.Value = false); diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index f995da9e4b..c3c566782f 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -23,6 +23,12 @@ namespace osu.Game.Graphics.UserInterface set => text.Text = value; } + public float TextSize + { + get => text.Font.Size; + set => text.Font = OsuFont.TorusAlternate.With(size: value); + } + public Colour4 DarkerColour { set From 4a370f429eb3d484fc261af50fb53b809dd9a70b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 18:21:46 +0900 Subject: [PATCH 0573/1276] Reorder initialisation of bindable action binding to make more sense --- osu.Game/Graphics/UserInterface/ShearedToggleButton.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs b/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs index c7273320fc..4780270f66 100644 --- a/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedToggleButton.cs @@ -47,6 +47,7 @@ namespace osu.Game.Graphics.UserInterface protected override void LoadComplete() { + Active.BindDisabledChanged(disabled => Action = disabled ? (Action?)null : Active.Toggle, true); Active.BindValueChanged(_ => { updateActiveState(); @@ -54,9 +55,6 @@ namespace osu.Game.Graphics.UserInterface }); updateActiveState(); - - Active.BindDisabledChanged(disabled => Action = disabled ? (Action?)null : Active.Toggle, true); - base.LoadComplete(); } From e646233722e2c24b789217d8d8a087b8b4cb6787 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Apr 2022 19:16:36 +0300 Subject: [PATCH 0574/1276] Use correct translation for "Accuracy" label in break overlay --- osu.Game/Screens/Play/Break/BreakInfo.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Break/BreakInfo.cs b/osu.Game/Screens/Play/Break/BreakInfo.cs index f95e949920..ead41a826a 100644 --- a/osu.Game/Screens/Play/Break/BreakInfo.cs +++ b/osu.Game/Screens/Play/Break/BreakInfo.cs @@ -43,8 +43,7 @@ namespace osu.Game.Screens.Play.Break Direction = FillDirection.Vertical, Children = new Drawable[] { - AccuracyDisplay = new PercentageBreakInfoLine(BeatmapsetsStrings.ShowStatsAccuracy), - + AccuracyDisplay = new PercentageBreakInfoLine(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy), // See https://github.com/ppy/osu/discussions/15185 // RankDisplay = new BreakInfoLine("Rank"), GradeDisplay = new BreakInfoLine("Grade"), From b0d6e16658a17299663ca3451de1b094918429bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 21:57:19 +0200 Subject: [PATCH 0575/1276] Invert data flow in column dim implementation --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 82 ++++++++++++----------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 11cffe7959..ccd160a534 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -86,9 +86,6 @@ namespace osu.Game.Overlays.Mods } }); - // initialise the scroll early, as we will want to pass it to its children in the hierarchy initialisation below. - columnScroll = new OsuScrollContainer(Direction.Horizontal); - MainAreaContent.AddRange(new Drawable[] { new Container @@ -101,13 +98,13 @@ namespace osu.Game.Overlays.Mods RelativePositionAxes = Axes.Both, Children = new Drawable[] { - columnScroll.With(col => + columnScroll = new OsuScrollContainer(Direction.Horizontal) { - col.RelativeSizeAxes = Axes.Both; - col.Masking = false; - col.ClampExtension = 100; - col.ScrollbarOverlapsContent = false; - col.Child = columnFlow = new ColumnFlowContainer + RelativeSizeAxes = Axes.Both, + Masking = false, + ClampExtension = 100, + ScrollbarOverlapsContent = false, + Child = columnFlow = new ColumnFlowContainer { Direction = FillDirection.Horizontal, Shear = new Vector2(SHEAR, 0), @@ -117,14 +114,14 @@ namespace osu.Game.Overlays.Mods Margin = new MarginPadding { Horizontal = 70 }, Children = new[] { - createModColumnContent(columnScroll, ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), - createModColumnContent(columnScroll, ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), - createModColumnContent(columnScroll, ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), - createModColumnContent(columnScroll, ModType.Conversion), - createModColumnContent(columnScroll, ModType.Fun) + createModColumnContent(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), + createModColumnContent(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), + createModColumnContent(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), + createModColumnContent(ModType.Conversion), + createModColumnContent(ModType.Fun) } - }; - }) + } + } } } }); @@ -159,11 +156,12 @@ namespace osu.Game.Overlays.Mods } } - private ColumnDimContainer createModColumnContent(OsuScrollContainer scroll, ModType modType, Key[]? toggleKeys = null) - => new ColumnDimContainer(scroll, CreateModColumn(modType, toggleKeys)) + private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null) + => new ColumnDimContainer(CreateModColumn(modType, toggleKeys)) { AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y + RelativeSizeAxes = Axes.Y, + RequestScroll = column => columnScroll.ScrollTo(Math.Clamp(column.DrawPosition.X - 70, 0, columnScroll.ScrollableExtent)) }; protected override void LoadComplete() @@ -322,6 +320,22 @@ namespace osu.Game.Overlays.Mods } } + protected override void Update() + { + base.Update(); + + foreach (var column in columnFlow) + { + // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, + // so we have to manually compensate. + var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), columnScroll); + var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), columnScroll); + + column.Active.Value = Precision.AlmostBigger(topLeft.X, 0) + && Precision.DefinitelyBigger(columnScroll.DrawWidth, topRight.X); + } + } + private class ColumnFlowContainer : FillFlowContainer { public IEnumerable Columns => Children.Select(dimWrapper => dimWrapper.Column); @@ -362,23 +376,22 @@ namespace osu.Game.Overlays.Mods { public ModColumn Column { get; } - private readonly ScrollContainer parentScroll; - private readonly Bindable isFullyOnScreen = new BindableBool(true); + public readonly Bindable Active = new BindableBool(); + public Action? RequestScroll { get; set; } [Resolved] private OsuColour colours { get; set; } = null!; - public ColumnDimContainer(ScrollContainer parentScroll, ModColumn column) + public ColumnDimContainer(ModColumn column) { - this.parentScroll = parentScroll; Child = Column = column; - column.Active.BindTo(isFullyOnScreen); + column.Active.BindTo(Active); } protected override void LoadComplete() { base.LoadComplete(); - isFullyOnScreen.BindValueChanged(_ => updateDim(), true); + Active.BindValueChanged(_ => updateDim(), true); FinishTransforms(); } @@ -386,7 +399,7 @@ namespace osu.Game.Overlays.Mods { Colour4 targetColour; - if (isFullyOnScreen.Value) + if (Active.Value) targetColour = Colour4.White; else targetColour = IsHovered ? colours.GrayC : colours.Gray8; @@ -394,21 +407,10 @@ namespace osu.Game.Overlays.Mods this.FadeColour(targetColour, 300, Easing.OutQuint); } - protected override void Update() - { - // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, - // so we have to manually compensate. - var topLeft = ToSpaceOfOtherDrawable(new Vector2(-DrawHeight * SHEAR, 0), parentScroll); - var topRight = ToSpaceOfOtherDrawable(new Vector2(DrawWidth, 0), parentScroll); - - isFullyOnScreen.Value = Precision.AlmostBigger(topLeft.X, 0) - && Precision.DefinitelyBigger(parentScroll.DrawWidth, topRight.X); - } - protected override bool OnMouseDown(MouseDownEvent e) { - if (!isFullyOnScreen.Value) - parentScroll.ScrollTo(Math.Clamp(DrawPosition.X - 70, 0, parentScroll.ScrollableExtent)); + if (!Active.Value) + RequestScroll?.Invoke(this); return true; } @@ -417,7 +419,7 @@ namespace osu.Game.Overlays.Mods { base.OnHover(e); updateDim(); - return isFullyOnScreen.Value; + return Active.Value; } protected override void OnHoverLost(HoverLostEvent e) From 13a36f2e5124cb152490017f529beec799165989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 22:01:24 +0200 Subject: [PATCH 0576/1276] Scroll to column when mouse is released rather than pressed --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index ccd160a534..4299bc05f1 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -407,7 +407,7 @@ namespace osu.Game.Overlays.Mods this.FadeColour(targetColour, 300, Easing.OutQuint); } - protected override bool OnMouseDown(MouseDownEvent e) + protected override bool OnClick(ClickEvent e) { if (!Active.Value) RequestScroll?.Invoke(this); From e9c9c764ca457277e39e2d965ef0e11f1a59ce32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 22:15:58 +0200 Subject: [PATCH 0577/1276] Fix column dim state changing when scrolling past extent --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 4299bc05f1..828ed93ca8 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -324,15 +324,22 @@ namespace osu.Game.Overlays.Mods { base.Update(); + // the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space. + // note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns. + float leftScrollBound = Math.Clamp(columnScroll.Current, 0, columnScroll.ScrollableExtent); + float rightScrollBound = leftScrollBound + columnScroll.DrawWidth; + foreach (var column in columnFlow) { // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, // so we have to manually compensate. - var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), columnScroll); - var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), columnScroll); + // additionally note that columnFlow.Parent is not columnScroll, but rather it is the scroll's internal container. + // this is intentional in order to include margin of the columnFlow in calculations correctly and operate in the "scroll internal content space". + var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), columnFlow.Parent); + var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), columnFlow.Parent); - column.Active.Value = Precision.AlmostBigger(topLeft.X, 0) - && Precision.DefinitelyBigger(columnScroll.DrawWidth, topRight.X); + column.Active.Value = Precision.AlmostBigger(topLeft.X, leftScrollBound) + && Precision.DefinitelyBigger(rightScrollBound, topRight.X); } } From a884f20c6541227ef42896b1d042ace7af3e0f6a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Apr 2022 22:19:20 +0300 Subject: [PATCH 0578/1276] Add failing test case --- .../TestSceneDrainingHealthProcessor.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index 296c5cef76..56fb68ea27 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs @@ -160,6 +160,43 @@ namespace osu.Game.Tests.Gameplay assertHealthNotEqualTo(1); } + [Test] + public void TestFailConditions() + { + var beatmap = createBeatmap(0, 1000); + createProcessor(beatmap); + + AddStep("setup fail conditions", () => processor.FailConditions += ((_, result) => result.Type == HitResult.Miss)); + + AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect })); + AddAssert("not failed", () => !processor.HasFailed); + AddStep("apply miss hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Miss })); + AddAssert("failed", () => processor.HasFailed); + } + + [Test] + public void TestMultipleFailConditions([Values] bool applyFirstCondition) + { + var beatmap = createBeatmap(0, 1000); + createProcessor(beatmap); + + AddStep("setup multiple fail conditions", () => + { + processor.FailConditions += ((_, result) => result.Type == HitResult.Miss); + processor.FailConditions += ((_, result) => result.Type == HitResult.Meh); + }); + + AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect })); + AddAssert("not failed", () => !processor.HasFailed); + + if (applyFirstCondition) + AddStep("apply miss hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Miss })); + else + AddStep("apply meh hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Meh })); + + AddAssert("failed", () => processor.HasFailed); + } + [Test] public void TestBonusObjectsExcludedFromDrain() { From 31f64b13819620d1eee947e84dae43220b371081 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Apr 2022 22:49:14 +0300 Subject: [PATCH 0579/1276] Fix `HealthProcessor` fail conditions not handling multiple invocations --- osu.Game/Rulesets/Scoring/HealthProcessor.cs | 32 +++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs index a92c30e593..5e4e40b3bb 100644 --- a/osu.Game/Rulesets/Scoring/HealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs @@ -43,11 +43,11 @@ namespace osu.Game.Rulesets.Scoring Health.Value += GetHealthIncreaseFor(result); - if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true) - return; - - if (Failed?.Invoke() != false) - HasFailed = true; + if (meetsFailConditions(result)) + { + if (Failed?.Invoke() != false) + HasFailed = true; + } } protected override void RevertResultInternal(JudgementResult result) @@ -69,6 +69,28 @@ namespace osu.Game.Rulesets.Scoring /// protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value); + /// + /// Whether the current state of or the provided meets the fail conditions. + /// + /// The judgement result. + private bool meetsFailConditions(JudgementResult result) + { + if (DefaultFailCondition) + return true; + + if (FailConditions != null) + { + foreach (var condition in FailConditions.GetInvocationList()) + { + bool conditionResult = (bool)condition.Method.Invoke(condition.Target, new object[] { this, result }); + if (conditionResult) + return true; + } + } + + return false; + } + protected override void Reset(bool storeResults) { base.Reset(storeResults); From fb6a112708e5b1759fd5625f96d04a2788f5ebde Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Apr 2022 23:09:51 +0300 Subject: [PATCH 0580/1276] Mark `OsuModTarget` and `OsuModSuddenDeath` as mutually exclusive `OsuModTarget` already fails on miss, so "Sudden Death" doesn't make sense to be enabled and may only cause issues. --- osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs | 6 +++++- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs index 0403e81229..429fe30fc5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs @@ -9,6 +9,10 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModSuddenDeath : ModSuddenDeath { - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] + { + typeof(OsuModAutopilot), + typeof(OsuModTarget), + }).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 5285380097..4fab9b6a5a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles) }; + public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSuddenDeath) }; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] public Bindable Seed { get; } = new Bindable From 94d07e147f565fa89552b27d2ba89a2acda6c5ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 22:35:18 +0200 Subject: [PATCH 0581/1276] Move dimming logic to custom scroll container --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 44 +++++++++++++---------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 828ed93ca8..3aaec5edfd 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Mods private DifficultyMultiplierDisplay? multiplierDisplay; private ModSettingsArea modSettingsArea = null!; - private OsuScrollContainer columnScroll = null!; + private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; [BackgroundDependencyLoader] @@ -98,7 +98,7 @@ namespace osu.Game.Overlays.Mods RelativePositionAxes = Axes.Both, Children = new Drawable[] { - columnScroll = new OsuScrollContainer(Direction.Horizontal) + columnScroll = new ColumnScrollContainer { RelativeSizeAxes = Axes.Both, Masking = false, @@ -320,26 +320,32 @@ namespace osu.Game.Overlays.Mods } } - protected override void Update() + private class ColumnScrollContainer : OsuScrollContainer { - base.Update(); - - // the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space. - // note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns. - float leftScrollBound = Math.Clamp(columnScroll.Current, 0, columnScroll.ScrollableExtent); - float rightScrollBound = leftScrollBound + columnScroll.DrawWidth; - - foreach (var column in columnFlow) + public ColumnScrollContainer() + : base(Direction.Horizontal) { - // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, - // so we have to manually compensate. - // additionally note that columnFlow.Parent is not columnScroll, but rather it is the scroll's internal container. - // this is intentional in order to include margin of the columnFlow in calculations correctly and operate in the "scroll internal content space". - var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), columnFlow.Parent); - var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), columnFlow.Parent); + } - column.Active.Value = Precision.AlmostBigger(topLeft.X, leftScrollBound) - && Precision.DefinitelyBigger(rightScrollBound, topRight.X); + protected override void Update() + { + base.Update(); + + // the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space. + // note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns. + float leftScrollBound = Math.Clamp(Current, 0, ScrollableExtent); + float rightScrollBound = leftScrollBound + DrawWidth; + + foreach (var column in Child) + { + // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, + // so we have to manually compensate. + var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), ScrollContent); + var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), ScrollContent); + + column.Active.Value = Precision.AlmostBigger(topLeft.X, leftScrollBound) + && Precision.DefinitelyBigger(rightScrollBound, topRight.X); + } } } From 21377d2a4d96f5265043ef4b360c6b2ab5754067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 22:43:58 +0200 Subject: [PATCH 0582/1276] Fix inactive columns flickering on and off mid-scroll --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 3aaec5edfd..d0d487d1ad 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -333,8 +333,13 @@ namespace osu.Game.Overlays.Mods // the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space. // note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns. - float leftScrollBound = Math.Clamp(Current, 0, ScrollableExtent); - float rightScrollBound = leftScrollBound + DrawWidth; + float leftVisibleBound = Math.Clamp(Current, 0, ScrollableExtent); + float rightVisibleBound = leftVisibleBound + DrawWidth; + + // if a movement is occurring at this time, the bounds below represent the full range of columns that the scroll movement will encompass. + // this will be used to ensure that columns do not change state from active to inactive back and forth until they are fully scrolled past. + float leftMovementBound = Math.Min(Current, Target); + float rightMovementBound = Math.Max(Current, Target) + DrawWidth; foreach (var column in Child) { @@ -343,8 +348,12 @@ namespace osu.Game.Overlays.Mods var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), ScrollContent); var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), ScrollContent); - column.Active.Value = Precision.AlmostBigger(topLeft.X, leftScrollBound) - && Precision.DefinitelyBigger(rightScrollBound, topRight.X); + bool isCurrentlyVisible = Precision.AlmostBigger(topLeft.X, leftVisibleBound) + && Precision.DefinitelyBigger(rightVisibleBound, topRight.X); + bool isBeingScrolledToward = Precision.AlmostBigger(topLeft.X, leftMovementBound) + && Precision.DefinitelyBigger(rightMovementBound, topRight.X); + + column.Active.Value = isCurrentlyVisible || isBeingScrolledToward; } } } From 921e8af3b039b7c1f3ee10d23aaea2fcf3807d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 22:54:54 +0200 Subject: [PATCH 0583/1276] Use more lenient column bounds for checking active state --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index d0d487d1ad..76f4a8cdd4 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -345,13 +345,13 @@ namespace osu.Game.Overlays.Mods { // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, // so we have to manually compensate. - var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), ScrollContent); - var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), ScrollContent); + var topLeft = column.ToSpaceOfOtherDrawable(Vector2.Zero, ScrollContent); + var bottomRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth - column.DrawHeight * SHEAR, 0), ScrollContent); bool isCurrentlyVisible = Precision.AlmostBigger(topLeft.X, leftVisibleBound) - && Precision.DefinitelyBigger(rightVisibleBound, topRight.X); + && Precision.DefinitelyBigger(rightVisibleBound, bottomRight.X); bool isBeingScrolledToward = Precision.AlmostBigger(topLeft.X, leftMovementBound) - && Precision.DefinitelyBigger(rightMovementBound, topRight.X); + && Precision.DefinitelyBigger(rightMovementBound, bottomRight.X); column.Active.Value = isCurrentlyVisible || isBeingScrolledToward; } From cccc9d7d39dbde7a876c3b48a60273edb0a03a3a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 27 Apr 2022 00:03:48 +0300 Subject: [PATCH 0584/1276] Rename method to better reflect what it's doing --- osu.Game/Rulesets/Scoring/HealthProcessor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs index 5e4e40b3bb..0f51560476 100644 --- a/osu.Game/Rulesets/Scoring/HealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Scoring Health.Value += GetHealthIncreaseFor(result); - if (meetsFailConditions(result)) + if (meetsAnyFailCondition(result)) { if (Failed?.Invoke() != false) HasFailed = true; @@ -70,10 +70,10 @@ namespace osu.Game.Rulesets.Scoring protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value); /// - /// Whether the current state of or the provided meets the fail conditions. + /// Whether the current state of or the provided meets any fail condition. /// /// The judgement result. - private bool meetsFailConditions(JudgementResult result) + private bool meetsAnyFailCondition(JudgementResult result) { if (DefaultFailCondition) return true; From 1676c2c3f69ad7836e2199986f34f44edab73960 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 27 Apr 2022 00:05:15 +0300 Subject: [PATCH 0585/1276] Change boolean argument to `HitResult` instead --- .../Gameplay/TestSceneDrainingHealthProcessor.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index 56fb68ea27..a7cd567635 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs @@ -174,8 +174,9 @@ namespace osu.Game.Tests.Gameplay AddAssert("failed", () => processor.HasFailed); } - [Test] - public void TestMultipleFailConditions([Values] bool applyFirstCondition) + [TestCase(HitResult.Miss)] + [TestCase(HitResult.Meh)] + public void TestMultipleFailConditions(HitResult resultApplied) { var beatmap = createBeatmap(0, 1000); createProcessor(beatmap); @@ -189,11 +190,7 @@ namespace osu.Game.Tests.Gameplay AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect })); AddAssert("not failed", () => !processor.HasFailed); - if (applyFirstCondition) - AddStep("apply miss hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Miss })); - else - AddStep("apply meh hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Meh })); - + AddStep($"apply {resultApplied.ToString().ToLower()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Miss })); AddAssert("failed", () => processor.HasFailed); } From 8842847b1850e574f95e537fba07684ff9523c23 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 27 Apr 2022 00:05:49 +0300 Subject: [PATCH 0586/1276] Fix wrong result applied --- osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index a7cd567635..a354464a8e 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs @@ -190,7 +190,7 @@ namespace osu.Game.Tests.Gameplay AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect })); AddAssert("not failed", () => !processor.HasFailed); - AddStep($"apply {resultApplied.ToString().ToLower()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Miss })); + AddStep($"apply {resultApplied.ToString().ToLower()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = resultApplied })); AddAssert("failed", () => processor.HasFailed); } From a849bfcf60523da4b7b69d1ce92325cd75a71f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 23:11:38 +0200 Subject: [PATCH 0587/1276] Rewrite dim test to pass headless Unfortunately neuters the test a touch, but alas. --- .../UserInterface/TestSceneModSelectScreen.cs | 28 +++++++++---------- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index d7ae131372..ec6e962c6a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -119,25 +119,23 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("any column dimmed", () => this.ChildrenOfType().Any(column => !column.Active.Value)); - ModColumn firstDimmed = null; - ModPanel firstPanel = null; + ModColumn lastColumn = null; - AddStep("click first panel on dimmed column", () => + AddAssert("last column dimmed", () => !this.ChildrenOfType().Last().Active.Value); + AddStep("request scroll to last column", () => { - firstDimmed = this.ChildrenOfType().First(column => !column.Active.Value); - firstPanel = firstDimmed.ChildrenOfType().First(); - InputManager.MoveMouseTo(firstPanel); + var lastDimContainer = this.ChildrenOfType().Last(); + lastColumn = lastDimContainer.Column; + lastDimContainer.RequestScroll?.Invoke(lastDimContainer); + }); + AddUntilStep("column undimmed", () => lastColumn.Active.Value); + + AddStep("click panel", () => + { + InputManager.MoveMouseTo(lastColumn.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); - AddUntilStep("column undimmed", () => firstDimmed.Active.Value); - AddAssert("panel not selected", () => !firstPanel.Active.Value); - - AddStep("click panel again", () => - { - InputManager.MoveMouseTo(firstPanel); - InputManager.Click(MouseButton.Left); - }); - AddUntilStep("panel selected", () => firstPanel.Active.Value); + AddUntilStep("panel selected", () => lastColumn.ChildrenOfType().First().Active.Value); } [Test] diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 76f4a8cdd4..e28b46436c 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -394,7 +394,7 @@ namespace osu.Game.Overlays.Mods } } - private class ColumnDimContainer : Container + internal class ColumnDimContainer : Container { public ModColumn Column { get; } From 0343687b85fd00349056b06da0ef3fda63317e4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 15:59:39 +0900 Subject: [PATCH 0588/1276] Make apply default methods more explicit in behaviour --- .../Overlays/FirstRunSetup/ScreenBehaviour.cs | 9 +++++---- osu.Game/Overlays/Settings/ISettingsItem.cs | 15 +++++++++++--- osu.Game/Overlays/Settings/SettingsItem.cs | 20 +++++++++---------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs index 61177c821b..1316d14302 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . 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.Graphics; using osu.Framework.Graphics.Containers; @@ -91,14 +92,14 @@ namespace osu.Game.Overlays.FirstRunSetup private void applyClassic() { - foreach (var i in searchContainer.ChildrenOfType()) - i.ApplyClassicDefault(true); + foreach (var i in searchContainer.ChildrenOfType().Where(s => s.HasClassicDefault)) + i.ApplyClassicDefault(); } private void applyStandard() { - foreach (var i in searchContainer.ChildrenOfType()) - i.ApplyClassicDefault(false); + foreach (var i in searchContainer.ChildrenOfType().Where(s => s.HasClassicDefault)) + i.ApplyDefault(); } } } diff --git a/osu.Game/Overlays/Settings/ISettingsItem.cs b/osu.Game/Overlays/Settings/ISettingsItem.cs index e6b1e68741..61191dcacf 100644 --- a/osu.Game/Overlays/Settings/ISettingsItem.cs +++ b/osu.Game/Overlays/Settings/ISettingsItem.cs @@ -11,9 +11,18 @@ namespace osu.Game.Overlays.Settings event Action SettingChanged; /// - /// Apply the default values of a setting item, if the setting item specifies a "classic" default via . + /// Whether this setting has a classic default (ie. a different default which better aligns with osu-stable expectations). /// - /// Whether to apply the classic value. If false, the standard default is applied. - void ApplyClassicDefault(bool useClassicDefault); + bool HasClassicDefault { get; } + + /// + /// Apply the classic default value of the associated setting. Will throw if is false. + /// + void ApplyClassicDefault(); + + /// + /// Apply the default value of the associated setting. + /// + void ApplyDefault(); } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 3c755ab570..afcd41af22 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.Settings LabelText.ToString() }; - if (hasClassicDefault) + if (HasClassicDefault) keywords.Add(CLASSIC_DEFAULT_SEARCH_TERM); return keywords; @@ -139,7 +139,8 @@ namespace osu.Game.Overlays.Settings public event Action SettingChanged; private T classicDefault; - private bool hasClassicDefault; + + public bool HasClassicDefault { get; private set; } /// /// A "classic" default value for this setting. @@ -149,21 +150,20 @@ namespace osu.Game.Overlays.Settings set { classicDefault = value; - hasClassicDefault = true; + HasClassicDefault = true; } } - public void ApplyClassicDefault(bool useClassicDefault) + public void ApplyClassicDefault() { - if (!hasClassicDefault) - return; + if (!HasClassicDefault) + throw new InvalidOperationException($"Cannot apply a classic default to a setting which doesn't have one defined via {nameof(ClassicDefault)}."); - if (useClassicDefault) - Current.Value = classicDefault; - else - Current.SetDefault(); + Current.Value = classicDefault; } + public void ApplyDefault() => Current.SetDefault(); + protected SettingsItem() { RelativeSizeAxes = Axes.X; From 0354dd5ce623830c0061e5414f59ba0372fe270e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 16:02:39 +0900 Subject: [PATCH 0589/1276] Add comment regarding section initialisation in `ScreenBehaviour` --- osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs | 2 ++ osu.Game/Overlays/SettingsOverlay.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs index 1316d14302..db7659e87a 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs @@ -74,8 +74,10 @@ namespace osu.Game.Overlays.FirstRunSetup AutoSizeAxes = Axes.Y, Children = new SettingsSection[] { + // This list should be kept in sync with SettingsOverlay. new GeneralSection(), new SkinSection(), + // InputSection is intentionally omitted for now due to its sub-panel being a pain to set up. new UserInterfaceSection(), new GameplaySection(), new RulesetSection(), diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index c84cba8189..7cd8fc6d66 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -23,6 +23,7 @@ namespace osu.Game.Overlays protected override IEnumerable CreateSections() => new SettingsSection[] { + // This list should be kept in sync with ScreenBehaviour. new GeneralSection(), new SkinSection(), new InputSection(createSubPanel(new KeyBindingPanel())), From b29af28028a172ed3ad756f5272a2286eecd833c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 16:55:15 +0900 Subject: [PATCH 0590/1276] Fix mod panels not ignoring super key presses Most other usages have this included. Noticed that the panel was changing state when exiting the game using cmd-w. Would probably be nice to have an exposed `HasAnyModifierPressed` helper property. --- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 1157c0c0c6..6a2c2adcba 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -441,7 +441,7 @@ namespace osu.Game.Overlays.Mods protected override bool OnKeyDown(KeyDownEvent e) { - if (e.ControlPressed || e.AltPressed) return false; + if (e.ControlPressed || e.AltPressed || e.SuperPressed) return false; if (toggleKeys == null) return false; int index = Array.IndexOf(toggleKeys, e.Key); From 1a345c06c6c92c20593bb1f413e267498e2f2bfa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 17:10:27 +0900 Subject: [PATCH 0591/1276] Fix regression in nested scroll behaviour due to the top level scroll container's type changing --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 4 ++-- osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index e28b46436c..a47a399a23 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -320,7 +320,7 @@ namespace osu.Game.Overlays.Mods } } - private class ColumnScrollContainer : OsuScrollContainer + internal class ColumnScrollContainer : OsuScrollContainer { public ColumnScrollContainer() : base(Direction.Horizontal) @@ -358,7 +358,7 @@ namespace osu.Game.Overlays.Mods } } - private class ColumnFlowContainer : FillFlowContainer + internal class ColumnFlowContainer : FillFlowContainer { public IEnumerable Columns => Children.Select(dimWrapper => dimWrapper.Column); diff --git a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs index aba47d5423..d27f97f3d2 100644 --- a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs +++ b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs @@ -14,13 +14,13 @@ namespace osu.Game.Overlays.Mods /// public class NestedVerticalScrollContainer : OsuScrollContainer { - private OsuScrollContainer? parentScrollContainer; + private ModSelectScreen.ColumnScrollContainer? parentScrollContainer; protected override void LoadComplete() { base.LoadComplete(); - parentScrollContainer = this.FindClosestParent(); + parentScrollContainer = this.FindClosestParent(); } protected override bool OnScroll(ScrollEvent e) From 7a23363d74ceed68fe84731c84f38e286d0d6f8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 18:10:58 +0900 Subject: [PATCH 0592/1276] Add migration of song progress setting from config to component --- osu.Game/Configuration/OsuConfigManager.cs | 4 ++ osu.Game/Screens/Play/SongProgress.cs | 56 ++++++++++++++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index bee4f914ce..64e77384a2 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -116,6 +116,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.HitLighting, true); SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always); + SetDefault(OsuSetting.ShowProgressGraph, true); SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true); SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true); SetDefault(OsuSetting.KeyOverlay, false); @@ -274,6 +275,9 @@ namespace osu.Game.Configuration AlwaysPlayFirstComboBreak, FloatingComments, HUDVisibilityMode, + + // This has been migrated to the component itself. can be removed 20221027. + ShowProgressGraph, ShowHealthDisplayWhenCantFail, FadePlayfieldWhenHealthLow, MouseDisableButtons, diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 9470c60fe6..592071b02e 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -1,20 +1,20 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using System; using System.Collections.Generic; -using osu.Game.Graphics; -using osu.Framework.Allocation; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Screens.Play { @@ -139,6 +139,52 @@ namespace osu.Game.Screens.Play AllowSeeking.BindValueChanged(_ => updateBarVisibility(), true); ShowGraph.BindValueChanged(_ => updateGraphVisibility(), true); + + migrateSettingFromConfig(); + } + + [Resolved] + private OsuConfigManager config { get; set; } + + [Resolved] + private SkinManager skinManager { get; set; } + + /// + /// This setting has been migrated to a per-component level. + /// Only take the value from the config if it is in a non-default state (then reset it to default so it only applies once). + /// + /// Can be removed 20221027. + /// + private void migrateSettingFromConfig() + { + Bindable configShowGraph = config.GetBindable(OsuSetting.ShowProgressGraph); + + if (!configShowGraph.IsDefault) + { + ShowGraph.Value = configShowGraph.Value; + configShowGraph.SetDefault(); + + // This is pretty ugly, but the only way to make this stick... + if (skinManager != null) + { + var skinnableTarget = this.FindClosestParent(); + + if (skinnableTarget != null) + { + skinManager.EnsureMutableSkin(); + + // If `EnsureMutableSkin` actually changed the skin, default layout may take a frame to apply. + // See `SkinnableTargetComponentsContainer`'s use of ScheduleAfterChildren. + ScheduleAfterChildren(() => + { + var skin = skinManager.CurrentSkin.Value; + skin.UpdateDrawableTarget(skinnableTarget); + + skinManager.Save(skin); + }); + } + } + } } protected override void PopIn() From 20a50ddb6e0639adc5980a4e2c6c85ad2c4d13aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 18:45:40 +0900 Subject: [PATCH 0593/1276] Add missing `OverlayColourProvider` in test scene --- .../Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs index 5ca09b34aa..64ad4ff119 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs @@ -1,13 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Screens; +using osu.Game.Overlays; using osu.Game.Overlays.FirstRunSetup; namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneFirstRunScreenUIScale : OsuManualInputManagerTestScene { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + public TestSceneFirstRunScreenUIScale() { AddStep("load screen", () => From e9ec406046033632a19d062e6b4cd7ae2bd86514 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 19:20:08 +0900 Subject: [PATCH 0594/1276] Remove weird code --- osu.Game/Beatmaps/Drawables/DownloadProgressBar.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DownloadProgressBar.cs b/osu.Game/Beatmaps/Drawables/DownloadProgressBar.cs index 54dcdc55e3..ad0ff876e8 100644 --- a/osu.Game/Beatmaps/Drawables/DownloadProgressBar.cs +++ b/osu.Game/Beatmaps/Drawables/DownloadProgressBar.cs @@ -28,11 +28,6 @@ namespace osu.Game.Beatmaps.Drawables }, downloadTracker = new BeatmapDownloadTracker(beatmapSet), }; - AddInternal(progressBar = new ProgressBar(false) - { - Height = 0, - Alpha = 0, - }); AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; From 66c730c382cdd9ab74f74053e043b385e17586a6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 27 Apr 2022 17:11:23 +0300 Subject: [PATCH 0595/1276] Change distance spacing keybind to Ctrl+Alt --- osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs | 5 ++--- osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs b/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs index 542f98207e..17232db509 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs @@ -12,7 +12,6 @@ using osu.Game.Overlays.Settings.Sections; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit; using osuTK; -using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit { @@ -74,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected override bool OnKeyDown(KeyDownEvent e) { - if (e.AltPressed && e.Key == Key.D && !e.Repeat) + if (e.ControlPressed && e.AltPressed && !e.Repeat) { expandingContainer.Expanded.Value = true; distanceSpacingScrollActive = true; @@ -86,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected override void OnKeyUp(KeyUpEvent e) { - if (distanceSpacingScrollActive && (!e.AltPressed || e.Key == Key.D)) + if (distanceSpacingScrollActive && (!e.AltPressed || !e.ControlPressed)) { expandingContainer.Expanded.Value = false; distanceSpacingScrollActive = false; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index d542732e19..7039d7d8a4 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -95,14 +95,14 @@ namespace osu.Game.Tests.Visual.Editing AddStep("retrieve original spacing", () => originalSpacing = (float)editorBeatmap.BeatmapInfo.DistanceSpacing); + AddStep("hold ctrl", () => InputManager.PressKey(Key.LControl)); AddStep("hold alt", () => InputManager.PressKey(Key.LAlt)); - AddStep("hold D", () => InputManager.PressKey(Key.D)); AddStep("scroll mouse 5 steps", () => InputManager.ScrollVerticalBy(5)); AddAssert("distance spacing increased by 0.5", () => Precision.AlmostEquals(editorBeatmap.BeatmapInfo.DistanceSpacing, originalSpacing + 0.5f)); AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt)); - AddStep("release D", () => InputManager.ReleaseKey(Key.D)); + AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl)); } public class EditorBeatmapContainer : Container From 59cf3ff50fad705e477d8b00c6fcfed55550e387 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 05:48:45 +0300 Subject: [PATCH 0596/1276] Move distancing methods from `IPositionSnapProvider` to `IDistanceSnapProvider` --- .../Editor/TestSceneManiaBeatSnapGrid.cs | 26 ----------- .../Components/PathControlPointVisualiser.cs | 2 +- .../Sliders/SliderPlacementBlueprint.cs | 4 +- .../Sliders/SliderSelectionBlueprint.cs | 6 +-- .../Edit/OsuSelectionHandler.cs | 6 +-- .../Rulesets/Edit/IDistanceSnapProvider.cs | 41 ++++++++++++++++++ .../Rulesets/Edit/IPositionSnapProvider.cs | 43 ------------------- .../Rulesets/Objects/SliderPathExtensions.cs | 2 +- .../Compose/Components/Timeline/Timeline.cs | 13 ------ 9 files changed, 51 insertions(+), 92 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs index 50be13c4e0..2e55f86bb6 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs @@ -13,7 +13,6 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -107,30 +106,5 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor { throw new System.NotImplementedException(); } - - public override float GetBeatSnapDistanceAt(HitObject referenceObject) - { - throw new System.NotImplementedException(); - } - - public override float DurationToDistance(HitObject referenceObject, double duration) - { - throw new System.NotImplementedException(); - } - - public override double DistanceToDuration(HitObject referenceObject, float distance) - { - throw new System.NotImplementedException(); - } - - public override double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) - { - throw new System.NotImplementedException(); - } - - public override float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) - { - throw new System.NotImplementedException(); - } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index ae4141073e..5de614722f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public Action> RemoveControlPointsRequested; [Resolved(CanBeNull = true)] - private IPositionSnapProvider snapProvider { get; set; } + private IDistanceSnapProvider snapProvider { get; set; } public PathControlPointVisualiser(Slider slider, bool allowSelection) { diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index b868c9a7ee..73a4ea5434 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private int currentSegmentLength; [Resolved(CanBeNull = true)] - private HitObjectComposer composer { get; set; } + private IDistanceSnapProvider snapProvider { get; set; } public SliderPlacementBlueprint() : base(new Objects.Slider()) @@ -220,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSlider() { - HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; + HitObject.Path.ExpectedDistance.Value = snapProvider?.GetSnappedDistanceFromDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; bodyPiece.UpdateFrom(HitObject); headCirclePiece.UpdateFrom(HitObject.HeadCircle); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 6cf2a493a9..a019f2fb64 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected PathControlPointVisualiser ControlPointVisualiser { get; private set; } [Resolved(CanBeNull = true)] - private HitObjectComposer composer { get; set; } + private IDistanceSnapProvider snapProvider { get; set; } [Resolved(CanBeNull = true)] private IPlacementHandler placementHandler { get; set; } @@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // Move the control points from the insertion index onwards to make room for the insertion controlPoints.Insert(insertionIndex, pathControlPoint); - HitObject.SnapTo(composer); + HitObject.SnapTo(snapProvider); return pathControlPoint; } @@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } // Snap the slider to the current beat divisor before checking length validity. - HitObject.SnapTo(composer); + HitObject.SnapTo(snapProvider); // If there are 0 or 1 remaining control points, or the slider has an invalid length, it is in a degenerate form and should be deleted if (controlPoints.Count <= 1 || !HitObject.Path.HasValidLength) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index efbac5439c..70c60ab635 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Edit public class OsuSelectionHandler : EditorSelectionHandler { [Resolved(CanBeNull = true)] - private IPositionSnapProvider? positionSnapProvider { get; set; } + private IDistanceSnapProvider? snapProvider { get; set; } /// /// During a transform, the initial origin is stored so it can be used throughout the operation. @@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Osu.Edit // Snap the slider's length to the current beat divisor // to calculate the final resulting duration / bounding box before the final checks. - slider.SnapTo(positionSnapProvider); + slider.SnapTo(snapProvider); //if sliderhead or sliderend end up outside playfield, revert scaling. Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider }); @@ -219,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Edit point.Position = oldControlPoints.Dequeue(); // Snap the slider's length again to undo the potentially-invalid length applied by the previous snap. - slider.SnapTo(positionSnapProvider); + slider.SnapTo(snapProvider); } private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) diff --git a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs index 70921a6170..c6e866561e 100644 --- a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs @@ -3,6 +3,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Edit { @@ -13,5 +14,45 @@ namespace osu.Game.Rulesets.Edit /// /// IBindable DistanceSpacingMultiplier { get; } + + /// + /// Retrieves the distance between two points within a timing point that are one beat length apart. + /// + /// An object to be used as a reference point for this operation. + /// The distance between two points residing in the timing point that are one beat length apart. + float GetBeatSnapDistanceAt(HitObject referenceObject); + + /// + /// Converts a duration to a distance. + /// + /// An object to be used as a reference point for this operation. + /// The duration to convert. + /// A value that represents as a distance in the timing point. + float DurationToDistance(HitObject referenceObject, double duration); + + /// + /// Converts a distance to a duration. + /// + /// An object to be used as a reference point for this operation. + /// The distance to convert. + /// A value that represents as a duration in the timing point. + double DistanceToDuration(HitObject referenceObject, float distance); + + /// + /// Converts a distance to a snapped duration. + /// + /// An object to be used as a reference point for this operation. + /// The distance to convert. + /// A value that represents as a duration snapped to the closest beat of the timing point. + double GetSnappedDurationFromDistance(HitObject referenceObject, float distance); + + /// + /// Converts an unsnapped distance to a snapped distance. + /// The returned distance will always be floored (as to never exceed the provided . + /// + /// An object to be used as a reference point for this operation. + /// The distance to convert. + /// A value that represents snapped to the closest beat of the timing point. + float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance); } } diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index 944619a3ca..8a179ed424 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Rulesets.Objects; using osuTK; namespace osu.Game.Rulesets.Edit @@ -24,47 +23,5 @@ namespace osu.Game.Rulesets.Edit /// The screen-space position to be snapped. /// The position post-snapping. Time will always be null. SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition); - - /// - /// Retrieves the distance between two points within a timing point that are one beat length apart. - /// - /// An object to be used as a reference point for this operation. - /// The distance between two points residing in the timing point that are one beat length apart. - // todo: this, and other distance-related methods below it, should move to IDistanceSnapProvider. - // needs thought on how to properly share implementation, since not all composers inherit IDistanceSnapProvider. - float GetBeatSnapDistanceAt(HitObject referenceObject); - - /// - /// Converts a duration to a distance. - /// - /// An object to be used as a reference point for this operation. - /// The duration to convert. - /// A value that represents as a distance in the timing point. - float DurationToDistance(HitObject referenceObject, double duration); - - /// - /// Converts a distance to a duration. - /// - /// An object to be used as a reference point for this operation. - /// The distance to convert. - /// A value that represents as a duration in the timing point. - double DistanceToDuration(HitObject referenceObject, float distance); - - /// - /// Converts a distance to a snapped duration. - /// - /// An object to be used as a reference point for this operation. - /// The distance to convert. - /// A value that represents as a duration snapped to the closest beat of the timing point. - double GetSnappedDurationFromDistance(HitObject referenceObject, float distance); - - /// - /// Converts an unsnapped distance to a snapped distance. - /// The returned distance will always be floored (as to never exceed the provided . - /// - /// An object to be used as a reference point for this operation. - /// The distance to convert. - /// A value that represents snapped to the closest beat of the timing point. - float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance); } } diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index ba614900c0..3100d26a55 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Objects /// /// Snaps the provided 's duration using the . /// - public static void SnapTo(this THitObject hitObject, IPositionSnapProvider? snapProvider) + public static void SnapTo(this THitObject hitObject, IDistanceSnapProvider? snapProvider) where THitObject : HitObject, IHasPath { hitObject.Path.ExpectedDistance.Value = snapProvider?.GetSnappedDistanceFromDistance(hitObject, (float)hitObject.Path.CalculatedDistance) ?? hitObject.Path.CalculatedDistance; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 8832c4e89c..c2b2bdb861 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -15,7 +15,6 @@ using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects; using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline @@ -316,17 +315,5 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private double getTimeFromPosition(Vector2 localPosition) => (localPosition.X / Content.DrawWidth) * track.Length; - - public IBindable DistanceSpacingMultiplier => throw new NotImplementedException(); - - public float GetBeatSnapDistanceAt(HitObject referenceObject) => throw new NotImplementedException(); - - public float DurationToDistance(HitObject referenceObject, double duration) => throw new NotImplementedException(); - - public double DistanceToDuration(HitObject referenceObject, float distance) => throw new NotImplementedException(); - - public double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) => throw new NotImplementedException(); - - public float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) => throw new NotImplementedException(); } } From d6395b14beae1116a3b7271363136c89642a1b98 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Thu, 28 Apr 2022 05:15:04 +0200 Subject: [PATCH 0597/1276] Add incompatibilities --- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 ++ osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 10 +++++++++- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index f46573c494..5d262cd95e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModClassic : ModClassic, IApplicableToHitObject, IApplicableToDrawableHitObject, IApplicableToDrawableRuleset { - public override Type[] IncompatibleMods => new[] { typeof(OsuModStrictTracking) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModStrictTracking), typeof(OsuModTarget) }; [SettingSource("No slider head accuracy requirement", "Scores sliders proportionally to the number of ticks hit.")] public Bindable NoSliderHeadAccuracy { get; } = new BindableBool(true); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index fea9246035..7a7e189c29 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Description => "It never gets boring!"; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget) }; + private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; private Random? rng; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs index 9be0dc748a..d9ab749ad3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Automation; public override string Description => @"Spinners will be automatically completed."; public override double ScoreMultiplier => 0.9; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) }; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTarget) }; public void ApplyToDrawableHitObject(DrawableHitObject hitObject) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs index ee325db66a..ab45e5192d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.DifficultyIncrease; public override string Description => @"Follow circles just got serious..."; public override double ScoreMultiplier => 1.0; - public override Type[] IncompatibleMods => new[] { typeof(ModClassic) }; + public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTarget) }; public void ApplyToDrawableHitObject(DrawableHitObject drawable) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 4fab9b6a5a..4d6c09f919 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -42,7 +42,15 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSuddenDeath) }; + public override Type[] IncompatibleMods => new[] + { + typeof(IRequiresApproachCircles), + typeof(OsuModClassic), + typeof(OsuModRandom), + typeof(OsuModSpunOut), + typeof(OsuModStrictTracking), + typeof(OsuModSuddenDeath) + }; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] public Bindable Seed { get; } = new Bindable From 835898dd30ae70170a6670178d19397d7a057361 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 05:49:37 +0300 Subject: [PATCH 0598/1276] Introduce `DistancedHitObjectComposer` and supersede `OsuToolboxComposite` --- .../Editor/TestSceneObjectObjectSnap.cs | 4 +- .../Editor/TestSceneSliderSnapping.cs | 3 +- .../Edit/OsuHitObjectComposer.cs | 12 +- .../Edit/OsuToolboxComposite.cs | 120 ------------- .../Edit/DistancedHitObjectComposer.cs | 158 ++++++++++++++++++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 48 ------ 6 files changed, 163 insertions(+), 182 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs create mode 100644 osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs index 954bb53dd5..dfb6ecfcae 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs @@ -7,7 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Tests.Beatmaps; @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("seek to first control point", () => EditorClock.Seek(Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First().Time)); AddStep("set distance spacing to 1", () => { - var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; + var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; distanceSpacing.Value = 1; }); } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index 2632e556c6..702e2216ac 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -11,6 +11,7 @@ using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; @@ -68,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor }); AddStep("set distance spacing to 1", () => { - var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; + var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; distanceSpacing.Value = 1; }); } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index a52c916ca0..bd26a99e51 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -24,13 +24,8 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Edit { - [Cached(typeof(IDistanceSnapProvider))] - public class OsuHitObjectComposer : HitObjectComposer, IDistanceSnapProvider + public class OsuHitObjectComposer : DistancedHitObjectComposer { - private OsuToolboxComposite osuToolboxComposite; - - public IBindable DistanceSpacingMultiplier => osuToolboxComposite.DistanceSpacing; - public OsuHitObjectComposer(Ruleset ruleset) : base(ruleset) { @@ -79,8 +74,6 @@ namespace osu.Game.Rulesets.Osu.Edit } }); - AddInternal(osuToolboxComposite = new OsuToolboxComposite()); - selectedHitObjects = EditorBeatmap.SelectedHitObjects.GetBoundCopy(); selectedHitObjects.CollectionChanged += (_, __) => updateDistanceSnapGrid(); @@ -135,9 +128,6 @@ namespace osu.Game.Rulesets.Osu.Edit } } - public override float GetBeatSnapDistanceAt(HitObject referenceObject) - => (float)(base.GetBeatSnapDistanceAt(referenceObject) * DistanceSpacingMultiplier.Value); - public override SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) { if (snapToVisibleBlueprints(screenSpacePosition, out var snapResult)) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs b/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs deleted file mode 100644 index 17232db509..0000000000 --- a/osu.Game.Rulesets.Osu/Edit/OsuToolboxComposite.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Events; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Settings.Sections; -using osu.Game.Rulesets.Edit; -using osu.Game.Screens.Edit; -using osuTK; - -namespace osu.Game.Rulesets.Osu.Edit -{ - /// - /// A toolbox composite for osu!-specific controls. - /// - // todo: once catch supports distance spacing, the control here should move out to a base "DistancingRulesetToolboxComposite" class or something better. - public class OsuToolboxComposite : CompositeDrawable - { - private ExpandingToolboxContainer expandingContainer; - private ExpandableSlider> distanceSpacingSlider; - - private readonly Bindable distanceSpacing = new BindableDouble(1.0) - { - MinValue = 0.1, - MaxValue = 6.0, - Precision = 0.01, - }; - - public IBindable DistanceSpacing => distanceSpacing; - - private bool distanceSpacingScrollActive; - - [Resolved] - private EditorBeatmap editorBeatmap { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - RelativeSizeAxes = Axes.Both; - - InternalChild = expandingContainer = new ExpandingToolboxContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Child = new EditorToolboxGroup("snapping") - { - Child = distanceSpacingSlider = new ExpandableSlider> - { - Current = { BindTarget = distanceSpacing }, - KeyboardStep = 0.1f, - } - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - distanceSpacing.Value = editorBeatmap.BeatmapInfo.DistanceSpacing; - distanceSpacing.BindValueChanged(v => - { - distanceSpacingSlider.ContractedLabelText = $"D. S. ({v.NewValue:0.##x})"; - distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; - editorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; - }, true); - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - if (e.ControlPressed && e.AltPressed && !e.Repeat) - { - expandingContainer.Expanded.Value = true; - distanceSpacingScrollActive = true; - return true; - } - - return base.OnKeyDown(e); - } - - protected override void OnKeyUp(KeyUpEvent e) - { - if (distanceSpacingScrollActive && (!e.AltPressed || !e.ControlPressed)) - { - expandingContainer.Expanded.Value = false; - distanceSpacingScrollActive = false; - } - } - - protected override bool OnScroll(ScrollEvent e) - { - if (distanceSpacingScrollActive) - { - distanceSpacing.Value += e.ScrollDelta.Y * (e.IsPrecise ? 0.01f : 0.1f); - return true; - } - - return base.OnScroll(e); - } - - private class ExpandingToolboxContainer : ExpandingContainer - { - protected override double HoverExpansionDelay => 250; - - public ExpandingToolboxContainer() - : base(130, 250) - { - RelativeSizeAxes = Axes.Y; - Padding = new MarginPadding { Left = 10 }; - - FillFlow.Spacing = new Vector2(10); - } - } - } -} diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs new file mode 100644 index 0000000000..a3624f6d77 --- /dev/null +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -0,0 +1,158 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings.Sections; +using osu.Game.Rulesets.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Edit +{ + /// + /// Represents a for rulesets with the concept of distances between objects. + /// + /// The base type of supported objects. + [Cached(typeof(IDistanceSnapProvider))] + public abstract class DistancedHitObjectComposer : HitObjectComposer, IDistanceSnapProvider + where TObject : HitObject + { + protected Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1.0) + { + MinValue = 0.1, + MaxValue = 6.0, + Precision = 0.01, + }; + + IBindable IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier; + + protected ExpandingToolboxContainer RightSideToolboxContainer { get; private set; } + + private ExpandableSlider> distanceSpacingSlider; + private bool distanceSpacingScrollActive; + + protected DistancedHitObjectComposer(Ruleset ruleset) + : base(ruleset) + { + } + + [BackgroundDependencyLoader] + private void load() + { + AddInternal(RightSideToolboxContainer = new ExpandingToolboxContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Child = new EditorToolboxGroup("snapping") + { + Child = distanceSpacingSlider = new ExpandableSlider> + { + Current = { BindTarget = DistanceSpacingMultiplier }, + KeyboardStep = 0.1f, + } + } + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + DistanceSpacingMultiplier.Value = EditorBeatmap.BeatmapInfo.DistanceSpacing; + DistanceSpacingMultiplier.BindValueChanged(v => + { + distanceSpacingSlider.ContractedLabelText = $"D. S. ({v.NewValue:0.##x})"; + distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; + EditorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; + }, true); + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.ControlPressed && e.AltPressed && !e.Repeat) + { + RightSideToolboxContainer.Expanded.Value = true; + distanceSpacingScrollActive = true; + return true; + } + + return base.OnKeyDown(e); + } + + protected override void OnKeyUp(KeyUpEvent e) + { + if (distanceSpacingScrollActive && (!e.AltPressed || !e.ControlPressed)) + { + RightSideToolboxContainer.Expanded.Value = false; + distanceSpacingScrollActive = false; + } + } + + protected override bool OnScroll(ScrollEvent e) + { + if (distanceSpacingScrollActive) + { + DistanceSpacingMultiplier.Value += e.ScrollDelta.Y * (e.IsPrecise ? 0.01f : 0.1f); + return true; + } + + return base.OnScroll(e); + } + + public virtual float GetBeatSnapDistanceAt(HitObject referenceObject) + { + return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * referenceObject.DifficultyControlPoint.SliderVelocity * DistanceSpacingMultiplier.Value / BeatSnapProvider.BeatDivisor); + } + + public virtual float DurationToDistance(HitObject referenceObject, double duration) + { + double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime); + return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceObject)); + } + + public virtual double DistanceToDuration(HitObject referenceObject, float distance) + { + double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime); + return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength; + } + + public virtual double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) + => BeatSnapProvider.SnapTime(referenceObject.StartTime + DistanceToDuration(referenceObject, distance), referenceObject.StartTime) - referenceObject.StartTime; + + public virtual float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) + { + double startTime = referenceObject.StartTime; + + double actualDuration = startTime + DistanceToDuration(referenceObject, distance); + + double snappedEndTime = BeatSnapProvider.SnapTime(actualDuration, startTime); + + double beatLength = BeatSnapProvider.GetBeatLengthAtTime(startTime); + + // we don't want to exceed the actual duration and snap to a point in the future. + // as we are snapping to beat length via SnapTime (which will round-to-nearest), check for snapping in the forward direction and reverse it. + if (snappedEndTime > actualDuration + 1) + snappedEndTime -= beatLength; + + return DurationToDistance(referenceObject, snappedEndTime - startTime); + } + + protected class ExpandingToolboxContainer : ExpandingContainer + { + protected override double HoverExpansionDelay => 250; + + public ExpandingToolboxContainer() + : base(130, 250) + { + RelativeSizeAxes = Axes.Y; + Padding = new MarginPadding { Left = 10 }; + + FillFlow.Spacing = new Vector2(10); + } + } + } +} diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 8d2bd1e5d1..9fc332d726 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -383,44 +383,6 @@ namespace osu.Game.Rulesets.Edit return new SnapResult(screenSpacePosition, targetTime, playfield); } - public override float GetBeatSnapDistanceAt(HitObject referenceObject) - { - return (float)(100 * referenceObject.DifficultyControlPoint.SliderVelocity * EditorBeatmap.Difficulty.SliderMultiplier / BeatSnapProvider.BeatDivisor); - } - - public override float DurationToDistance(HitObject referenceObject, double duration) - { - double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime); - return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceObject)); - } - - public override double DistanceToDuration(HitObject referenceObject, float distance) - { - double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime); - return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength; - } - - public override double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) - => BeatSnapProvider.SnapTime(referenceObject.StartTime + DistanceToDuration(referenceObject, distance), referenceObject.StartTime) - referenceObject.StartTime; - - public override float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) - { - double startTime = referenceObject.StartTime; - - double actualDuration = startTime + DistanceToDuration(referenceObject, distance); - - double snappedEndTime = BeatSnapProvider.SnapTime(actualDuration, startTime); - - double beatLength = BeatSnapProvider.GetBeatLengthAtTime(startTime); - - // we don't want to exceed the actual duration and snap to a point in the future. - // as we are snapping to beat length via SnapTime (which will round-to-nearest), check for snapping in the forward direction and reverse it. - if (snappedEndTime > actualDuration + 1) - snappedEndTime -= beatLength; - - return DurationToDistance(referenceObject, snappedEndTime - startTime); - } - #endregion private class LeftToolboxFlow : ExpandingButtonContainer @@ -473,16 +435,6 @@ namespace osu.Game.Rulesets.Edit public virtual SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, null); - public abstract float GetBeatSnapDistanceAt(HitObject referenceObject); - - public abstract float DurationToDistance(HitObject referenceObject, double duration); - - public abstract double DistanceToDuration(HitObject referenceObject, float distance); - - public abstract double GetSnappedDurationFromDistance(HitObject referenceObject, float distance); - - public abstract float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance); - #endregion } } From bfc6bfc91b19a1fc9fb57518e490b81d9c09b8c3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 05:50:55 +0300 Subject: [PATCH 0599/1276] Let `CatchHitObjectComposer` inherit from `DistancedHitObjectComposer` and hide control --- .../Edit/CatchHitObjectComposer.cs | 9 +++++---- .../Edit/DistancedHitObjectComposer.cs | 20 +++++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index b3e6fe382a..a90b0d3e89 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -24,10 +24,7 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Edit { - /// - /// todo: should inherit once it supports distance spacing properly. - /// - public class CatchHitObjectComposer : HitObjectComposer + public class CatchHitObjectComposer : DistancedHitObjectComposer { private const float distance_snap_radius = 50; @@ -45,6 +42,10 @@ namespace osu.Game.Rulesets.Catch.Edit [BackgroundDependencyLoader] private void load() { + // todo: enable distance spacing once catch supports it. + RightSideToolboxContainer.Alpha = 0; + DistanceSpacingMultiplier.Disabled = true; + LayerBelowRuleset.Add(new PlayfieldBorder { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index a3624f6d77..d0c9babf24 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -45,6 +45,7 @@ namespace osu.Game.Rulesets.Edit { AddInternal(RightSideToolboxContainer = new ExpandingToolboxContainer { + Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Child = new EditorToolboxGroup("snapping") @@ -62,18 +63,21 @@ namespace osu.Game.Rulesets.Edit { base.LoadComplete(); - DistanceSpacingMultiplier.Value = EditorBeatmap.BeatmapInfo.DistanceSpacing; - DistanceSpacingMultiplier.BindValueChanged(v => + if (!DistanceSpacingMultiplier.Disabled) { - distanceSpacingSlider.ContractedLabelText = $"D. S. ({v.NewValue:0.##x})"; - distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; - EditorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; - }, true); + DistanceSpacingMultiplier.Value = EditorBeatmap.BeatmapInfo.DistanceSpacing; + DistanceSpacingMultiplier.BindValueChanged(v => + { + distanceSpacingSlider.ContractedLabelText = $"D. S. ({v.NewValue:0.##x})"; + distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; + EditorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; + }, true); + } } protected override bool OnKeyDown(KeyDownEvent e) { - if (e.ControlPressed && e.AltPressed && !e.Repeat) + if (!DistanceSpacingMultiplier.Disabled && e.ControlPressed && e.AltPressed && !e.Repeat) { RightSideToolboxContainer.Expanded.Value = true; distanceSpacingScrollActive = true; @@ -85,7 +89,7 @@ namespace osu.Game.Rulesets.Edit protected override void OnKeyUp(KeyUpEvent e) { - if (distanceSpacingScrollActive && (!e.AltPressed || !e.ControlPressed)) + if (!DistanceSpacingMultiplier.Disabled && distanceSpacingScrollActive && (!e.AltPressed || !e.ControlPressed)) { RightSideToolboxContainer.Expanded.Value = false; distanceSpacingScrollActive = false; From a5defbe4961b06d37a8a6c7f834196e00f63a806 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 06:44:02 +0300 Subject: [PATCH 0600/1276] Keep distance spacing reset to one on test scenes with slider snapping for now --- .../Editor/TestSceneObjectObjectSnap.cs | 12 ++++-------- .../Editor/TestSceneSliderSnapping.cs | 10 ++-------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs index dfb6ecfcae..21ba1e647d 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs @@ -3,11 +3,9 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Bindables; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Tests.Beatmaps; @@ -21,18 +19,16 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { private OsuPlayfield playfield; - protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(Ruleset.Value, false); + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(Ruleset.Value, false) + { + BeatmapInfo = { DistanceSpacing = 1 } + }; public override void SetUpSteps() { base.SetUpSteps(); AddStep("get playfield", () => playfield = Editor.ChildrenOfType().First()); AddStep("seek to first control point", () => EditorClock.Seek(Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First().Time)); - AddStep("set distance spacing to 1", () => - { - var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; - distanceSpacing.Value = 1; - }); } [TestCase(true)] diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index 702e2216ac..4833266dca 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -4,14 +4,12 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Input.Events; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Input.Bindings; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; @@ -38,7 +36,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor controlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length }); return new TestBeatmap(ruleset, false) { - ControlPointInfo = controlPointInfo + ControlPointInfo = controlPointInfo, + BeatmapInfo = { DistanceSpacing = 1 }, }; } @@ -67,11 +66,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor var beatDivisor = Editor.Dependencies.Get(); beatDivisor.Value = 1; }); - AddStep("set distance spacing to 1", () => - { - var distanceSpacing = (BindableDouble)Editor.ChildrenOfType().Single().DistanceSpacingMultiplier; - distanceSpacing.Value = 1; - }); } [Test] From 71533808e9958f7470c407750cc34cdc0a014804 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 06:46:20 +0300 Subject: [PATCH 0601/1276] Add test case for `DistanceSpacingMultiplier` in distance snapping test scene --- .../TestSceneHitObjectComposerDistanceSnapping.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index d9312cb7ae..e2ff45c431 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; @@ -70,7 +71,7 @@ namespace osu.Game.Tests.Editing [TestCase(2)] public void TestSliderMultiplier(float multiplier) { - AddStep($"set multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = multiplier); + AddStep($"set slider multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = multiplier); assertSnapDistance(100 * multiplier); } @@ -88,6 +89,15 @@ namespace osu.Game.Tests.Editing }); } + [TestCase(1)] + [TestCase(2)] + public void TestDistanceSpacingMultiplier(float multiplier) + { + AddStep($"set distance spacing = {multiplier}", () => composer.DistanceSpacingMultiplier.Value = multiplier); + + assertSnapDistance(100 * multiplier); + } + [TestCase(1)] [TestCase(2)] public void TestBeatDivisor(int divisor) @@ -222,6 +232,8 @@ namespace osu.Game.Tests.Editing { public new EditorBeatmap EditorBeatmap => base.EditorBeatmap; + public new Bindable DistanceSpacingMultiplier => base.DistanceSpacingMultiplier; + public TestHitObjectComposer() : base(new OsuRuleset()) { From dacca73838049da362da4f7628c5e8948fbe8322 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 06:46:34 +0300 Subject: [PATCH 0602/1276] Set default value of `DistanceSpacing` to 1 --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index f90208d0c0..1a9703f478 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -109,7 +109,7 @@ namespace osu.Game.Beatmaps public bool SamplesMatchPlaybackRate { get; set; } = true; - public double DistanceSpacing { get; set; } + public double DistanceSpacing { get; set; } = 1.0; public int BeatDivisor { get; set; } From 2e8372fe906996996553d275f333358b3699c9b3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 06:52:42 +0300 Subject: [PATCH 0603/1276] Minor cleanup Just from reading diff. --- .../Editor/TestSceneSliderSnapping.cs | 3 +-- .../Visual/Editing/TestSceneHitObjectComposer.cs | 7 +++---- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 +--- .../Screens/Edit/Compose/Components/DistanceSnapGrid.cs | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index 4833266dca..6b9a0ff531 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -3,7 +3,6 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Input.Events; using osu.Framework.Testing; using osu.Framework.Utils; @@ -63,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor })); AddStep("set beat divisor to 1/1", () => { - var beatDivisor = Editor.Dependencies.Get(); + var beatDivisor = (BindableBeatDivisor)Editor.Dependencies.Get(typeof(BindableBeatDivisor)); beatDivisor.Value = 1; }); } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 7039d7d8a4..ae1b691767 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; @@ -91,15 +90,15 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestDistanceSpacingHotkeys() { - float originalSpacing = 0; + double originalSpacing = 0; - AddStep("retrieve original spacing", () => originalSpacing = (float)editorBeatmap.BeatmapInfo.DistanceSpacing); + AddStep("retrieve original spacing", () => originalSpacing = editorBeatmap.BeatmapInfo.DistanceSpacing); AddStep("hold ctrl", () => InputManager.PressKey(Key.LControl)); AddStep("hold alt", () => InputManager.PressKey(Key.LAlt)); AddStep("scroll mouse 5 steps", () => InputManager.ScrollVerticalBy(5)); - AddAssert("distance spacing increased by 0.5", () => Precision.AlmostEquals(editorBeatmap.BeatmapInfo.DistanceSpacing, originalSpacing + 0.5f)); + AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5); AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt)); AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl)); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 9fc332d726..a235a5bc60 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -117,8 +117,6 @@ namespace osu.Game.Rulesets.Edit }, new LeftToolboxFlow { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, Children = new Drawable[] { new EditorToolboxGroup("toolbox (1-9)") @@ -134,7 +132,7 @@ namespace osu.Game.Rulesets.Edit Direction = FillDirection.Vertical, Spacing = new Vector2(0, 5), }, - }, + } } }, }; diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 9dd6cec8ce..4f348567b1 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -84,7 +84,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.LoadComplete(); - beatDivisor.BindValueChanged(_ => updateSpacing(), true); + beatDivisor.BindValueChanged(_ => updateSpacing()); distanceSpacingMultiplier = SnapProvider.DistanceSpacingMultiplier.GetBoundCopy(); distanceSpacingMultiplier.BindValueChanged(_ => updateSpacing(), true); From aabe5865784ca5ed2729c507c2267ca14cc0d0c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 14:09:30 +0900 Subject: [PATCH 0604/1276] Add return value to `EnsureMutableSkin` to understand whether a new skin was made --- osu.Game/Skinning/SkinManager.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 01e7646644..77e60d179c 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -143,12 +143,15 @@ namespace osu.Game.Skinning /// Ensure that the current skin is in a state it can accept user modifications. /// This will create a copy of any internal skin and being tracking in the database if not already. /// - public void EnsureMutableSkin() + /// + /// Whether a new skin was created to allow for mutation. + /// + public bool EnsureMutableSkin() { - CurrentSkinInfo.Value.PerformRead(s => + return CurrentSkinInfo.Value.PerformRead(s => { if (!s.Protected) - return; + return false; string[] existingSkinNames = realm.Run(r => r.All() .Where(skin => !skin.DeletePending) @@ -160,7 +163,7 @@ namespace osu.Game.Skinning { Creator = s.Creator, InstantiationInfo = s.InstantiationInfo, - Name = NamingUtils.GetNextBestName(existingSkinNames, $"{s.Name} (modified)") + Name = NamingUtils.GetNextBestName(existingSkinNames, $@"{s.Name} (modified)") }; var result = skinModelManager.Import(skinInfo); @@ -172,6 +175,8 @@ namespace osu.Game.Skinning result.PerformRead(skin => Save(skin.CreateInstance(this))); CurrentSkinInfo.Value = result; } + + return true; }); } From 7c1b0b41ff1e52acb9cc5bc25fa166bf4d95637f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 14:09:41 +0900 Subject: [PATCH 0605/1276] Fix migration not working correctly when applying on a default skin --- osu.Game/Screens/Play/SongProgress.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 592071b02e..35d70976d2 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -162,7 +162,6 @@ namespace osu.Game.Screens.Play if (!configShowGraph.IsDefault) { ShowGraph.Value = configShowGraph.Value; - configShowGraph.SetDefault(); // This is pretty ugly, but the only way to make this stick... if (skinManager != null) @@ -171,17 +170,17 @@ namespace osu.Game.Screens.Play if (skinnableTarget != null) { - skinManager.EnsureMutableSkin(); + // If the skin is not mutable, a mutable instance will be created, causing this migration logic to run again on the correct skin. + // Therefore we want to avoid resetting the config value on this invocation. + if (skinManager.EnsureMutableSkin()) + return; - // If `EnsureMutableSkin` actually changed the skin, default layout may take a frame to apply. - // See `SkinnableTargetComponentsContainer`'s use of ScheduleAfterChildren. - ScheduleAfterChildren(() => - { - var skin = skinManager.CurrentSkin.Value; - skin.UpdateDrawableTarget(skinnableTarget); + var skin = skinManager.CurrentSkin.Value; + skin.UpdateDrawableTarget(skinnableTarget); - skinManager.Save(skin); - }); + skinManager.Save(skin); + + configShowGraph.SetDefault(); } } } From 3c45746ba10e889f6ca68172d40fa397b448ecb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 14:29:57 +0900 Subject: [PATCH 0606/1276] Add back schedule for safety --- osu.Game/Screens/Play/SongProgress.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 35d70976d2..a21a08e558 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -175,10 +175,15 @@ namespace osu.Game.Screens.Play if (skinManager.EnsureMutableSkin()) return; - var skin = skinManager.CurrentSkin.Value; - skin.UpdateDrawableTarget(skinnableTarget); + // If `EnsureMutableSkin` actually changed the skin, default layout may take a frame to apply. + // See `SkinnableTargetComponentsContainer`'s use of ScheduleAfterChildren. + ScheduleAfterChildren(() => + { + var skin = skinManager.CurrentSkin.Value; + skin.UpdateDrawableTarget(skinnableTarget); - skinManager.Save(skin); + skinManager.Save(skin); + }); configShowGraph.SetDefault(); } From f3a0e2ed55ba0c99feaec64911d92575bbb3d72c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 14:59:39 +0900 Subject: [PATCH 0607/1276] Increase fade duration --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index a47a399a23..03a66575c4 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -426,7 +426,7 @@ namespace osu.Game.Overlays.Mods else targetColour = IsHovered ? colours.GrayC : colours.Gray8; - this.FadeColour(targetColour, 300, Easing.OutQuint); + this.FadeColour(targetColour, 800, Easing.OutQuint); } protected override bool OnClick(ClickEvent e) From 7aaa88cac2be239dcc778a1082585f8e8d5a7e55 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 10:24:36 +0300 Subject: [PATCH 0608/1276] Convert data type of `DistanceSpacing` to float --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 2 +- .../TestSceneHitObjectComposerDistanceSnapping.cs | 2 +- .../Visual/Editing/TestSceneDistanceSnapGrid.cs | 2 +- .../UserInterface/TestSceneExpandingContainer.cs | 4 ++-- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- osu.Game/Beatmaps/EFBeatmapInfo.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- .../Settings/Sections/Gameplay/InputSettings.cs | 2 +- osu.Game/Overlays/Settings/Sections/SizeSlider.cs | 7 ++----- .../Sections/UserInterface/GeneralSettings.cs | 2 +- .../Rulesets/Edit/DistancedHitObjectComposer.cs | 14 +++++++------- osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs | 2 +- .../Edit/Compose/Components/DistanceSnapGrid.cs | 2 +- 13 files changed, 21 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 2ba30c5f74..4cccddddf3 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); - public IBindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); + public IBindable DistanceSpacingMultiplier { get; } = new BindableFloat(1); public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length; diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index e2ff45c431..ffaf3ec41d 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -232,7 +232,7 @@ namespace osu.Game.Tests.Editing { public new EditorBeatmap EditorBeatmap => base.EditorBeatmap; - public new Bindable DistanceSpacingMultiplier => base.DistanceSpacingMultiplier; + public new Bindable DistanceSpacingMultiplier => base.DistanceSpacingMultiplier; public TestHitObjectComposer() : base(new OsuRuleset()) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index b9cfa84a5d..20bf9b3fa0 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.Editing public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); - public IBindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); + public IBindable DistanceSpacingMultiplier { get; } = new BindableFloat(1); public float GetBeatSnapDistanceAt(HitObject referenceObject) => 10; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs index 288c0cb140..f4920b4412 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.UserInterface private TestExpandingContainer container; private SettingsToolboxGroup toolboxGroup; - private ExpandableSlider> slider1; + private ExpandableSlider slider1; private ExpandableSlider slider2; [SetUp] @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.UserInterface Width = 1, Children = new Drawable[] { - slider1 = new ExpandableSlider> + slider1 = new ExpandableSlider { Current = new BindableFloat { diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 1a9703f478..1b364247a8 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -109,7 +109,7 @@ namespace osu.Game.Beatmaps public bool SamplesMatchPlaybackRate { get; set; } = true; - public double DistanceSpacing { get; set; } = 1.0; + public float DistanceSpacing { get; set; } = 1.0f; public int BeatDivisor { get; set; } diff --git a/osu.Game/Beatmaps/EFBeatmapInfo.cs b/osu.Game/Beatmaps/EFBeatmapInfo.cs index 740adfd1c7..67c4dbe788 100644 --- a/osu.Game/Beatmaps/EFBeatmapInfo.cs +++ b/osu.Game/Beatmaps/EFBeatmapInfo.cs @@ -107,7 +107,7 @@ namespace osu.Game.Beatmaps [NotMapped] public int[] Bookmarks { get; set; } = Array.Empty(); - public double DistanceSpacing { get; set; } + public float DistanceSpacing { get; set; } public int BeatDivisor { get; set; } public int GridSize { get; set; } public double TimelineZoom { get; set; } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 79d8bd3bb3..0ba7a07010 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -240,7 +240,7 @@ namespace osu.Game.Beatmaps.Formats break; case @"DistanceSpacing": - beatmap.BeatmapInfo.DistanceSpacing = Math.Max(0, Parsing.ParseDouble(pair.Value)); + beatmap.BeatmapInfo.DistanceSpacing = Math.Max(0, Parsing.ParseFloat(pair.Value)); break; case @"BeatDivisor": diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs index 83ea655601..962572ca6e 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { Children = new Drawable[] { - new SettingsSlider> + new SettingsSlider { LabelText = SkinSettingsStrings.GameplayCursorSize, Current = config.GetBindable(OsuSetting.GameplayCursorSize), diff --git a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs index c8a46162af..8aeb440be1 100644 --- a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs +++ b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Globalization; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; @@ -11,9 +9,8 @@ namespace osu.Game.Overlays.Settings.Sections /// /// A slider intended to show a "size" multiplier number, where 1x is 1.0. /// - public class SizeSlider : OsuSliderBar - where T : struct, IEquatable, IComparable, IConvertible, IFormattable + internal class SizeSlider : OsuSliderBar { - public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", NumberFormatInfo.CurrentInfo); + public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x"); } } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 0e3b822c82..6e1558f7d7 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface LabelText = UserInterfaceStrings.CursorRotation, Current = config.GetBindable(OsuSetting.CursorRotation) }, - new SettingsSlider> + new SettingsSlider { LabelText = UserInterfaceStrings.MenuCursorSize, Current = config.GetBindable(OsuSetting.MenuCursorSize), diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index d0c9babf24..ce31318fb8 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -21,18 +21,18 @@ namespace osu.Game.Rulesets.Edit public abstract class DistancedHitObjectComposer : HitObjectComposer, IDistanceSnapProvider where TObject : HitObject { - protected Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1.0) + protected Bindable DistanceSpacingMultiplier { get; } = new BindableFloat(1.0f) { - MinValue = 0.1, - MaxValue = 6.0, - Precision = 0.01, + MinValue = 0.1f, + MaxValue = 6.0f, + Precision = 0.01f, }; - IBindable IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier; + IBindable IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier; protected ExpandingToolboxContainer RightSideToolboxContainer { get; private set; } - private ExpandableSlider> distanceSpacingSlider; + private ExpandableSlider distanceSpacingSlider; private bool distanceSpacingScrollActive; protected DistancedHitObjectComposer(Ruleset ruleset) @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Edit Origin = Anchor.TopRight, Child = new EditorToolboxGroup("snapping") { - Child = distanceSpacingSlider = new ExpandableSlider> + Child = distanceSpacingSlider = new ExpandableSlider { Current = { BindTarget = DistanceSpacingMultiplier }, KeyboardStep = 0.1f, diff --git a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs index c6e866561e..0eb572d22a 100644 --- a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Edit /// The spacing multiplier applied to beat snap distances. /// /// - IBindable DistanceSpacingMultiplier { get; } + IBindable DistanceSpacingMultiplier { get; } /// /// Retrieves the distance between two points within a timing point that are one beat length apart. diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 4f348567b1..cd56beda47 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved] private BindableBeatDivisor beatDivisor { get; set; } - private IBindable distanceSpacingMultiplier; + private IBindable distanceSpacingMultiplier; private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit); private readonly double? endTime; From abb88b090702a9330401ab4c348b725b379a2d25 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 10:54:38 +0300 Subject: [PATCH 0609/1276] Move distance spacing application to `DistanceSnapGrid` --- .../Editor/TestSceneObjectObjectSnap.cs | 5 +---- .../Editor/TestSceneSliderSnapping.cs | 3 +-- .../TestSceneHitObjectComposerDistanceSnapping.cs | 10 ---------- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 2 +- .../Edit/Compose/Components/DistanceSnapGrid.cs | 2 +- 5 files changed, 4 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs index 21ba1e647d..7bdf131e0d 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs @@ -19,10 +19,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { private OsuPlayfield playfield; - protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(Ruleset.Value, false) - { - BeatmapInfo = { DistanceSpacing = 1 } - }; + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(Ruleset.Value, false); public override void SetUpSteps() { diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index 6b9a0ff531..b43b2b1461 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -35,8 +35,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor controlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length }); return new TestBeatmap(ruleset, false) { - ControlPointInfo = controlPointInfo, - BeatmapInfo = { DistanceSpacing = 1 }, + ControlPointInfo = controlPointInfo }; } diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index ffaf3ec41d..2c2c06ae0c 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -62,7 +62,6 @@ namespace osu.Game.Tests.Editing BeatDivisor.Value = 1; composer.EditorBeatmap.Difficulty.SliderMultiplier = 1; - composer.EditorBeatmap.BeatmapInfo.DistanceSpacing = 1; composer.EditorBeatmap.ControlPointInfo.Clear(); composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }); }); @@ -89,15 +88,6 @@ namespace osu.Game.Tests.Editing }); } - [TestCase(1)] - [TestCase(2)] - public void TestDistanceSpacingMultiplier(float multiplier) - { - AddStep($"set distance spacing = {multiplier}", () => composer.DistanceSpacingMultiplier.Value = multiplier); - - assertSnapDistance(100 * multiplier); - } - [TestCase(1)] [TestCase(2)] public void TestBeatDivisor(int divisor) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index ce31318fb8..1e1fbb4edf 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Edit public virtual float GetBeatSnapDistanceAt(HitObject referenceObject) { - return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * referenceObject.DifficultyControlPoint.SliderVelocity * DistanceSpacingMultiplier.Value / BeatSnapProvider.BeatDivisor); + return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * referenceObject.DifficultyControlPoint.SliderVelocity / BeatSnapProvider.BeatDivisor); } public virtual float DurationToDistance(HitObject referenceObject, double duration) diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index cd56beda47..13c3b713b1 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -92,7 +92,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updateSpacing() { - DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject); + DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * distanceSpacingMultiplier.Value; if (endTime == null) MaxIntervals = int.MaxValue; From b6d2ca7709b0b8a32f179c0c258852217372b520 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 10:57:14 +0300 Subject: [PATCH 0610/1276] Clarify TODO comment --- osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index a90b0d3e89..9ff6d10a49 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Edit [BackgroundDependencyLoader] private void load() { - // todo: enable distance spacing once catch supports it. + // todo: enable distance spacing once catch supports applying it to its existing distance snap grid implementation. RightSideToolboxContainer.Alpha = 0; DistanceSpacingMultiplier.Disabled = true; From edeefc28501a12c7719025a63925975b39e4bd3c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 11:10:14 +0300 Subject: [PATCH 0611/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 82dec74855..ff6499631d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 325e834fa5..26891ad978 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 8775442be2..d261e13ade 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From c8665dc93b40835dd08687c51ec4384354e83584 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 11:12:50 +0300 Subject: [PATCH 0612/1276] Update `TimeSpan` localisation to use `ToLocalisableString` --- osu.Game/Extensions/TimeDisplayExtensions.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs index 54af6a5942..98633958ee 100644 --- a/osu.Game/Extensions/TimeDisplayExtensions.cs +++ b/osu.Game/Extensions/TimeDisplayExtensions.cs @@ -3,6 +3,7 @@ using System; using Humanizer; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; @@ -42,12 +43,12 @@ namespace osu.Game.Extensions public static LocalisableString ToFormattedDuration(this TimeSpan timeSpan) { if (timeSpan.TotalDays >= 1) - return new LocalisableFormattableString(timeSpan, @"dd\:hh\:mm\:ss"); + return timeSpan.ToLocalisableString(@"dd\:hh\:mm\:ss"); if (timeSpan.TotalHours >= 1) - return new LocalisableFormattableString(timeSpan, @"hh\:mm\:ss"); + return timeSpan.ToLocalisableString(@"hh\:mm\:ss"); - return new LocalisableFormattableString(timeSpan, @"mm\:ss"); + return timeSpan.ToLocalisableString(@"mm\:ss"); } /// From be2d616e345e7e37ff5698b1b57c435209874d83 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 11:19:30 +0300 Subject: [PATCH 0613/1276] Fix localisation use cases not falling back to `default(LocalisableString)` Previously such use cases have been returning `null` values just fine since `GetLocalisableString` was returning `LocalisableFormattableString`, and these null values were wrapped in `LocalisableString`s implicitly, therefore not requiring an explicit fallback string and `null` doesn't break anything. --- .../Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs | 2 +- .../Overlays/Profile/Header/Components/LevelProgressBar.cs | 2 +- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 2 +- osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index ec795cf6b2..9ee002fd9d 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -119,7 +119,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x"); ppColumn.Alpha = value.BeatmapInfo.Status.GrantsPerformancePoints() ? 1 : 0; - ppColumn.Text = value.PP?.ToLocalisableString(@"N0"); + ppColumn.Text = value.PP?.ToLocalisableString(@"N0") ?? default(LocalisableString); statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(createStatisticsColumn); modsColumn.Mods = value.Mods; diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index 8f6b935128..ec9cb55042 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void updateProgress(APIUser user) { levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0; - levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'"); + levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'") ?? default(LocalisableString); } } } diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index dfa45cc543..48a4c31f30 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Rankings startDateColumn.Value = dateToString(response.Spotlight.StartDate); endDateColumn.Value = dateToString(response.Spotlight.EndDate); mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString(@"N0"); - participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString(@"N0"); + participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString(@"N0") ?? default(LocalisableString); } private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString(@"yyyy-MM-dd"); diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 17c17b1f1a..bdbd2942d1 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; @@ -24,7 +25,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new RowText { Text = item.PP?.ToLocalisableString(@"N0"), } + new RowText { Text = item.PP?.ToLocalisableString(@"N0") ?? default(LocalisableString), } }; } } From 92cac0b74e67616aec5845d11c9adb7deb3d6b2a Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 28 Apr 2022 17:30:24 +0900 Subject: [PATCH 0614/1276] Fix TabControls using the wrong sample --- .../BeatmapListing/BeatmapListingCardSizeTabControl.cs | 3 ++- osu.Game/Overlays/OverlaySortTabControl.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs index e4fda9d9c3..1f9a63e3b9 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs @@ -72,7 +72,8 @@ namespace osu.Game.Overlays.BeatmapListing Size = new Vector2(12), Icon = getIconForCardSize(Value) } - } + }, + new HoverClickSounds(HoverSampleSet.TabSelect) }; } diff --git a/osu.Game/Overlays/OverlaySortTabControl.cs b/osu.Game/Overlays/OverlaySortTabControl.cs index d4dde0db3f..5f5cfce344 100644 --- a/osu.Game/Overlays/OverlaySortTabControl.cs +++ b/osu.Game/Overlays/OverlaySortTabControl.cs @@ -149,7 +149,7 @@ namespace osu.Game.Overlays } }); - AddInternal(new HoverClickSounds()); + AddInternal(new HoverClickSounds(HoverSampleSet.TabSelect)); } protected override void LoadComplete() From 37816ebc41abc2679c667a848865382872aa01b2 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 28 Apr 2022 17:33:51 +0900 Subject: [PATCH 0615/1276] Add audio feedback to `SwitchButton` checkbox --- .../Graphics/UserInterfaceV2/SwitchButton.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs index c6477d1781..f483e67b27 100644 --- a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs @@ -4,6 +4,8 @@ #nullable enable using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -31,6 +33,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 private Color4 enabledColour; private Color4 disabledColour; + private Sample? sampleChecked; + private Sample? sampleUnchecked; + public SwitchButton() { Size = new Vector2(45, 20); @@ -70,13 +75,16 @@ namespace osu.Game.Graphics.UserInterfaceV2 } [BackgroundDependencyLoader(true)] - private void load(OverlayColourProvider? colourProvider, OsuColour colours) + private void load(OverlayColourProvider? colourProvider, OsuColour colours, AudioManager audio) { enabledColour = colourProvider?.Highlight1 ?? colours.BlueDark; disabledColour = colourProvider?.Background3 ?? colours.Gray3; switchContainer.Colour = enabledColour; fill.Colour = disabledColour; + + sampleChecked = audio.Samples.Get(@"UI/check-on"); + sampleUnchecked = audio.Samples.Get(@"UI/check-off"); } protected override void LoadComplete() @@ -107,6 +115,16 @@ namespace osu.Game.Graphics.UserInterfaceV2 base.OnHoverLost(e); } + protected override void OnUserChange(bool value) + { + base.OnUserChange(value); + + if (value) + sampleChecked?.Play(); + else + sampleUnchecked?.Play(); + } + private void updateBorder() { circularContainer.TransformBorderTo((Current.Value ? enabledColour : disabledColour).Lighten(IsHovered ? 0.3f : 0)); From 691bec6f16845c94fa79ba26ff87372e988c43d3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 11:38:40 +0300 Subject: [PATCH 0616/1276] Use `LocalisableString.Interpolate` instead of invalid `TranslatableString`s --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 4 ++-- osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 27a057bf09..735de670e3 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -317,11 +317,11 @@ namespace osu.Game.Overlays } else { - BackButton.Text = new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Back, steps[currentStepIndex.Value - 1].Description); + BackButton.Text = LocalisableString.Interpolate($@"{CommonStrings.Back} ({steps[currentStepIndex.Value - 1].Description})"); NextButton.Text = isLastStep ? CommonStrings.Finish - : new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Next, steps[currentStepIndex.Value + 1].Description); + : LocalisableString.Interpolate($@"{CommonStrings.Next} ({steps[currentStepIndex.Value + 1].Description})"); } } diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 42091c521f..1662ca399f 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -99,8 +99,8 @@ namespace osu.Game.Screens.Play.PlayerSettings { public override LocalisableString TooltipText => Current.Value == 0 - ? new TranslatableString("_", @"{0} ms", base.TooltipText) - : new TranslatableString("_", @"{0} ms {1}", base.TooltipText, getEarlyLateText(Current.Value)); + ? LocalisableString.Interpolate($@"{base.TooltipText} ms") + : LocalisableString.Interpolate($@"{base.TooltipText} ms {getEarlyLateText(Current.Value)}"); private LocalisableString getEarlyLateText(double value) { From 93db6c6bb0a82b85cf1a4429bcf8e7ff165bc75f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 17:46:00 +0900 Subject: [PATCH 0617/1276] Move `FastRandom` to `LegacyRandom` in `osu.Game` project --- .../Beatmaps/CatchBeatmapProcessor.cs | 8 +- .../Beatmaps/ManiaBeatmapConverter.cs | 8 +- .../Legacy/DistanceObjectPatternGenerator.cs | 4 +- .../Legacy/EndTimeObjectPatternGenerator.cs | 4 +- .../Legacy/HitObjectPatternGenerator.cs | 4 +- .../Patterns/Legacy/PatternGenerator.cs | 6 +- .../MathUtils/FastRandom.cs | 95 ------------------- .../Utils/LegacyRandom.cs | 37 +++++--- 8 files changed, 40 insertions(+), 126 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs rename osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs => osu.Game/Utils/LegacyRandom.cs (79%) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 346a09cac8..ab61b14ac4 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.MathUtils; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Utils; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps public void ApplyPositionOffsets(IBeatmap beatmap) { - var rng = new FastRandom(RNG_SEED); + var rng = new LegacyRandom(RNG_SEED); float? lastPosition = null; double lastStartTime = 0; @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps initialiseHyperDash(beatmap); } - private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) + private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, LegacyRandom rng) { float offsetPosition = hitObject.OriginalX; double startTime = hitObject.StartTime; @@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps /// The position which the offset should be applied to. /// The maximum offset, cannot exceed 20px. /// The random number generator. - private static void applyRandomOffset(ref float position, double maxOffset, FastRandom rng) + private static void applyRandomOffset(ref float position, double maxOffset, LegacyRandom rng) { bool right = rng.NextBool(); float rand = Math.Min(20, (float)rng.Next(0, Math.Max(0, maxOffset))); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 47e0e6d7b1..207c6907c8 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -11,8 +11,8 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Beatmaps.Patterns; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; +using osu.Game.Utils; using osuTK; namespace osu.Game.Rulesets.Mania.Beatmaps @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private readonly int originalTargetColumns; // Internal for testing purposes - internal FastRandom Random { get; private set; } + internal LegacyRandom Random { get; private set; } private Pattern lastPattern = new Pattern(); @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps IBeatmapDifficultyInfo difficulty = original.Difficulty; int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate); - Random = new FastRandom(seed); + Random = new LegacyRandom(seed); return base.ConvertBeatmap(original, cancellationToken); } @@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator { - public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public SpecificBeatmapPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 5f8b58d94d..dafe65f415 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -8,12 +8,12 @@ using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Game.Audio; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private PatternType convertType; - public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public DistanceObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { convertType = PatternType.None; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index f816a70ab3..2265d3d347 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -2,13 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using System.Linq; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly int endTime; private readonly PatternType convertType; - public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public EndTimeObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { endTime = (int)((HitObject as IHasDuration)?.EndTime ?? 0); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 53b059b4e2..41d4c9322b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -9,11 +9,11 @@ using osuTK; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly PatternType convertType; - public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, + public HitObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index eaf0ea0f2b..d5689c047a 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -5,8 +5,8 @@ using System; using System.Linq; using JetBrains.Annotations; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -23,14 +23,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// The random number generator to use. /// - protected readonly FastRandom Random; + protected readonly LegacyRandom Random; /// /// The beatmap which is being converted from. /// protected readonly IBeatmap OriginalBeatmap; - protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + protected PatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(hitObject, beatmap, previousPattern) { if (random == null) throw new ArgumentNullException(nameof(random)); diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs deleted file mode 100644 index a9cd7f2476..0000000000 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; - -namespace osu.Game.Rulesets.Mania.MathUtils -{ - /// - /// A PRNG specified in http://heliosphan.org/fastrandom.html. - /// - internal class FastRandom - { - private const double int_to_real = 1.0 / (int.MaxValue + 1.0); - private const uint int_mask = 0x7FFFFFFF; - private const uint y = 842502087; - private const uint z = 3579807591; - private const uint w = 273326509; - - internal uint X { get; private set; } - internal uint Y { get; private set; } = y; - internal uint Z { get; private set; } = z; - internal uint W { get; private set; } = w; - - public FastRandom(int seed) - { - X = (uint)seed; - } - - public FastRandom() - : this(Environment.TickCount) - { - } - - /// - /// Generates a random unsigned integer within the range [, ). - /// - /// The random value. - public uint NextUInt() - { - uint t = X ^ (X << 11); - X = Y; - Y = Z; - Z = W; - return W = W ^ (W >> 19) ^ t ^ (t >> 8); - } - - /// - /// Generates a random integer value within the range [0, ). - /// - /// The random value. - public int Next() => (int)(int_mask & NextUInt()); - - /// - /// Generates a random integer value within the range [0, ). - /// - /// The upper bound. - /// The random value. - public int Next(int upperBound) => (int)(NextDouble() * upperBound); - - /// - /// Generates a random integer value within the range [, ). - /// - /// The lower bound of the range. - /// The upper bound of the range. - /// The random value. - public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); - - /// - /// Generates a random double value within the range [0, 1). - /// - /// The random value. - public double NextDouble() => int_to_real * Next(); - - private uint bitBuffer; - private int bitIndex = 32; - - /// - /// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls. - /// - /// The random value. - public bool NextBool() - { - if (bitIndex == 32) - { - bitBuffer = NextUInt(); - bitIndex = 1; - - return (bitBuffer & 1) == 1; - } - - bitIndex++; - return ((bitBuffer >>= 1) & 1) == 1; - } - } -} diff --git a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs b/osu.Game/Utils/LegacyRandom.cs similarity index 79% rename from osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs rename to osu.Game/Utils/LegacyRandom.cs index 46e427e1b7..cf731aa91f 100644 --- a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs +++ b/osu.Game/Utils/LegacyRandom.cs @@ -2,27 +2,36 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Utils; -namespace osu.Game.Rulesets.Catch.MathUtils +namespace osu.Game.Utils { /// /// A PRNG specified in http://heliosphan.org/fastrandom.html. + /// Should only be used to match legacy behaviour. See for a newer alternative. /// - public class FastRandom + /// + /// Known in osu-stable code as `FastRandom`. + /// + public class LegacyRandom { private const double int_to_real = 1.0 / (int.MaxValue + 1.0); private const uint int_mask = 0x7FFFFFFF; - private const uint y_initial = 842502087; - private const uint z_initial = 3579807591; - private const uint w_initial = 273326509; - private uint x, y = y_initial, z = z_initial, w = w_initial; + private const uint y = 842502087; + private const uint z = 3579807591; + private const uint w = 273326509; - public FastRandom(int seed) + public uint X { get; private set; } + public uint Y { get; private set; } = y; + public uint Z { get; private set; } = z; + public uint W { get; private set; } = w; + + public LegacyRandom(int seed) { - x = (uint)seed; + X = (uint)seed; } - public FastRandom() + public LegacyRandom() : this(Environment.TickCount) { } @@ -33,11 +42,11 @@ namespace osu.Game.Rulesets.Catch.MathUtils /// The random value. public uint NextUInt() { - uint t = x ^ (x << 11); - x = y; - y = z; - z = w; - return w = w ^ (w >> 19) ^ t ^ (t >> 8); + uint t = X ^ (X << 11); + X = Y; + Y = Z; + Z = W; + return W = W ^ (W >> 19) ^ t ^ (t >> 8); } /// From d052321eee722155c2635560819b60a3a8a41089 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 12:11:06 +0300 Subject: [PATCH 0618/1276] Revert "Fix context menus not working" This reverts commit 51aa17eb524e70899f09c1058842e0d55f58109b. --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 18 +----------------- osu.Game/Graphics/UserInterface/OsuMenu.cs | 18 +----------------- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 20fa7d5148..b1d4691938 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -131,22 +130,7 @@ namespace osu.Game.Graphics.UserInterface BackgroundColourSelected = SelectionColour }; - protected override ScrollContainer CreateScrollContainer(Direction direction) => new DropdownScrollContainer(direction); - - // Hotfix for https://github.com/ppy/osu/issues/17961 - public class DropdownScrollContainer : OsuScrollContainer - { - public DropdownScrollContainer(Direction direction) - : base(direction) - { - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - base.OnMouseDown(e); - return true; - } - } + protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); #region DrawableOsuDropdownMenuItem diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index bfdfd32fb3..a16adcbd57 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -9,7 +9,6 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osuTK; @@ -82,22 +81,7 @@ namespace osu.Game.Graphics.UserInterface return new DrawableOsuMenuItem(item); } - protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuMenuScrollContainer(direction); - - // Hotfix for https://github.com/ppy/osu/issues/17961 - public class OsuMenuScrollContainer : OsuScrollContainer - { - public OsuMenuScrollContainer(Direction direction) - : base(direction) - { - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - base.OnMouseDown(e); - return true; - } - } + protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical) { From 99d2d7b80592777fbde57bca6aab8f8d2b49fdcb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 18:57:10 +0900 Subject: [PATCH 0619/1276] Add very basic implementation of bundled beatmap downloader --- .../TestSceneBundledBeatmapDownloader.cs | 24 ++ .../Drawables/BundledBeatmapDownloader.cs | 227 ++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs create mode 100644 osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs b/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs new file mode 100644 index 0000000000..895524e79a --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps.Drawables; + +namespace osu.Game.Tests.Visual.Online +{ + [Ignore("Only for visual testing")] + public class TestSceneBundledBeatmapDownloader : OsuTestScene + { + private BundledBeatmapDownloader downloader; + + [Test] + public void TestDownloader() + { + AddStep("Create downloader", () => + { + downloader?.Expire(); + Add(downloader = new BundledBeatmapDownloader()); + }); + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs new file mode 100644 index 0000000000..6b14ed760d --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -0,0 +1,227 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using System.Text.RegularExpressions; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Beatmaps.Drawables +{ + public class BundledBeatmapDownloader : CompositeDrawable + { + [Resolved] + private BeatmapModelDownloader beatmapDownloader { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + foreach (string filename in bundled_beatmap_filenames.OrderBy(_ => RNG.NextSingle()).Take(10)) + { + var match = Regex.Match(filename, @"([0-9]*) (.*) - (.*)\.osz"); + + beatmapDownloader.Download(new APIBeatmapSet + { + OnlineID = int.Parse(match.Groups[1].Value), + Artist = match.Groups[2].Value, + Title = match.Groups[3].Value, + }); + } + } + + private static readonly string[] bundled_beatmap_filenames = new[] + { + "682286 Yuyoyuppe - Emerald Galaxy.osz", + "682287 baker - For a Dead Girl+.osz", + "682289 Hige Driver - I Wanna Feel Your Love (feat. shully).osz", + "682290 Hige Driver - Miracle Sugite Yabai (feat. shully).osz", + "682416 Hige Driver - Palette.osz", + "682595 baker - Kimi ga Kimi ga -vocanico remix-.osz", + "716211 yuki. - Spring Signal.osz", + "716213 dark cat - BUBBLE TEA (feat. juu & cinders).osz", + "716215 LukHash - CLONED.osz", + "716219 IAHN - Snowdrop.osz", + "716249 *namirin - Senaka Awase no Kuukyo (with Kakichoco).osz", + "716390 sakuraburst - SHA.osz", + "716441 Fractal Dreamers - Paradigm Shift.osz", + "729808 Thaehan - Leprechaun.osz", + "751771 Cranky - Hanaarashi.osz", + "751772 Cranky - Ran.osz", + "751773 Cranky - Feline, the White....osz", + "751774 Function Phantom - Variable.osz", + "751779 Rin - Daishibyo set 14 ~ Sado no Futatsuiwa.osz", + "751782 Fractal Dreamers - Fata Morgana.osz", + "751785 Cranky - Chandelier - King.osz", + "751846 Fractal Dreamers - Celestial Horizon.osz", + "751866 Rin - Moriya set 08 ReEdit ~ Youkai no Yama.osz", + "751894 Fractal Dreamers - Blue Haven.osz", + "751896 Cranky - Rave 2 Rave.osz", + "751932 Cranky - La fuite des jours.osz", + "751972 Cranky - CHASER.osz", + "779173 Thaehan - Superpower.osz", + "780932 VINXIS - A Centralized View.osz", + "785572 S3RL - I'll See You Again (feat. Chi Chi).osz", + "785650 yuki. feat. setsunan - Hello! World.osz", + "785677 Dictate - Militant.osz", + "785731 S3RL - Catchit (Radio Edit).osz", + "785774 LukHash - GLITCH.osz", + "786498 Trial & Error - Tokoyami no keiyaku KEGARETA-SHOUJO feat. GUMI.osz", + "789374 Pulse - LP.osz", + "789528 James Portland - Sky.osz", + "789529 Lexurus - Gravity.osz", + "789544 Andromedik - Invasion.osz", + "789905 Gourski x Himmes - Silence.osz", + "791667 cYsmix - Babaroque (Short Ver.).osz", + "791798 cYsmix - Behind the Walls.osz", + "791845 cYsmix - Little Knight.osz", + "792241 cYsmix - Eden.osz", + "792396 cYsmix - The Ballad of a Mindless Girl.osz", + "795432 Phonetic - Journey.osz", + "831322 DJ'TEKINA//SOMETHING - Hidamari no Uta.osz", + "847764 Cranky - Crocus.osz", + "847776 Culprate & Joe Ford - Gaucho.osz", + "847812 J. Pachelbel - Canon (Cranky Remix).osz", + "847900 Cranky - Time Alter.osz", + "847930 LukHash - 8BIT FAIRY TALE.osz", + "848003 Culprate - Aurora.osz", + "848068 nanobii - popsicle beach.osz", + "848090 Trial & Error - DAI*TAN SENSATION feat. Nanahira, Mii, Aitsuki Nakuru (Short Ver.).osz", + "848259 Culprate & Skorpion - Jester.osz", + "848976 Dictate - Treason.osz", + "851543 Culprate - Florn.osz", + "864748 Thaehan - Angry Birds Epic (Remix).osz", + "873667 OISHII - ONIGIRI FREEWAY.osz", + "876227 Culprate, Keota & Sophie Meiers - Mechanic Heartbeat.osz", + "880487 cYsmix - Peer Gynt.osz", + "883088 Wisp X - Somewhere I'd Rather Be.osz", + "891333 HyuN - White Aura.osz", + "891334 HyuN - Wild Card.osz", + "891337 HyuN feat. LyuU - Cross Over.osz", + "891338 HyuN & Ritoru - Apocalypse in Love.osz", + "891339 HyuN feat. Ato - Asu wa Ame ga Yamukara.osz", + "891345 HyuN - Infinity Heaven.osz", + "891348 HyuN - Guitian.osz", + "891356 HyuN - Legend of Genesis.osz", + "891366 HyuN - Illusion of Inflict.osz", + "891417 HyuN feat. Yu-A - My life is for you.osz", + "891441 HyuN - You'Re aRleAdY dEAd.osz", + "891632 HyuN feat. YURI - Disorder.osz", + "891712 HyuN - Tokyo's Starlight.osz", + "901091 *namirin - Ciel etoile.osz", + "916990 *namirin - Koishiteiku Planet.osz", + "929284 tieff - Sense of Nostalgia.osz", + "933940 Ben Briggs - Yes (Maybe).osz", + "934415 Ben Briggs - Fearless Living.osz", + "934627 Ben Briggs - New Game Plus.osz", + "934666 Ben Briggs - Wave Island.osz", + "936126 siromaru + cranky - conflict.osz", + "940377 onumi - ARROGANCE.osz", + "940597 tieff - Take Your Swimsuit.osz", + "941085 tieff - Our Story.osz", + "949297 tieff - Sunflower.osz", + "952380 Ben Briggs - Why Are We Yelling.osz", + "954272 *namirin - Kanzen Shouri*Esper Girl.osz", + "955866 KIRA & Heartbreaker - B.B.F (feat. Hatsune Miku & Kagamine Rin).osz", + "961320 Kuba Oms - All In All.osz", + "964553 The Flashbulb - You Take the World's Weight Away.osz", + "965651 Fractal Dreamers - Ad Astra.osz", + "966225 The Flashbulb - Passage D.osz", + "966324 DJ'TEKINA//SOMETHING - Hidamari no Uta.osz", + "972810 James Landino & Kabuki - Birdsong.osz", + "972932 James Landino - Hide And Seek.osz", + "977276 The Flashbulb - Mellann.osz", + "981616 *namirin - Mizutamari Tobikoete (with Nanahira).osz", + "985788 Loki - Wizard's Tower.osz", + "996628 OISHII - ONIGIRI FREEWAY.osz", + "996898 HyuN - White Aura.osz", + "1003554 yuki. - Nadeshiko Sensation.osz", + "1014936 Thaehan - Bwa !.osz", + "1019827 UNDEAD CORPORATION - Sad Dream.osz", + "1020213 Creo - Idolize.osz", + "1021450 Thaehan - Chiptune & Baroque.osz", + "707824 Fractal Dreamers - Fortuna Redux.osz", + "789553 Cranky - Ran.osz", + "827822 Function Phantom - Neuronecia.osz", + "847323 Nakanojojo - Bittersweet (feat. Kuishinboakachan a.k.a Kiato).osz", + "847433 Trial & Error - Tokoyami no keiyaku KEGARETA-SHOUJO feat. GUMI.osz", + "847576 dark cat - hot chocolate.osz", + "847957 Wisp X - Final Moments.osz", + "876282 VINXIS - Greetings.osz", + "876648 Thaehan - Angry Birds Epic (Remix).osz", + "877069 IAHN - Transform (Original Mix).osz", + "877496 Thaehan - Leprechaun.osz", + "877935 Thaehan - Overpowered.osz", + "878344 yuki. - Be Your Light.osz", + "918446 VINXIS - Facade.osz", + "918903 LukHash - Ghosts.osz", + "919251 *namirin - Hitokoto no Kyori.osz", + "919704 S3RL - I Will Pick You Up (feat. Tamika).osz", + "921535 SOOOO - Raven Haven.osz", + "927206 *namirin - Kanzen Shouri*Esper Girl.osz", + "927544 Camellia feat. Nanahira - Kansoku Eisei.osz", + "930806 Nakanojojo - Pararara (feat. Amekoya).osz", + "931741 Camellia - Quaoar.osz", + "935699 Rin - Mythic set ~ Heart-Stirring Urban Legends.osz", + "935732 Thaehan - Yuujou.osz", + "941145 Function Phantom - Euclid.osz", + "942334 Dictate - Cauldron.osz", + "946540 nanobii - astral blast.osz", + "948844 Rin - Kishinjou set 01 ~ Mist Lake.osz", + "949122 Wisp X - Petal.osz", + "951618 Rin - Kishinjou set 02 ~ Mermaid from the Uncharted Land.osz", + "957412 Rin - Lunatic set 16 ~ The Space Shrine Maiden Returns Home.osz", + "961335 Thaehan - Insert Coin.osz", + "965178 The Flashbulb - DIDJ PVC.osz", + "966087 The Flashbulb - Creep.osz", + "966277 The Flashbulb - Amen Iraq.osz", + "966407 LukHash - ROOM 12.osz", + "966451 The Flashbulb - Six Acid Strings.osz", + "972301 BilliumMoto - four veiled stars.osz", + "973173 nanobii - popsicle beach.osz", + "973954 BilliumMoto - Rocky Buinne (Short Ver.).osz", + "975435 BilliumMoto - life flashes before weeb eyes.osz", + "978759 L. V. Beethoven - Moonlight Sonata (Cranky Remix).osz", + "982559 BilliumMoto - HDHR.osz", + "984361 The Flashbulb - Ninedump.osz", + "1023681 Inferi - The Ruin of Mankind.osz", + "1034358 ALEPH - The Evil Spirit.osz", + "1037567 ALEPH - Scintillations.osz", + "554256 Helblinde - When Time Sleeps.osz", + "693123 yuki. - Nadeshiko Sensation.osz", + "767009 OISHII - PIZZA PLAZA.osz", + "767346 Thaehan - Bwa !.osz", + "815162 VINXIS - Greetings.osz", + "840964 cYsmix - Breeze.osz", + "932657 Wisp X - Eventide.osz", + "933700 onumi - CONFUSION PART ONE.osz", + "933984 onumi - PERSONALITY.osz", + "934785 onumi - FAKE.osz", + "936545 onumi - REGRET PART ONE.osz", + "943803 Fractal Dreamers - Everything for a Dream.osz", + "943876 S3RL - I Will Pick You Up (feat. Tamika).osz", + "946773 Trial & Error - DREAMING COLOR (Short Ver.).osz", + "955808 Trial & Error - Tokoyami no keiyaku KEGARETA-SHOUJO feat. GUMI (Short Ver.).osz", + "957808 Fractal Dreamers - Module_410.osz", + "957842 antiPLUR - One Life Left to Live.osz", + "965730 The Flashbulb - Lawn Wake IV (Black).osz", + "966240 Creo - Challenger.osz", + "968232 Rin - Lunatic set 15 ~ The Moon as Seen from the Shrine.osz", + "972302 VINXIS - A Centralized View.osz", + "972887 HyuN - Illusion of Inflict.osz", + "1008600 LukHash - WHEN AN ANGEL DIES.osz", + "1032103 LukHash - H8 U.osz", + "943516 antiPLUR - Clockwork Spooks.osz", + "946394 VINXIS - Three Times The Original Charm.osz", + "966408 antiPLUR - One Life Left to Live.osz", + "971561 antiPLUR - Runengon.osz", + "983864 James Landino - Shiba Island.osz", + "989512 BilliumMoto - 1xMISS.osz", + "994104 James Landino - Reaction feat. Slyleaf.osz", + "1003217 nekodex - circles!.osz", + "1009907 James Landino & Kabuki - Birdsong.osz", + "1015169 Thaehan - Insert Coin.osz", + }; + } +} From b042f1cad55680e61d4656522b4b4cb500a9e05a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 18:57:20 +0900 Subject: [PATCH 0620/1276] Add screen to perform bundled beatmap downloads --- .../FirstRunSetup/ScreenBundledBeatmaps.cs | 47 +++++++++++++++++++ osu.Game/Overlays/FirstRunSetupOverlay.cs | 1 + 2 files changed, 48 insertions(+) create mode 100644 osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs new file mode 100644 index 0000000000..fae852860b --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.ComponentModel; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.FirstRunSetup +{ + [Description("Bundled Beatmaps")] + public class ScreenBundledBeatmaps : FirstRunSetupScreen + { + private TriangleButton downloadButton; + + [BackgroundDependencyLoader] + private void load() + { + Content.Children = new Drawable[] + { + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + { + Text = "osu! doesn't come with any beatmaps pre-loaded. To get started, we have some recommended beatmaps.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + downloadButton = new TriangleButton + { + Width = 300, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Download beatmap selection", + Action = download + } + }; + } + + private void download() + { + AddInternal(new BundledBeatmapDownloader()); + downloadButton.Enabled.Value = false; + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 82158793a2..b43f14522e 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -60,6 +60,7 @@ namespace osu.Game.Overlays private readonly Type[] steps = { typeof(ScreenWelcome), + typeof(ScreenBundledBeatmaps), typeof(ScreenUIScale) }; From 973dd4bfa9eab3d7d7ee934ed4b02df0a6c940ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 19:41:13 +0900 Subject: [PATCH 0621/1276] Add initial flow for download button / progress display --- .../Drawables/BundledBeatmapDownloader.cs | 51 ++++++++++++++++--- .../FirstRunSetup/ScreenBundledBeatmaps.cs | 29 ++++++++++- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 6b14ed760d..5937186705 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -1,37 +1,55 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; +using osu.Game.Database; +using osu.Game.Online; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; namespace osu.Game.Beatmaps.Drawables { public class BundledBeatmapDownloader : CompositeDrawable { - [Resolved] - private BeatmapModelDownloader beatmapDownloader { get; set; } + public IEnumerable DownloadTrackers => downloadTrackers; + + private readonly List downloadTrackers = new List(); [BackgroundDependencyLoader] - private void load() + private void load(BeatmapManager beatmapManager, IAPIProvider api, INotificationOverlay notifications) { + var beatmapDownloader = new BundledBeatmapModelDownloader(beatmapManager, api) + { + PostNotification = notifications.Post + }; + foreach (string filename in bundled_beatmap_filenames.OrderBy(_ => RNG.NextSingle()).Take(10)) { var match = Regex.Match(filename, @"([0-9]*) (.*) - (.*)\.osz"); - beatmapDownloader.Download(new APIBeatmapSet + var beatmapSet = new APIBeatmapSet { OnlineID = int.Parse(match.Groups[1].Value), Artist = match.Groups[2].Value, Title = match.Groups[3].Value, - }); + }; + + var beatmapDownloadTracker = new BeatmapDownloadTracker(beatmapSet); + downloadTrackers.Add(beatmapDownloadTracker); + AddInternal(beatmapDownloadTracker); + + beatmapDownloader.Download(beatmapSet); } } - private static readonly string[] bundled_beatmap_filenames = new[] + private static readonly string[] bundled_beatmap_filenames = { "682286 Yuyoyuppe - Emerald Galaxy.osz", "682287 baker - For a Dead Girl+.osz", @@ -223,5 +241,26 @@ namespace osu.Game.Beatmaps.Drawables "1009907 James Landino & Kabuki - Birdsong.osz", "1015169 Thaehan - Insert Coin.osz", }; + + private class BundledBeatmapModelDownloader : BeatmapModelDownloader + { + public BundledBeatmapModelDownloader(IModelImporter beatmapImporter, IAPIProvider api) + : base(beatmapImporter, api) + { + } + + protected override ArchiveDownloadRequest CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize) + => new BundledBeatmapDownloadRequest(set, minimiseDownloadSize); + + public class BundledBeatmapDownloadRequest : DownloadBeatmapSetRequest + { + protected override string Uri => $"https://assets.ppy.sh/client-resources/bundled/{Model.OnlineID}.osz"; + + public BundledBeatmapDownloadRequest(IBeatmapSetInfo beatmapSetInfo, bool minimiseDownloadSize) + : base(beatmapSetInfo, minimiseDownloadSize) + { + } + } + } } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs index fae852860b..97f429bcf2 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs @@ -2,12 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using System.ComponentModel; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osuTK.Graphics; namespace osu.Game.Overlays.FirstRunSetup { @@ -16,6 +19,9 @@ namespace osu.Game.Overlays.FirstRunSetup { private TriangleButton downloadButton; + private ProgressBar progressBar; + private BundledBeatmapDownloader downloader; + [BackgroundDependencyLoader] private void load() { @@ -34,14 +40,33 @@ namespace osu.Game.Overlays.FirstRunSetup Origin = Anchor.TopCentre, Text = "Download beatmap selection", Action = download - } + }, }; + + downloadButton.Add(progressBar = new ProgressBar(false) + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + FillColour = Color4.Aqua, + Alpha = 0.5f, + Depth = float.MinValue + }); } private void download() { - AddInternal(new BundledBeatmapDownloader()); + AddInternal(downloader = new BundledBeatmapDownloader()); downloadButton.Enabled.Value = false; + + foreach (var tracker in downloader.DownloadTrackers) + tracker.State.BindValueChanged(_ => updateProgress()); + } + + private void updateProgress() + { + double progress = (double)downloader.DownloadTrackers.Count(t => t.State.Value == DownloadState.LocallyAvailable) / downloader.DownloadTrackers.Count(); + + this.TransformBindableTo(progressBar.Current, progress, 1000, Easing.OutQuint); } } } From b424d20f267b26104eca245cf1f3de926f29849b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 20:18:46 +0900 Subject: [PATCH 0622/1276] Fix rounded buttons not allowing custom colour specifications --- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index 23ebc6e98d..f535a32b39 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; +using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -28,7 +29,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 [BackgroundDependencyLoader(true)] private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) { - BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; + if (BackgroundColour == Color4.White) + BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; } protected override void LoadComplete() From 58399a5113e9bc5906e529672fc917b19a33a187 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 20:19:11 +0900 Subject: [PATCH 0623/1276] Add tutorial download support and improve the visuals "slightly" --- .../TestSceneBundledBeatmapDownloader.cs | 2 +- .../Drawables/BundledBeatmapDownloader.cs | 37 +++++-- .../FirstRunSetup/ScreenBundledBeatmaps.cs | 98 +++++++++++++++---- 3 files changed, 108 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs b/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs index 895524e79a..2af1c9a0f0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Create downloader", () => { downloader?.Expire(); - Add(downloader = new BundledBeatmapDownloader()); + Add(downloader = new BundledBeatmapDownloader(false)); }); } } diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 5937186705..0bdaaaca22 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -22,15 +22,34 @@ namespace osu.Game.Beatmaps.Drawables private readonly List downloadTrackers = new List(); - [BackgroundDependencyLoader] - private void load(BeatmapManager beatmapManager, IAPIProvider api, INotificationOverlay notifications) - { - var beatmapDownloader = new BundledBeatmapModelDownloader(beatmapManager, api) - { - PostNotification = notifications.Post - }; + private readonly List downloadableFilenames = new List(); - foreach (string filename in bundled_beatmap_filenames.OrderBy(_ => RNG.NextSingle()).Take(10)) + private BundledBeatmapModelDownloader beatmapDownloader; + + public BundledBeatmapDownloader(bool onlyTutorial) + { + if (onlyTutorial) + downloadableFilenames.Add(tutorial_filename); + else + downloadableFilenames.AddRange(bundled_beatmap_filenames); + } + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var localDependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + localDependencies.CacheAs(beatmapDownloader = new BundledBeatmapModelDownloader(parent.Get(), parent.Get()) + { + PostNotification = parent.Get().Post + }); + + return localDependencies; + } + + [BackgroundDependencyLoader] + private void load() + { + foreach (string filename in downloadableFilenames.OrderBy(_ => RNG.NextSingle()).Take(10)) { var match = Regex.Match(filename, @"([0-9]*) (.*) - (.*)\.osz"); @@ -49,6 +68,8 @@ namespace osu.Game.Beatmaps.Drawables } } + private const string tutorial_filename = "1011011 nekodex - new beginnings.osz"; + private static readonly string[] bundled_beatmap_filenames = { "682286 Yuyoyuppe - Emerald Galaxy.osz", diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs index 97f429bcf2..518903e74b 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs @@ -9,64 +9,122 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Online; -using osuTK.Graphics; +using osuTK; namespace osu.Game.Overlays.FirstRunSetup { [Description("Bundled Beatmaps")] public class ScreenBundledBeatmaps : FirstRunSetupScreen { - private TriangleButton downloadButton; + private RoundedButton downloadBundledButton; - private ProgressBar progressBar; - private BundledBeatmapDownloader downloader; + private ProgressBar progressBarBundled; + + private RoundedButton downloadTutorialButton; + private ProgressBar progressBarTutorial; + + private BundledBeatmapDownloader tutorialDownloader; + private BundledBeatmapDownloader bundledDownloader; [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { + Vector2 buttonSize = new Vector2(500, 80); + Content.Children = new Drawable[] { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Text = "osu! doesn't come with any beatmaps pre-loaded. To get started, we have some recommended beatmaps.", + Text = + "osu! doesn't come with any beatmaps pre-loaded. To get started, we have some recommended beatmaps. You can obtain more beatmaps from the main menu \"browse\" button at any time.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, - downloadButton = new TriangleButton + downloadTutorialButton = new RoundedButton { - Width = 300, + Size = buttonSize, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = "Download beatmap selection", - Action = download + BackgroundColour = colours.Pink3, + Text = "Download tutorial", + Action = downloadTutorial }, + downloadBundledButton = new RoundedButton + { + Size = buttonSize, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + BackgroundColour = colours.Blue3, + Text = "Download beatmap selection", + Action = downloadBundled + }, + // TODO: add stable import button if a stable install is detected. }; - downloadButton.Add(progressBar = new ProgressBar(false) + downloadTutorialButton.Add(progressBarTutorial = new ProgressBar(false) { RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, - FillColour = Color4.Aqua, + FillColour = downloadTutorialButton.BackgroundColour, + Alpha = 0.5f, + Depth = float.MinValue + }); + + downloadBundledButton.Add(progressBarBundled = new ProgressBar(false) + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + FillColour = downloadBundledButton.BackgroundColour, Alpha = 0.5f, Depth = float.MinValue }); } - private void download() + private void downloadTutorial() { - AddInternal(downloader = new BundledBeatmapDownloader()); - downloadButton.Enabled.Value = false; + if (tutorialDownloader != null) + return; - foreach (var tracker in downloader.DownloadTrackers) - tracker.State.BindValueChanged(_ => updateProgress()); + tutorialDownloader = new BundledBeatmapDownloader(true); + + AddInternal(tutorialDownloader); + + var downloadTracker = tutorialDownloader.DownloadTrackers.First(); + + downloadTracker.Progress.BindValueChanged(progress => + { + progressBarTutorial.Current.Value = progress.NewValue; + + if (progress.NewValue == 1) + downloadTutorialButton.Enabled.Value = false; + }, true); } - private void updateProgress() + private void downloadBundled() { - double progress = (double)downloader.DownloadTrackers.Count(t => t.State.Value == DownloadState.LocallyAvailable) / downloader.DownloadTrackers.Count(); + if (bundledDownloader != null) + return; - this.TransformBindableTo(progressBar.Current, progress, 1000, Easing.OutQuint); + // downloadBundledButton.Enabled.Value = false; + + bundledDownloader = new BundledBeatmapDownloader(false); + + AddInternal(bundledDownloader); + + foreach (var tracker in bundledDownloader.DownloadTrackers) + tracker.State.BindValueChanged(_ => updateProgress(), true); + + void updateProgress() + { + double progress = (double)bundledDownloader.DownloadTrackers.Count(t => t.State.Value == DownloadState.LocallyAvailable) / bundledDownloader.DownloadTrackers.Count(); + + this.TransformBindableTo(progressBarBundled.Current, progress, 1000, Easing.OutQuint); + + if (progress == 1) + downloadBundledButton.Enabled.Value = false; + } } } } From d0564657423632e9ae41978b085f85682054ba6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 16:08:32 +0900 Subject: [PATCH 0624/1276] Remove explicit dependency on `INotificationOverlay` --- osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 0bdaaaca22..029bb37c18 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -38,10 +38,10 @@ namespace osu.Game.Beatmaps.Drawables { var localDependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - localDependencies.CacheAs(beatmapDownloader = new BundledBeatmapModelDownloader(parent.Get(), parent.Get()) - { - PostNotification = parent.Get().Post - }); + localDependencies.CacheAs(beatmapDownloader = new BundledBeatmapModelDownloader(parent.Get(), parent.Get())); + + if (parent.Get() is INotificationOverlay notifications) + beatmapDownloader.PostNotification = notifications.Post; return localDependencies; } From 3c0bdcaf3804717eb827bdbbcf579c6e8345143a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 16:09:00 +0900 Subject: [PATCH 0625/1276] Rename screen, add tests and add stable import step --- .../TestSceneFirstRunScreenBundledBeatmaps.cs | 24 ++++++ ...enBundledBeatmaps.cs => ScreenBeatmaps.cs} | 81 ++++++++++++++----- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 3 files changed, 84 insertions(+), 23 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBundledBeatmaps.cs rename osu.Game/Overlays/FirstRunSetup/{ScreenBundledBeatmaps.cs => ScreenBeatmaps.cs} (52%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBundledBeatmaps.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBundledBeatmaps.cs new file mode 100644 index 0000000000..51065939f0 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBundledBeatmaps.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Screens; +using osu.Game.Overlays; +using osu.Game.Overlays.FirstRunSetup; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneFirstRunScreenBundledBeatmaps : OsuManualInputManagerTestScene + { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + public TestSceneFirstRunScreenBundledBeatmaps() + { + AddStep("load screen", () => + { + Child = new ScreenStack(new ScreenBeatmaps()); + }); + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs similarity index 52% rename from osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs rename to osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 518903e74b..aff084be73 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -1,3 +1,4 @@ +#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. @@ -6,39 +7,51 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps.Drawables; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Localisation; using osu.Game.Online; using osuTK; namespace osu.Game.Overlays.FirstRunSetup { - [Description("Bundled Beatmaps")] - public class ScreenBundledBeatmaps : FirstRunSetupScreen + [Description("Obtaining Beatmaps")] + public class ScreenBeatmaps : FirstRunSetupScreen { - private RoundedButton downloadBundledButton; + private RoundedButton downloadBundledButton = null!; + private RoundedButton importBeatmapsButton = null!; - private ProgressBar progressBarBundled; + private ProgressBar progressBarBundled = null!; - private RoundedButton downloadTutorialButton; - private ProgressBar progressBarTutorial; + private RoundedButton downloadTutorialButton = null!; + private ProgressBar progressBarTutorial = null!; - private BundledBeatmapDownloader tutorialDownloader; - private BundledBeatmapDownloader bundledDownloader; + private BundledBeatmapDownloader tutorialDownloader = null!; + private BundledBeatmapDownloader bundledDownloader = null!; - [BackgroundDependencyLoader] - private void load(OsuColour colours) + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuColour colours, OverlayColourProvider overlayColourProvider, LegacyImportManager? legacyImportManager) { - Vector2 buttonSize = new Vector2(500, 80); + Vector2 buttonSize = new Vector2(500, 60); Content.Children = new Drawable[] { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { + Colour = overlayColourProvider.Content1, Text = - "osu! doesn't come with any beatmaps pre-loaded. To get started, we have some recommended beatmaps. You can obtain more beatmaps from the main menu \"browse\" button at any time.", + "\"Beatmaps\" are what we call playable levels in osu!.\n\nosu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + { + Colour = overlayColourProvider.Content1, + Text = + "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -51,6 +64,13 @@ namespace osu.Game.Overlays.FirstRunSetup Text = "Download tutorial", Action = downloadTutorial }, + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + { + Colour = overlayColourProvider.Content1, + Text = "To get you started, we have some recommended beatmaps.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, downloadBundledButton = new RoundedButton { Size = buttonSize, @@ -60,9 +80,34 @@ namespace osu.Game.Overlays.FirstRunSetup Text = "Download beatmap selection", Action = downloadBundled }, - // TODO: add stable import button if a stable install is detected. + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + { + Colour = overlayColourProvider.Content1, + Text = "If you have an existing osu! install, you can also choose to import your existing beatmap collection.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + importBeatmapsButton = new RoundedButton + { + Size = buttonSize, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + BackgroundColour = colours.Blue3, + Text = MaintenanceSettingsStrings.ImportBeatmapsFromStable, + Action = () => + { + importBeatmapsButton.Enabled.Value = false; + legacyImportManager?.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => importBeatmapsButton.Enabled.Value = true)); + } + }, + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + { + Colour = overlayColourProvider.Content1, + Text = "You can also obtain more beatmaps from the main menu \"browse\" button at any time.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, }; - downloadTutorialButton.Add(progressBarTutorial = new ProgressBar(false) { RelativeSizeAxes = Axes.Both, @@ -84,9 +129,6 @@ namespace osu.Game.Overlays.FirstRunSetup private void downloadTutorial() { - if (tutorialDownloader != null) - return; - tutorialDownloader = new BundledBeatmapDownloader(true); AddInternal(tutorialDownloader); @@ -104,11 +146,6 @@ namespace osu.Game.Overlays.FirstRunSetup private void downloadBundled() { - if (bundledDownloader != null) - return; - - // downloadBundledButton.Enabled.Value = false; - bundledDownloader = new BundledBeatmapDownloader(false); AddInternal(bundledDownloader); diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index b43f14522e..c151554081 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays private readonly Type[] steps = { typeof(ScreenWelcome), - typeof(ScreenBundledBeatmaps), + typeof(ScreenBeatmaps), typeof(ScreenUIScale) }; From 081275e0b996b5198c116ebb2903d0ef9898e0e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 16:36:25 +0900 Subject: [PATCH 0626/1276] Adjust text and layout --- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 6 +++--- osu.Game/Overlays/FirstRunSetupOverlay.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index aff084be73..b1e2394f89 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.FirstRunSetup { Colour = overlayColourProvider.Content1, Text = - "\"Beatmaps\" are what we call playable levels in osu!.\n\nosu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.", + "\"Beatmaps\" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.FirstRunSetup Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, BackgroundColour = colours.Pink3, - Text = "Download tutorial", + Text = "Get the osu! tutorial", Action = downloadTutorial }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.FirstRunSetup Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, BackgroundColour = colours.Blue3, - Text = "Download beatmap selection", + Text = "Get recommended beatmaps", Action = downloadBundled }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index c151554081..13fb73bd27 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 50 }, + Padding = new MarginPadding { Horizontal = 70 * 2 }, Child = new InputBlockingContainer { Masking = true, @@ -104,7 +104,7 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Vertical = 20, - Horizontal = 20, + Horizontal = 70, }, } }, From c860eb46b0274e28a32aff95c3935c37290fec8f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 17:11:29 +0900 Subject: [PATCH 0627/1276] Split out button implementation into its own class --- .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 97 ++++++++++++------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index b1e2394f89..893c940a1d 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; @@ -21,19 +22,18 @@ namespace osu.Game.Overlays.FirstRunSetup [Description("Obtaining Beatmaps")] public class ScreenBeatmaps : FirstRunSetupScreen { - private RoundedButton downloadBundledButton = null!; - private RoundedButton importBeatmapsButton = null!; + private ProgressRoundedButton downloadBundledButton = null!; + private ProgressRoundedButton importBeatmapsButton = null!; + private ProgressRoundedButton downloadTutorialButton = null!; - private ProgressBar progressBarBundled = null!; + private BundledBeatmapDownloader? tutorialDownloader; + private BundledBeatmapDownloader? bundledDownloader; - private RoundedButton downloadTutorialButton = null!; - private ProgressBar progressBarTutorial = null!; - - private BundledBeatmapDownloader tutorialDownloader = null!; - private BundledBeatmapDownloader bundledDownloader = null!; + [Resolved] + private OsuColour colours { get; set; } = null!; [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, OverlayColourProvider overlayColourProvider, LegacyImportManager? legacyImportManager) + private void load(OverlayColourProvider overlayColourProvider, LegacyImportManager? legacyImportManager) { Vector2 buttonSize = new Vector2(500, 60); @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, - downloadTutorialButton = new RoundedButton + downloadTutorialButton = new ProgressRoundedButton { Size = buttonSize, Anchor = Anchor.TopCentre, @@ -71,7 +71,7 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, - downloadBundledButton = new RoundedButton + downloadBundledButton = new ProgressRoundedButton { Size = buttonSize, Anchor = Anchor.TopCentre, @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, - importBeatmapsButton = new RoundedButton + importBeatmapsButton = new ProgressRoundedButton { Size = buttonSize, Anchor = Anchor.TopCentre, @@ -97,7 +97,13 @@ namespace osu.Game.Overlays.FirstRunSetup Action = () => { importBeatmapsButton.Enabled.Value = false; - legacyImportManager?.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => importBeatmapsButton.Enabled.Value = true)); + legacyImportManager?.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => + { + // TODO: can we know if the import was successful? + // if so we should turn the button green and disable it in that case alone. + // importBeatmapsButton.Enabled.Value = true; + importBeatmapsButton.Complete(); + })); } }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) @@ -108,27 +114,13 @@ namespace osu.Game.Overlays.FirstRunSetup AutoSizeAxes = Axes.Y }, }; - downloadTutorialButton.Add(progressBarTutorial = new ProgressBar(false) - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, - FillColour = downloadTutorialButton.BackgroundColour, - Alpha = 0.5f, - Depth = float.MinValue - }); - - downloadBundledButton.Add(progressBarBundled = new ProgressBar(false) - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, - FillColour = downloadBundledButton.BackgroundColour, - Alpha = 0.5f, - Depth = float.MinValue - }); } private void downloadTutorial() { + if (tutorialDownloader != null) + return; + tutorialDownloader = new BundledBeatmapDownloader(true); AddInternal(tutorialDownloader); @@ -137,15 +129,18 @@ namespace osu.Game.Overlays.FirstRunSetup downloadTracker.Progress.BindValueChanged(progress => { - progressBarTutorial.Current.Value = progress.NewValue; + downloadTutorialButton.Current.Value = progress.NewValue; if (progress.NewValue == 1) - downloadTutorialButton.Enabled.Value = false; + downloadTutorialButton.Complete(); }, true); } private void downloadBundled() { + if (bundledDownloader != null) + return; + bundledDownloader = new BundledBeatmapDownloader(false); AddInternal(bundledDownloader); @@ -157,10 +152,44 @@ namespace osu.Game.Overlays.FirstRunSetup { double progress = (double)bundledDownloader.DownloadTrackers.Count(t => t.State.Value == DownloadState.LocallyAvailable) / bundledDownloader.DownloadTrackers.Count(); - this.TransformBindableTo(progressBarBundled.Current, progress, 1000, Easing.OutQuint); + this.TransformBindableTo(downloadBundledButton.Current, progress, 2000, Easing.OutQuint); if (progress == 1) - downloadBundledButton.Enabled.Value = false; + downloadBundledButton.Complete(); + } + } + + private class ProgressRoundedButton : RoundedButton + { + [Resolved] + private OsuColour colours { get; set; } = null!; + + private ProgressBar progressBar = null!; + + public Bindable Current => progressBar.Current; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(progressBar = new ProgressBar(false) + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + FillColour = BackgroundColour, + Alpha = 0.5f, + Depth = float.MinValue + }); + } + + public void Complete() + { + Enabled.Value = false; + + Background.FadeColour(colours.Green, 500, Easing.OutQuint); + progressBar.FillColour = colours.Green; + + this.TransformBindableTo(Current, 1, 500, Easing.OutQuint); } } } From 18a38f7e23786e2e83de7a9c6478e440b64fe8aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 17:46:00 +0900 Subject: [PATCH 0628/1276] Move `FastRandom` to `LegacyRandom` in `osu.Game` project --- .../Beatmaps/CatchBeatmapProcessor.cs | 8 +- .../Beatmaps/ManiaBeatmapConverter.cs | 8 +- .../Legacy/DistanceObjectPatternGenerator.cs | 4 +- .../Legacy/EndTimeObjectPatternGenerator.cs | 4 +- .../Legacy/HitObjectPatternGenerator.cs | 4 +- .../Patterns/Legacy/PatternGenerator.cs | 6 +- .../MathUtils/FastRandom.cs | 95 ------------------- .../Utils/LegacyRandom.cs | 37 +++++--- 8 files changed, 40 insertions(+), 126 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs rename osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs => osu.Game/Utils/LegacyRandom.cs (79%) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 346a09cac8..ab61b14ac4 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.MathUtils; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Utils; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps public void ApplyPositionOffsets(IBeatmap beatmap) { - var rng = new FastRandom(RNG_SEED); + var rng = new LegacyRandom(RNG_SEED); float? lastPosition = null; double lastStartTime = 0; @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps initialiseHyperDash(beatmap); } - private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) + private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, LegacyRandom rng) { float offsetPosition = hitObject.OriginalX; double startTime = hitObject.StartTime; @@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps /// The position which the offset should be applied to. /// The maximum offset, cannot exceed 20px. /// The random number generator. - private static void applyRandomOffset(ref float position, double maxOffset, FastRandom rng) + private static void applyRandomOffset(ref float position, double maxOffset, LegacyRandom rng) { bool right = rng.NextBool(); float rand = Math.Min(20, (float)rng.Next(0, Math.Max(0, maxOffset))); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 47e0e6d7b1..207c6907c8 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -11,8 +11,8 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Beatmaps.Patterns; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; +using osu.Game.Utils; using osuTK; namespace osu.Game.Rulesets.Mania.Beatmaps @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private readonly int originalTargetColumns; // Internal for testing purposes - internal FastRandom Random { get; private set; } + internal LegacyRandom Random { get; private set; } private Pattern lastPattern = new Pattern(); @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps IBeatmapDifficultyInfo difficulty = original.Difficulty; int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate); - Random = new FastRandom(seed); + Random = new LegacyRandom(seed); return base.ConvertBeatmap(original, cancellationToken); } @@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator { - public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public SpecificBeatmapPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 5f8b58d94d..dafe65f415 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -8,12 +8,12 @@ using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Game.Audio; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private PatternType convertType; - public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public DistanceObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { convertType = PatternType.None; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index f816a70ab3..2265d3d347 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -2,13 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using System.Linq; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly int endTime; private readonly PatternType convertType; - public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public EndTimeObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { endTime = (int)((HitObject as IHasDuration)?.EndTime ?? 0); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 53b059b4e2..41d4c9322b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -9,11 +9,11 @@ using osuTK; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly PatternType convertType; - public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, + public HitObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index eaf0ea0f2b..d5689c047a 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -5,8 +5,8 @@ using System; using System.Linq; using JetBrains.Annotations; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -23,14 +23,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// The random number generator to use. /// - protected readonly FastRandom Random; + protected readonly LegacyRandom Random; /// /// The beatmap which is being converted from. /// protected readonly IBeatmap OriginalBeatmap; - protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + protected PatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(hitObject, beatmap, previousPattern) { if (random == null) throw new ArgumentNullException(nameof(random)); diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs deleted file mode 100644 index a9cd7f2476..0000000000 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; - -namespace osu.Game.Rulesets.Mania.MathUtils -{ - /// - /// A PRNG specified in http://heliosphan.org/fastrandom.html. - /// - internal class FastRandom - { - private const double int_to_real = 1.0 / (int.MaxValue + 1.0); - private const uint int_mask = 0x7FFFFFFF; - private const uint y = 842502087; - private const uint z = 3579807591; - private const uint w = 273326509; - - internal uint X { get; private set; } - internal uint Y { get; private set; } = y; - internal uint Z { get; private set; } = z; - internal uint W { get; private set; } = w; - - public FastRandom(int seed) - { - X = (uint)seed; - } - - public FastRandom() - : this(Environment.TickCount) - { - } - - /// - /// Generates a random unsigned integer within the range [, ). - /// - /// The random value. - public uint NextUInt() - { - uint t = X ^ (X << 11); - X = Y; - Y = Z; - Z = W; - return W = W ^ (W >> 19) ^ t ^ (t >> 8); - } - - /// - /// Generates a random integer value within the range [0, ). - /// - /// The random value. - public int Next() => (int)(int_mask & NextUInt()); - - /// - /// Generates a random integer value within the range [0, ). - /// - /// The upper bound. - /// The random value. - public int Next(int upperBound) => (int)(NextDouble() * upperBound); - - /// - /// Generates a random integer value within the range [, ). - /// - /// The lower bound of the range. - /// The upper bound of the range. - /// The random value. - public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); - - /// - /// Generates a random double value within the range [0, 1). - /// - /// The random value. - public double NextDouble() => int_to_real * Next(); - - private uint bitBuffer; - private int bitIndex = 32; - - /// - /// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls. - /// - /// The random value. - public bool NextBool() - { - if (bitIndex == 32) - { - bitBuffer = NextUInt(); - bitIndex = 1; - - return (bitBuffer & 1) == 1; - } - - bitIndex++; - return ((bitBuffer >>= 1) & 1) == 1; - } - } -} diff --git a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs b/osu.Game/Utils/LegacyRandom.cs similarity index 79% rename from osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs rename to osu.Game/Utils/LegacyRandom.cs index 46e427e1b7..cf731aa91f 100644 --- a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs +++ b/osu.Game/Utils/LegacyRandom.cs @@ -2,27 +2,36 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Utils; -namespace osu.Game.Rulesets.Catch.MathUtils +namespace osu.Game.Utils { /// /// A PRNG specified in http://heliosphan.org/fastrandom.html. + /// Should only be used to match legacy behaviour. See for a newer alternative. /// - public class FastRandom + /// + /// Known in osu-stable code as `FastRandom`. + /// + public class LegacyRandom { private const double int_to_real = 1.0 / (int.MaxValue + 1.0); private const uint int_mask = 0x7FFFFFFF; - private const uint y_initial = 842502087; - private const uint z_initial = 3579807591; - private const uint w_initial = 273326509; - private uint x, y = y_initial, z = z_initial, w = w_initial; + private const uint y = 842502087; + private const uint z = 3579807591; + private const uint w = 273326509; - public FastRandom(int seed) + public uint X { get; private set; } + public uint Y { get; private set; } = y; + public uint Z { get; private set; } = z; + public uint W { get; private set; } = w; + + public LegacyRandom(int seed) { - x = (uint)seed; + X = (uint)seed; } - public FastRandom() + public LegacyRandom() : this(Environment.TickCount) { } @@ -33,11 +42,11 @@ namespace osu.Game.Rulesets.Catch.MathUtils /// The random value. public uint NextUInt() { - uint t = x ^ (x << 11); - x = y; - y = z; - z = w; - return w = w ^ (w >> 19) ^ t ^ (t >> 8); + uint t = X ^ (X << 11); + X = Y; + Y = Z; + Z = W; + return W = W ^ (W >> 19) ^ t ^ (t >> 8); } /// From 3c9e52018834ab2409758e57fae5d0fa3a71f548 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 17:24:47 +0900 Subject: [PATCH 0629/1276] Update bundle logic to match stable --- .../Drawables/BundledBeatmapDownloader.cs | 84 +++++++++++++------ 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 029bb37c18..5b81022cbf 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -1,18 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; -using osu.Framework.Utils; using osu.Game.Database; using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; +using osu.Game.Utils; namespace osu.Game.Beatmaps.Drawables { @@ -29,9 +30,16 @@ namespace osu.Game.Beatmaps.Drawables public BundledBeatmapDownloader(bool onlyTutorial) { if (onlyTutorial) - downloadableFilenames.Add(tutorial_filename); + { + queueDownloads(new[] { tutorial_filename }); + } else - downloadableFilenames.AddRange(bundled_beatmap_filenames); + { + queueDownloads(bundled_osu, 8); + queueDownloads(bundled_taiko, 3); + queueDownloads(bundled_catch, 3); + queueDownloads(bundled_mania, 3); + } } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -49,7 +57,7 @@ namespace osu.Game.Beatmaps.Drawables [BackgroundDependencyLoader] private void load() { - foreach (string filename in downloadableFilenames.OrderBy(_ => RNG.NextSingle()).Take(10)) + foreach (string filename in downloadableFilenames) { var match = Regex.Match(filename, @"([0-9]*) (.*) - (.*)\.osz"); @@ -68,9 +76,42 @@ namespace osu.Game.Beatmaps.Drawables } } + private void queueDownloads(string[] sourceFilenames, int? limit = null) + { + try + { + // Matches osu-stable, in order to provide new users with roughly the same randomised selection of bundled beatmaps. + var random = new LegacyRandom(DateTime.UtcNow.Year * 1000 + (DateTime.UtcNow.DayOfYear / 7)); + + downloadableFilenames.AddRange(sourceFilenames.OrderBy(x => random.NextDouble()).Take(limit ?? int.MaxValue)); + } + catch { } + } + + private class BundledBeatmapModelDownloader : BeatmapModelDownloader + { + public BundledBeatmapModelDownloader(IModelImporter beatmapImporter, IAPIProvider api) + : base(beatmapImporter, api) + { + } + + protected override ArchiveDownloadRequest CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize) + => new BundledBeatmapDownloadRequest(set, minimiseDownloadSize); + + public class BundledBeatmapDownloadRequest : DownloadBeatmapSetRequest + { + protected override string Uri => $"https://assets.ppy.sh/client-resources/bundled/{Model.OnlineID}.osz"; + + public BundledBeatmapDownloadRequest(IBeatmapSetInfo beatmapSetInfo, bool minimiseDownloadSize) + : base(beatmapSetInfo, minimiseDownloadSize) + { + } + } + } + private const string tutorial_filename = "1011011 nekodex - new beginnings.osz"; - private static readonly string[] bundled_beatmap_filenames = + private static readonly string[] bundled_osu = { "682286 Yuyoyuppe - Emerald Galaxy.osz", "682287 baker - For a Dead Girl+.osz", @@ -180,6 +221,10 @@ namespace osu.Game.Beatmaps.Drawables "1019827 UNDEAD CORPORATION - Sad Dream.osz", "1020213 Creo - Idolize.osz", "1021450 Thaehan - Chiptune & Baroque.osz", + }; + + private static readonly string[] bundled_taiko = + { "707824 Fractal Dreamers - Fortuna Redux.osz", "789553 Cranky - Ran.osz", "827822 Function Phantom - Neuronecia.osz", @@ -227,6 +272,10 @@ namespace osu.Game.Beatmaps.Drawables "1023681 Inferi - The Ruin of Mankind.osz", "1034358 ALEPH - The Evil Spirit.osz", "1037567 ALEPH - Scintillations.osz", + }; + + private static readonly string[] bundled_catch = + { "554256 Helblinde - When Time Sleeps.osz", "693123 yuki. - Nadeshiko Sensation.osz", "767009 OISHII - PIZZA PLAZA.osz", @@ -251,6 +300,10 @@ namespace osu.Game.Beatmaps.Drawables "972887 HyuN - Illusion of Inflict.osz", "1008600 LukHash - WHEN AN ANGEL DIES.osz", "1032103 LukHash - H8 U.osz", + }; + + private static readonly string[] bundled_mania = + { "943516 antiPLUR - Clockwork Spooks.osz", "946394 VINXIS - Three Times The Original Charm.osz", "966408 antiPLUR - One Life Left to Live.osz", @@ -262,26 +315,5 @@ namespace osu.Game.Beatmaps.Drawables "1009907 James Landino & Kabuki - Birdsong.osz", "1015169 Thaehan - Insert Coin.osz", }; - - private class BundledBeatmapModelDownloader : BeatmapModelDownloader - { - public BundledBeatmapModelDownloader(IModelImporter beatmapImporter, IAPIProvider api) - : base(beatmapImporter, api) - { - } - - protected override ArchiveDownloadRequest CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize) - => new BundledBeatmapDownloadRequest(set, minimiseDownloadSize); - - public class BundledBeatmapDownloadRequest : DownloadBeatmapSetRequest - { - protected override string Uri => $"https://assets.ppy.sh/client-resources/bundled/{Model.OnlineID}.osz"; - - public BundledBeatmapDownloadRequest(IBeatmapSetInfo beatmapSetInfo, bool minimiseDownloadSize) - : base(beatmapSetInfo, minimiseDownloadSize) - { - } - } - } } } From 9f6597695ce4555cbaa12df3d4705534d05f928e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 18:17:29 +0900 Subject: [PATCH 0630/1276] Only mark stable import complete if it actually completes --- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 893c940a1d..abbef52f8f 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -99,10 +99,10 @@ namespace osu.Game.Overlays.FirstRunSetup importBeatmapsButton.Enabled.Value = false; legacyImportManager?.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => { - // TODO: can we know if the import was successful? - // if so we should turn the button green and disable it in that case alone. - // importBeatmapsButton.Enabled.Value = true; - importBeatmapsButton.Complete(); + if (t.IsCompletedSuccessfully) + importBeatmapsButton.Complete(); + else + importBeatmapsButton.Enabled.Value = true; })); } }, From 4e0d70cbf49322e74362c88bba9f4bd5a0eb6e06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 18:26:29 +0900 Subject: [PATCH 0631/1276] Internalise bindable and progress handling to ensure transforms don't go backwards --- .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index abbef52f8f..e02ba4d3ed 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -5,7 +5,6 @@ using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; @@ -129,7 +128,7 @@ namespace osu.Game.Overlays.FirstRunSetup downloadTracker.Progress.BindValueChanged(progress => { - downloadTutorialButton.Current.Value = progress.NewValue; + downloadTutorialButton.SetProgress(progress.NewValue, false); if (progress.NewValue == 1) downloadTutorialButton.Complete(); @@ -152,10 +151,10 @@ namespace osu.Game.Overlays.FirstRunSetup { double progress = (double)bundledDownloader.DownloadTrackers.Count(t => t.State.Value == DownloadState.LocallyAvailable) / bundledDownloader.DownloadTrackers.Count(); - this.TransformBindableTo(downloadBundledButton.Current, progress, 2000, Easing.OutQuint); - if (progress == 1) downloadBundledButton.Complete(); + else + downloadBundledButton.SetProgress(progress, true); } } @@ -166,8 +165,6 @@ namespace osu.Game.Overlays.FirstRunSetup private ProgressBar progressBar = null!; - public Bindable Current => progressBar.Current; - protected override void LoadComplete() { base.LoadComplete(); @@ -189,7 +186,15 @@ namespace osu.Game.Overlays.FirstRunSetup Background.FadeColour(colours.Green, 500, Easing.OutQuint); progressBar.FillColour = colours.Green; - this.TransformBindableTo(Current, 1, 500, Easing.OutQuint); + this.TransformBindableTo(progressBar.Current, 1, 500, Easing.OutQuint); + } + + public void SetProgress(double progress, bool animated) + { + if (!Enabled.Value) + return; + + this.TransformBindableTo(progressBar.Current, progress, animated ? 500 : 0, Easing.OutQuint); } } } From 88bfd5dece938e0e886dce8f433191cd7fe4b9ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 18:57:32 +0900 Subject: [PATCH 0632/1276] Show current beatmap count on beatmap screen --- .../FirstRunSetup/FirstRunSetupScreen.cs | 7 ++- .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 63 +++++++++++++++++-- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index 282ba52ddc..d4032306ad 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -19,8 +19,11 @@ namespace osu.Game.Overlays.FirstRunSetup protected FillFlowContainer Content { get; private set; } + [Resolved] + protected OverlayColourProvider OverlayColourProvider { get; private set; } + [BackgroundDependencyLoader] - private void load(OverlayColourProvider overlayColourProvider) + private void load() { const float header_size = 40; const float spacing = 20; @@ -36,7 +39,7 @@ namespace osu.Game.Overlays.FirstRunSetup { Text = this.GetLocalisableDescription(), Font = OsuFont.Default.With(size: header_size), - Colour = overlayColourProvider.Light1, + Colour = OverlayColourProvider.Light1, }, Content = new FillFlowContainer { diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index e02ba4d3ed..fda9fde8b7 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -2,10 +2,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; using osu.Game.Graphics; @@ -15,6 +17,8 @@ using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; using osu.Game.Online; using osuTK; +using Realms; +using Container = osu.Framework.Graphics.Containers.Container; namespace osu.Game.Overlays.FirstRunSetup { @@ -25,14 +29,21 @@ namespace osu.Game.Overlays.FirstRunSetup private ProgressRoundedButton importBeatmapsButton = null!; private ProgressRoundedButton downloadTutorialButton = null!; + private OsuTextFlowContainer currentlyLoadedBeatmaps = null!; + private BundledBeatmapDownloader? tutorialDownloader; private BundledBeatmapDownloader? bundledDownloader; [Resolved] private OsuColour colours { get; set; } = null!; + [Resolved] + private RealmAccess realmAccess { get; set; } = null!; + + private IDisposable? beatmapSubscription; + [BackgroundDependencyLoader(permitNulls: true)] - private void load(OverlayColourProvider overlayColourProvider, LegacyImportManager? legacyImportManager) + private void load(LegacyImportManager? legacyImportManager) { Vector2 buttonSize = new Vector2(500, 60); @@ -40,15 +51,31 @@ namespace osu.Game.Overlays.FirstRunSetup { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Colour = overlayColourProvider.Content1, + Colour = OverlayColourProvider.Content1, Text = "\"Beatmaps\" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, + new Container + { + RelativeSizeAxes = Axes.X, + Height = 30, + Children = new Drawable[] + { + currentlyLoadedBeatmaps = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24, weight: FontWeight.SemiBold)) + { + Colour = OverlayColourProvider.Content2, + TextAnchor = Anchor.Centre, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + }, + } + }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Colour = overlayColourProvider.Content1, + Colour = OverlayColourProvider.Content1, Text = "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay.", RelativeSizeAxes = Axes.X, @@ -65,7 +92,7 @@ namespace osu.Game.Overlays.FirstRunSetup }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Colour = overlayColourProvider.Content1, + Colour = OverlayColourProvider.Content1, Text = "To get you started, we have some recommended beatmaps.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y @@ -81,7 +108,7 @@ namespace osu.Game.Overlays.FirstRunSetup }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Colour = overlayColourProvider.Content1, + Colour = OverlayColourProvider.Content1, Text = "If you have an existing osu! install, you can also choose to import your existing beatmap collection.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y @@ -107,7 +134,7 @@ namespace osu.Game.Overlays.FirstRunSetup }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Colour = overlayColourProvider.Content1, + Colour = OverlayColourProvider.Content1, Text = "You can also obtain more beatmaps from the main menu \"browse\" button at any time.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y @@ -115,6 +142,30 @@ namespace osu.Game.Overlays.FirstRunSetup }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + beatmapSubscription = realmAccess.RegisterForNotifications(r => r.All().Where(s => !s.DeletePending && !s.Protected), beatmapsChanged); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + beatmapSubscription?.Dispose(); + } + + private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes, Exception error) + { + currentlyLoadedBeatmaps.Text = $"You currently have {sender.Count} beatmap(s) loaded!"; + + if (changes != null && (changes.DeletedIndices.Any() || changes.InsertedIndices.Any())) + { + currentlyLoadedBeatmaps.FadeColour(colours.YellowLight).FadeColour(OverlayColourProvider.Content2); + currentlyLoadedBeatmaps.ScaleTo(1.1f).ScaleTo(1, 1000, Easing.OutQuint); + } + } + private void downloadTutorial() { if (tutorialDownloader != null) From 804848c9fb546aea2634da3e9b45c9eee2d6ad10 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 18:57:41 +0900 Subject: [PATCH 0633/1276] Allow bundled downloader to not post notifications --- .../Beatmaps/Drawables/BundledBeatmapDownloader.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 5b81022cbf..bc99aa4a5a 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -19,6 +19,8 @@ namespace osu.Game.Beatmaps.Drawables { public class BundledBeatmapDownloader : CompositeDrawable { + private readonly bool shouldPostNotifications; + public IEnumerable DownloadTrackers => downloadTrackers; private readonly List downloadTrackers = new List(); @@ -27,8 +29,15 @@ namespace osu.Game.Beatmaps.Drawables private BundledBeatmapModelDownloader beatmapDownloader; - public BundledBeatmapDownloader(bool onlyTutorial) + /// + /// Construct a new beatmap downloader. + /// + /// Whether only the tutorial should be downloaded, instead of bundled beatmaps. + /// Whether downloads should create tracking notifications. + public BundledBeatmapDownloader(bool onlyTutorial, bool shouldPostNotifications = false) { + this.shouldPostNotifications = shouldPostNotifications; + if (onlyTutorial) { queueDownloads(new[] { tutorial_filename }); @@ -48,7 +57,7 @@ namespace osu.Game.Beatmaps.Drawables localDependencies.CacheAs(beatmapDownloader = new BundledBeatmapModelDownloader(parent.Get(), parent.Get())); - if (parent.Get() is INotificationOverlay notifications) + if (shouldPostNotifications && parent.Get() is INotificationOverlay notifications) beatmapDownloader.PostNotification = notifications.Post; return localDependencies; From b6896376e7db72813bbd8c42a6d9ce9bd26c3a45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 19:08:16 +0900 Subject: [PATCH 0634/1276] Better signal when no beatmaps are loaded --- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index fda9fde8b7..645713c52a 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -159,10 +159,17 @@ namespace osu.Game.Overlays.FirstRunSetup { currentlyLoadedBeatmaps.Text = $"You currently have {sender.Count} beatmap(s) loaded!"; - if (changes != null && (changes.DeletedIndices.Any() || changes.InsertedIndices.Any())) + if (sender.Count == 0) { - currentlyLoadedBeatmaps.FadeColour(colours.YellowLight).FadeColour(OverlayColourProvider.Content2); - currentlyLoadedBeatmaps.ScaleTo(1.1f).ScaleTo(1, 1000, Easing.OutQuint); + currentlyLoadedBeatmaps.FadeColour(colours.Red1, 500, Easing.OutQuint); + } + else if (changes != null && (changes.DeletedIndices.Any() || changes.InsertedIndices.Any())) + { + currentlyLoadedBeatmaps.FadeColour(colours.Yellow) + .FadeColour(OverlayColourProvider.Content2, 1500, Easing.OutQuint); + + currentlyLoadedBeatmaps.ScaleTo(1.1f) + .ScaleTo(1, 1500, Easing.OutQuint); } } From 14316855f9a478734cc4627463e70332c3b8fdbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 19:26:28 +0900 Subject: [PATCH 0635/1276] Add localisation length hinting --- .editorconfig | 2 ++ osu.Game/.editorconfig | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index be5652954b..49e83c9103 100644 --- a/.editorconfig +++ b/.editorconfig @@ -175,6 +175,8 @@ csharp_style_prefer_switch_expression = false:none #Supressing roslyn built-in analyzers # Suppress: EC112 +dotnet_diagnostic.OLOC001.words_in_name = 5 + #Private method is unused dotnet_diagnostic.IDE0051.severity = silent #Private member is unused diff --git a/osu.Game/.editorconfig b/osu.Game/.editorconfig index 4107d1bb35..539cd56dab 100644 --- a/osu.Game/.editorconfig +++ b/osu.Game/.editorconfig @@ -1,3 +1,4 @@ [*.cs] +dotnet_diagnostic.OLOC001.words_in_name = 5 dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation -dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. \ No newline at end of file +dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. From 694f33b827d327b2b8fb8afd7e39761c8a12a25b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 19:33:16 +0900 Subject: [PATCH 0636/1276] Add localisation of beatmaps screen --- .../FirstRunSetupBeatmapScreenStrings.cs | 54 +++++++++++++++++++ .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 22 ++++---- 2 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs diff --git a/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs b/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs new file mode 100644 index 0000000000..3a7fe4bb12 --- /dev/null +++ b/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs @@ -0,0 +1,54 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class FirstRunSetupBeatmapScreenStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.FirstRunSetupBeatmapScreen"; + + /// + /// "Obtaining Beatmaps" + /// + public static LocalisableString Header => new TranslatableString(getKey(@"header"), @"Obtaining Beatmaps"); + + /// + /// ""Beatmaps" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection." + /// + public static LocalisableString Description => new TranslatableString(getKey(@"description"), @"""Beatmaps"" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection."); + + /// + /// "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay." + /// + public static LocalisableString TutorialDescription => new TranslatableString(getKey(@"tutorial_description"), @"If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay."); + + /// + /// "Get the osu! tutorial" + /// + public static LocalisableString TutorialButton => new TranslatableString(getKey(@"tutorial_button"), @"Get the osu! tutorial"); + + /// + /// "To get you started, we have some recommended beatmaps." + /// + public static LocalisableString BundledDescription => new TranslatableString(getKey(@"bundled_description"), @"To get you started, we have some recommended beatmaps."); + + /// + /// "Get recommended beatmaps" + /// + public static LocalisableString BundledButton => new TranslatableString(getKey(@"bundled_button"), @"Get recommended beatmaps"); + + /// + /// "You can also obtain more beatmaps from the main menu "browse" button at any time." + /// + public static LocalisableString ObtainMoreBeatmaps => new TranslatableString(getKey(@"obtain_more_beatmaps"), @"You can also obtain more beatmaps from the main menu ""browse"" button at any time."); + + /// + /// "You currently have {0} beatmap(s) loaded!" + /// + public static LocalisableString CurrentlyLoadedBeatmaps(int beatmaps) => new TranslatableString(getKey(@"currently_loaded_beatmaps"), @"You currently have {0} beatmap(s) loaded!", beatmaps); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 645713c52a..892856d280 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -3,10 +3,11 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; @@ -18,11 +19,10 @@ using osu.Game.Localisation; using osu.Game.Online; using osuTK; using Realms; -using Container = osu.Framework.Graphics.Containers.Container; namespace osu.Game.Overlays.FirstRunSetup { - [Description("Obtaining Beatmaps")] + [LocalisableDescription(typeof(FirstRunSetupBeatmapScreenStrings), nameof(FirstRunSetupBeatmapScreenStrings.Header))] public class ScreenBeatmaps : FirstRunSetupScreen { private ProgressRoundedButton downloadBundledButton = null!; @@ -52,8 +52,7 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { Colour = OverlayColourProvider.Content1, - Text = - "\"Beatmaps\" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.", + Text = FirstRunSetupBeatmapScreenStrings.Description, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -76,8 +75,7 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { Colour = OverlayColourProvider.Content1, - Text = - "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay.", + Text = FirstRunSetupBeatmapScreenStrings.TutorialDescription, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -87,13 +85,13 @@ namespace osu.Game.Overlays.FirstRunSetup Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, BackgroundColour = colours.Pink3, - Text = "Get the osu! tutorial", + Text = FirstRunSetupBeatmapScreenStrings.TutorialButton, Action = downloadTutorial }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { Colour = OverlayColourProvider.Content1, - Text = "To get you started, we have some recommended beatmaps.", + Text = FirstRunSetupBeatmapScreenStrings.BundledDescription, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -103,7 +101,7 @@ namespace osu.Game.Overlays.FirstRunSetup Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, BackgroundColour = colours.Blue3, - Text = "Get recommended beatmaps", + Text = FirstRunSetupBeatmapScreenStrings.BundledButton, Action = downloadBundled }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) @@ -135,7 +133,7 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { Colour = OverlayColourProvider.Content1, - Text = "You can also obtain more beatmaps from the main menu \"browse\" button at any time.", + Text = FirstRunSetupBeatmapScreenStrings.ObtainMoreBeatmaps, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -157,7 +155,7 @@ namespace osu.Game.Overlays.FirstRunSetup private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes, Exception error) { - currentlyLoadedBeatmaps.Text = $"You currently have {sender.Count} beatmap(s) loaded!"; + currentlyLoadedBeatmaps.Text = FirstRunSetupBeatmapScreenStrings.CurrentlyLoadedBeatmaps(sender.Count); if (sender.Count == 0) { From fe0fcc7e9ea522ebfa9bca7a71603de316a80371 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Apr 2022 20:00:38 +0900 Subject: [PATCH 0637/1276] Rename countdown object --- .../Multiplayer/ForceGameplayStartCountdown.cs | 17 +++++++++++++++++ .../Multiplayer/GameplayStartCountdown.cs | 15 --------------- .../Online/Multiplayer/MultiplayerCountdown.cs | 2 +- osu.Game/Online/SignalRWorkaroundTypes.cs | 2 +- 4 files changed, 19 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs delete mode 100644 osu.Game/Online/Multiplayer/GameplayStartCountdown.cs diff --git a/osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs b/osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs new file mode 100644 index 0000000000..4ec5019a07 --- /dev/null +++ b/osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using MessagePack; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// A started by the server when clients being to load. + /// Indicates how long until gameplay will forcefully start, excluding any users which have not completed loading, + /// and forcing progression of any clients that are blocking load due to user interaction. + /// + [MessagePackObject] + public class ForceGameplayStartCountdown : MultiplayerCountdown + { + } +} diff --git a/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs b/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs deleted file mode 100644 index d3d7f78507..0000000000 --- a/osu.Game/Online/Multiplayer/GameplayStartCountdown.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using MessagePack; - -namespace osu.Game.Online.Multiplayer -{ - /// - /// A indicating that gameplay will start after a given period of time. - /// - [MessagePackObject] - public class GameplayStartCountdown : MultiplayerCountdown - { - } -} diff --git a/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs b/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs index 308eea3aef..dbf2ab667b 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.Multiplayer /// [MessagePackObject] [Union(0, typeof(MatchStartCountdown))] // IMPORTANT: Add rules to SignalRUnionWorkaroundResolver for new derived types. - [Union(1, typeof(GameplayStartCountdown))] + [Union(1, typeof(ForceGameplayStartCountdown))] public abstract class MultiplayerCountdown { /// diff --git a/osu.Game/Online/SignalRWorkaroundTypes.cs b/osu.Game/Online/SignalRWorkaroundTypes.cs index 00fdadb87e..d1f0ba725f 100644 --- a/osu.Game/Online/SignalRWorkaroundTypes.cs +++ b/osu.Game/Online/SignalRWorkaroundTypes.cs @@ -25,7 +25,7 @@ namespace osu.Game.Online (typeof(TeamVersusRoomState), typeof(MatchRoomState)), (typeof(TeamVersusUserState), typeof(MatchUserState)), (typeof(MatchStartCountdown), typeof(MultiplayerCountdown)), - (typeof(GameplayStartCountdown), typeof(MultiplayerCountdown)) + (typeof(ForceGameplayStartCountdown), typeof(MultiplayerCountdown)) }; } } From f300b62877cad3c2c25b52fae29c7a446a887dda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 20:03:54 +0900 Subject: [PATCH 0638/1276] Add `ScrollIntoView` method which accepts an offset to allow usage in mod select --- .../Graphics/Containers/OsuScrollContainer.cs | 21 +++++++++++++++++++ osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index 017ea6ec32..817b8409e6 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -3,6 +3,7 @@ #nullable enable +using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -57,6 +58,26 @@ namespace osu.Game.Graphics.Containers { } + /// + /// Scrolls a into view. + /// + /// The to scroll into view. + /// Whether to animate the movement. + /// An added amount to scroll beyond the requirement to bring the target into view. + public void ScrollIntoView(Drawable d, bool animated = true, float extraScroll = 0) + { + float childPos0 = GetChildPosInContent(d); + float childPos1 = GetChildPosInContent(d, d.DrawSize); + + float minPos = Math.Min(childPos0, childPos1); + float maxPos = Math.Max(childPos0, childPos1); + + if (minPos < Current || (minPos > Current && d.DrawSize[ScrollDim] > DisplayableContent)) + ScrollTo(minPos - extraScroll, animated); + else if (maxPos > Current + DisplayableContent) + ScrollTo(maxPos - DisplayableContent + extraScroll, animated); + } + protected override bool OnMouseDown(MouseDownEvent e) { if (shouldPerformRightMouseScroll(e)) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 03a66575c4..ffd6e9a52c 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -161,7 +161,7 @@ namespace osu.Game.Overlays.Mods { AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, - RequestScroll = column => columnScroll.ScrollTo(Math.Clamp(column.DrawPosition.X - 70, 0, columnScroll.ScrollableExtent)) + RequestScroll = column => columnScroll.ScrollIntoView(column, extraScroll: 140) }; protected override void LoadComplete() From 1d8ac6917dc4a3fe58f67b254a32143b2c9bd632 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Apr 2022 20:10:47 +0900 Subject: [PATCH 0639/1276] Send Loaded state from PlayerLoader on update thread --- .../Multiplayer/MultiplayerPlayer.cs | 3 --- .../Multiplayer/MultiplayerPlayerLoader.cs | 22 +++++++++++++++++++ osu.Game/Screens/Play/PlayerLoader.cs | 10 ++++++++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 7dd57ee50f..02ff040a94 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -133,9 +133,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer failAndBail(); } }), true); - - client.ChangeState(MultiplayerUserState.Loaded) - .ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); } protected override void LoadComplete() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs index 236df7917c..7f01bd64ab 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Screens.Play; @@ -28,6 +30,26 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // The server is forcefully starting gameplay. || multiplayerClient.LocalUser?.State == MultiplayerUserState.Playing; + protected override void OnPlayerLoaded() + { + base.OnPlayerLoaded(); + + multiplayerClient.ChangeState(MultiplayerUserState.Loaded) + .ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); + } + + private void failAndBail(string message = null) + { + if (!string.IsNullOrEmpty(message)) + Logger.Log(message, LoggingTarget.Runtime, LogLevel.Important); + + Schedule(() => + { + if (this.IsCurrentScreen()) + this.Exit(); + }); + } + public override void OnSuspending(ScreenTransitionEvent e) { base.OnSuspending(e); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index bdfa822cb0..d75466764d 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -368,7 +368,15 @@ namespace osu.Game.Screens.Play CurrentPlayer.RestartCount = restartCount++; CurrentPlayer.RestartRequested = restartRequested; - LoadTask = LoadComponentAsync(CurrentPlayer, _ => MetadataInfo.Loading = false); + LoadTask = LoadComponentAsync(CurrentPlayer, _ => + { + MetadataInfo.Loading = false; + OnPlayerLoaded(); + }); + } + + protected virtual void OnPlayerLoaded() + { } private void restartRequested() From a50dd2ae93becc4c980c88e8b881a1a914e8b914 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 20:15:40 +0900 Subject: [PATCH 0640/1276] Fix rider failures in nullable --- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 892856d280..190a0badab 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -1,7 +1,8 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Linq; using osu.Framework.Allocation; From b902e69634ea7f56e4af3ceb29037186695b39b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 20:16:32 +0900 Subject: [PATCH 0641/1276] Update test expectations based on new screen order --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 31c4d66784..836cf6caad 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("step to next", () => overlay.NextButton.TriggerClick()); - AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenUIScale); + AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenBeatmaps); AddStep("hide", () => overlay.Hide()); AddAssert("overlay hidden", () => overlay.State.Value == Visibility.Hidden); @@ -188,7 +188,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("run notification action", () => lastNotification.Activated()); AddAssert("overlay shown", () => overlay.State.Value == Visibility.Visible); - AddAssert("is resumed", () => overlay.CurrentScreen is ScreenUIScale); + AddAssert("is resumed", () => overlay.CurrentScreen is ScreenBeatmaps); } } } From 32ad2166698528574947aca2c17fe28eb35d2757 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 22:58:32 +0900 Subject: [PATCH 0642/1276] Allow dragging the osu! logo Logic borrowed from `NowPlayingOverlay`. --- osu.Game/Screens/Menu/OsuLogo.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 1d3aef0653..1217a28fe7 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -109,7 +109,7 @@ namespace osu.Game.Screens.Menu AutoSizeAxes = Axes.Both, Children = new Drawable[] { - logoBounceContainer = new Container + logoBounceContainer = new DragContainer { AutoSizeAxes = Axes.Both, Children = new Drawable[] @@ -402,5 +402,26 @@ namespace osu.Game.Screens.Menu impactContainer.ScaleTo(0.96f); impactContainer.ScaleTo(1.12f, 250); } + + private class DragContainer : Container + { + protected override bool OnDragStart(DragStartEvent e) => true; + + protected override void OnDrag(DragEvent e) + { + Vector2 change = e.MousePosition - e.MouseDownPosition; + + // Diminish the drag distance as we go further to simulate "rubber band" feeling. + change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.6f) / change.Length; + + this.MoveTo(change); + } + + protected override void OnDragEnd(DragEndEvent e) + { + this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); + base.OnDragEnd(e); + } + } } } From d5702e29558f7845e27e4a60bbf2c88231535d3f Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Thu, 28 Apr 2022 18:29:37 +0200 Subject: [PATCH 0643/1276] Append incompatible mods to `base.IncompatibleMods` --- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index 5d262cd95e..b27b1a7267 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModClassic : ModClassic, IApplicableToHitObject, IApplicableToDrawableHitObject, IApplicableToDrawableRuleset { - public override Type[] IncompatibleMods => new[] { typeof(OsuModStrictTracking), typeof(OsuModTarget) }; + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModStrictTracking), typeof(OsuModTarget) }).ToArray(); [SettingSource("No slider head accuracy requirement", "Scores sliders proportionally to the number of ticks hit.")] public Bindable NoSliderHeadAccuracy { get; } = new BindableBool(true); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 7a7e189c29..23500f5da6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Description => "It never gets boring!"; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget) }; + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTarget)).ToArray(); private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 4d6c09f919..bb21550c56 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(IRequiresApproachCircles), typeof(OsuModClassic), @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods typeof(OsuModSpunOut), typeof(OsuModStrictTracking), typeof(OsuModSuddenDeath) - }; + }).ToArray(); [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] public Bindable Seed { get; } = new Bindable From fa5c05120c913dd9c5931d7e92a84d8ba5b4b4c0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 20:04:52 +0300 Subject: [PATCH 0644/1276] Allow rewinding random in song select with Shift + Left Click --- osu.Game/Screens/Select/FooterButtonRandom.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 1d4722cf5d..5cdf381879 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -59,6 +59,18 @@ namespace osu.Game.Screens.Select }; } + protected override bool OnClick(ClickEvent e) + { + rewindSearch = e.ShiftPressed; + return base.OnClick(e); + } + + protected override void OnMouseUp(MouseUpEvent e) + { + rewindSearch = false; + base.OnMouseUp(e); + } + public override bool OnPressed(KeyBindingPressEvent e) { rewindSearch = e.Action == GlobalAction.SelectPreviousRandom; From e31fdc28ddbadae1e8bd2150d6ea695bd4b133e6 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Thu, 28 Apr 2022 22:31:48 +0200 Subject: [PATCH 0645/1276] Make Classic and Target compatible again --- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index b27b1a7267..76ff361ce3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModClassic : ModClassic, IApplicableToHitObject, IApplicableToDrawableHitObject, IApplicableToDrawableRuleset { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModStrictTracking), typeof(OsuModTarget) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModStrictTracking)).ToArray(); [SettingSource("No slider head accuracy requirement", "Scores sliders proportionally to the number of ticks hit.")] public Bindable NoSliderHeadAccuracy { get; } = new BindableBool(true); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index bb21550c56..5b121f4673 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -45,7 +45,6 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(IRequiresApproachCircles), - typeof(OsuModClassic), typeof(OsuModRandom), typeof(OsuModSpunOut), typeof(OsuModStrictTracking), From 294340279eb5614a8a6d1c23d305b6ba9aa5a174 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 04:14:09 +0300 Subject: [PATCH 0646/1276] Add inputting test coverage --- .../SongSelect/TestSceneSongSelectFooter.cs | 90 ++++++++++++++----- 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs index 0ac65b357c..6485123b29 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs @@ -1,35 +1,85 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Select; +using osuTK.Input; namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneSongSelectFooter : OsuManualInputManagerTestScene { - public TestSceneSongSelectFooter() - { - AddStep("Create footer", () => - { - Footer footer; - AddRange(new Drawable[] - { - footer = new Footer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - }); + private FooterButtonRandom randomButton; - footer.AddButton(new FooterButtonMods(), null); - footer.AddButton(new FooterButtonRandom - { - NextRandom = () => { }, - PreviousRandom = () => { }, - }, null); - footer.AddButton(new FooterButtonOptions(), null); + private bool nextRandomCalled; + private bool previousRandomCalled; + + [SetUp] + public void SetUp() => Schedule(() => + { + nextRandomCalled = false; + previousRandomCalled = false; + + Footer footer; + + Child = footer = new Footer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + footer.AddButton(new FooterButtonMods(), null); + footer.AddButton(randomButton = new FooterButtonRandom + { + NextRandom = () => nextRandomCalled = true, + PreviousRandom = () => previousRandomCalled = true, + }, null); + footer.AddButton(new FooterButtonOptions(), null); + }); + + [Test] + public void TestFooterRandom() + { + AddStep("press F2", () => InputManager.Key(Key.F2)); + AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled); + } + + [Test] + public void TestFooterRandomViaMouse() + { + AddStep("click button", () => + { + InputManager.MoveMouseTo(randomButton); + InputManager.Click(MouseButton.Left); }); + AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled); + } + + [Test] + public void TestFooterRewind() + { + AddStep("press Shift+F2", () => + { + InputManager.PressKey(Key.LShift); + InputManager.PressKey(Key.F2); + InputManager.ReleaseKey(Key.F2); + InputManager.ReleaseKey(Key.LShift); + }); + AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled); + } + + [Test] + public void TestFooterRewindViaMouse() + { + AddStep("shift + click button", () => + { + InputManager.PressKey(Key.LShift); + InputManager.MoveMouseTo(randomButton); + InputManager.Click(MouseButton.Left); + InputManager.ReleaseKey(Key.LShift); + }); + AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled); } } } From 7442ae283cee27f591c76c80de62c915d190829d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 05:24:00 +0300 Subject: [PATCH 0647/1276] Tidy up test cases a touch --- .../Mods/TestSceneOsuModAlternate.cs | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs index de1f61a0bd..67654c11f2 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs @@ -16,31 +16,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { public class TestSceneOsuModAlternate : OsuModTestScene { - [Test] - public void TestInputAtIntro() => CreateModTest(new ModTestData - { - Mod = new OsuModAlternate(), - PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, - Autoplay = false, - Beatmap = new Beatmap - { - HitObjects = new List - { - new HitCircle - { - StartTime = 1000, - Position = new Vector2(100), - }, - }, - }, - ReplayFrames = new List - { - new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton), - new OsuReplayFrame(501, new Vector2(200)), - new OsuReplayFrame(1000, new Vector2(100), OsuAction.LeftButton), - } - }); - [Test] public void TestInputAlternating() => CreateModTest(new ModTestData { @@ -116,6 +91,39 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods } }); + /// + /// Ensures alternation is reset before the first hitobject after intro. + /// + [Test] + public void TestInputSingularAtIntro() => CreateModTest(new ModTestData + { + Mod = new OsuModAlternate(), + PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 1000, + Position = new Vector2(100), + }, + }, + }, + ReplayFrames = new List + { + // first press during intro. + new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(200)), + // press same key at hitobject and ensure it has been hit. + new OsuReplayFrame(1000, new Vector2(100), OsuAction.LeftButton), + } + }); + + /// + /// Ensures alternation is reset after a break. + /// [Test] public void TestInputSingularWithBreak() => CreateModTest(new ModTestData { @@ -144,8 +152,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }, ReplayFrames = new List { + // first press to start alternate lock. new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), new OsuReplayFrame(501, new Vector2(100)), + // press same key at second hitobject and ensure it has been hit. new OsuReplayFrame(2500, new Vector2(100), OsuAction.LeftButton), new OsuReplayFrame(2501, new Vector2(100)), } From 845e7dba95d28d2d37bf8502db04ee7910cb0e63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Apr 2022 11:27:14 +0900 Subject: [PATCH 0648/1276] Reduce padding slightly to restore usability of UI scale screen --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index c3dcdef318..4277f0f2ba 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -84,7 +84,7 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 70 * 2 }, + Padding = new MarginPadding { Horizontal = 70 * 1.2f }, Child = new InputBlockingContainer { Masking = true, From 723d70be7096b300e86568b937b2f2998f4ded56 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 05:26:19 +0300 Subject: [PATCH 0649/1276] Change `TestInputSingularWithBreak` to reproduce case of pressing before second object --- .../Mods/TestSceneOsuModAlternate.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs index 67654c11f2..5e46498aca 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs @@ -122,19 +122,19 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }); /// - /// Ensures alternation is reset after a break. + /// Ensures alternation is reset before the first hitobject after a break. /// [Test] public void TestInputSingularWithBreak() => CreateModTest(new ModTestData { Mod = new OsuModAlternate(), - PassCondition = () => Player.ScoreProcessor.Combo.Value == 2, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2, Autoplay = false, Beatmap = new Beatmap { Breaks = new List { - new BreakPeriod(500, 2250), + new BreakPeriod(500, 2000), }, HitObjects = new List { @@ -146,8 +146,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods new HitCircle { StartTime = 2500, - Position = new Vector2(100), - } + Position = new Vector2(500, 100), + }, + new HitCircle + { + StartTime = 3000, + Position = new Vector2(500, 100), + }, } }, ReplayFrames = new List @@ -155,9 +160,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods // first press to start alternate lock. new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), new OsuReplayFrame(501, new Vector2(100)), + // press same key after break but before hit object. + new OsuReplayFrame(2250, new Vector2(300, 100), OsuAction.LeftButton), + new OsuReplayFrame(2251, new Vector2(300, 100)), // press same key at second hitobject and ensure it has been hit. - new OsuReplayFrame(2500, new Vector2(100), OsuAction.LeftButton), - new OsuReplayFrame(2501, new Vector2(100)), + new OsuReplayFrame(2500, new Vector2(500, 100), OsuAction.LeftButton), + new OsuReplayFrame(2501, new Vector2(500, 100)), + // press same key at third hitobject and ensure it has been missed. + new OsuReplayFrame(3000, new Vector2(500, 100), OsuAction.LeftButton), + new OsuReplayFrame(3001, new Vector2(500, 100)), } }); } From 92d4463e55acf51e8707d3eddbba642ec6a22541 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Apr 2022 11:45:18 +0900 Subject: [PATCH 0650/1276] Add "always bundled" beatmaps --- .../Beatmaps/Drawables/BundledBeatmapDownloader.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index bc99aa4a5a..df0a69cb25 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -44,6 +44,8 @@ namespace osu.Game.Beatmaps.Drawables } else { + queueDownloads(always_bundled_beatmaps); + queueDownloads(bundled_osu, 8); queueDownloads(bundled_taiko, 3); queueDownloads(bundled_catch, 3); @@ -120,6 +122,15 @@ namespace osu.Game.Beatmaps.Drawables private const string tutorial_filename = "1011011 nekodex - new beginnings.osz"; + /// + /// Contest winners or other special cases. + /// + private static readonly string[] always_bundled_beatmaps = + { + // This thing is 40mb, I'm not sure we want it here... + @"1388906 Raphlesia & BilliumMoto - My Love.osz" + }; + private static readonly string[] bundled_osu = { "682286 Yuyoyuppe - Emerald Galaxy.osz", From 6ccbdc85a37fc172159f591762efe4110875e3fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Apr 2022 11:56:46 +0900 Subject: [PATCH 0651/1276] Improve setting description text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Screens/Play/SongProgress.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index a21a08e558..b38dcb937d 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Play /// public readonly Bindable AllowSeeking = new Bindable(); - [SettingSource("Show difficulty graph", "Whether a graph should show difficulty throughout the beatmap")] + [SettingSource("Show difficulty graph", "Whether a graph displaying difficulty throughout the beatmap should be shown")] public Bindable ShowGraph { get; } = new BindableBool(true); public override bool HandleNonPositionalInput => AllowSeeking.Value; From 22e9620211fd32352494ef48ba336dad178471b3 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 29 Apr 2022 12:09:11 +0900 Subject: [PATCH 0652/1276] Don't block clicks when dragging osu! logo --- osu.Game/Screens/Menu/OsuLogo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 1217a28fe7..f5743c7d5a 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -405,6 +405,8 @@ namespace osu.Game.Screens.Menu private class DragContainer : Container { + public override bool DragBlocksClick => false; + protected override bool OnDragStart(DragStartEvent e) => true; protected override void OnDrag(DragEvent e) From d7441fbcd443464ca8e2e108ac677e68b42f1f51 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 29 Apr 2022 12:16:37 +0900 Subject: [PATCH 0653/1276] Move diagnostic down --- .editorconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 49e83c9103..840fa98334 100644 --- a/.editorconfig +++ b/.editorconfig @@ -175,8 +175,6 @@ csharp_style_prefer_switch_expression = false:none #Supressing roslyn built-in analyzers # Suppress: EC112 -dotnet_diagnostic.OLOC001.words_in_name = 5 - #Private method is unused dotnet_diagnostic.IDE0051.severity = silent #Private member is unused @@ -193,4 +191,5 @@ dotnet_diagnostic.CA2225.severity = none # Banned APIs dotnet_diagnostic.RS0030.severity = error +dotnet_diagnostic.OLOC001.words_in_name = 5 dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. From a4ca8bfe75ab0aa19c3aa20275492cdfe4a182fa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 05:26:56 +0300 Subject: [PATCH 0654/1276] Improve "Alternate" to reset before first hitobject after break --- osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs index 46b97dd23b..dfa28a537a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs @@ -2,21 +2,24 @@ // 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; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; +using osu.Game.Utils; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModAlternate : Mod, IApplicableToDrawableRuleset, IApplicableToPlayer + public class OsuModAlternate : Mod, IApplicableToDrawableRuleset { public override string Name => @"Alternate"; public override string Acronym => @"AL"; @@ -26,9 +29,16 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Conversion; public override IconUsage? Icon => FontAwesome.Solid.Keyboard; - private double firstObjectValidJudgementTime; - private IBindable isBreakTime; private const double flash_duration = 1000; + + /// + /// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods). + /// + /// + /// This is different from in that the periods here end strictly at the first object after the break, rather than the break's end time. + /// + private PeriodTracker nonGameplayPeriods; + private OsuAction? lastActionPressed; private DrawableRuleset ruleset; @@ -39,29 +49,30 @@ namespace osu.Game.Rulesets.Osu.Mods ruleset = drawableRuleset; drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); - var firstHitObject = ruleset.Objects.FirstOrDefault(); - firstObjectValidJudgementTime = (firstHitObject?.StartTime ?? 0) - (firstHitObject?.HitWindows.WindowFor(HitResult.Meh) ?? 0); + var periods = new List(); + + if (drawableRuleset.Objects.Any()) + { + periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1)); + + foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks) + periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1)); + + static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh); + } + + nonGameplayPeriods = new PeriodTracker(periods); gameplayClock = drawableRuleset.FrameStableClock; } - public void ApplyToPlayer(Player player) - { - isBreakTime = player.IsBreakTime.GetBoundCopy(); - isBreakTime.ValueChanged += e => - { - if (e.NewValue) - lastActionPressed = null; - }; - } - private bool checkCorrectAction(OsuAction action) { - if (isBreakTime.Value) - return true; - - if (gameplayClock.CurrentTime < firstObjectValidJudgementTime) + if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime)) + { + lastActionPressed = null; return true; + } switch (action) { From ee99b949402e7917dc579a84dd9a2fbfae9a5708 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Apr 2022 13:46:18 +0900 Subject: [PATCH 0655/1276] Fix return condition from `EnsureMutableSkin` not accounting for "failed" imports --- osu.Game/Skinning/SkinManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 77e60d179c..5e1902f520 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -174,9 +174,10 @@ namespace osu.Game.Skinning // currently this only happens on save. result.PerformRead(skin => Save(skin.CreateInstance(this))); CurrentSkinInfo.Value = result; + return true; } - return true; + return false; }); } From 7e3d1511c67d620ba2dc63059c6cec86ed25d244 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 06:55:44 +0300 Subject: [PATCH 0656/1276] Hide "Rank Achieved" sorting mode until it's supported --- osu.Game/Screens/Select/Filter/SortMode.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Filter/SortMode.cs b/osu.Game/Screens/Select/Filter/SortMode.cs index 1ab54fa069..5dfa2a2664 100644 --- a/osu.Game/Screens/Select/Filter/SortMode.cs +++ b/osu.Game/Screens/Select/Filter/SortMode.cs @@ -27,8 +27,9 @@ namespace osu.Game.Screens.Select.Filter [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.ArtistTracksLength))] Length, - [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchFiltersRank))] - RankAchieved, + // todo: pending support (https://github.com/ppy/osu/issues/4917) + // [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchFiltersRank))] + // RankAchieved, [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoSource))] Source, From ef56dc07b5eea8392407aec09de8f3adbd1e8681 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 06:55:58 +0300 Subject: [PATCH 0657/1276] Hide "modding" tab in user profile until it's implemented --- osu.Game/Overlays/Profile/ProfileHeader.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index fab2487c0d..44e0d9c37f 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -31,7 +31,9 @@ namespace osu.Game.Overlays.Profile User.ValueChanged += e => updateDisplay(e.NewValue); TabControl.AddItem(LayoutStrings.HeaderUsersShow); - TabControl.AddItem(LayoutStrings.HeaderUsersModding); + + // todo: pending implementation. + // TabControl.AddItem(LayoutStrings.HeaderUsersModding); centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); } From fef94d49f4a57b1a1ac7bfd4fa5ef87278e2f049 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 08:02:07 +0300 Subject: [PATCH 0658/1276] Revert "Convert data type of `DistanceSpacing` to float" This reverts commit 7aaa88cac2be239dcc778a1082585f8e8d5a7e55. --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 2 +- .../TestSceneHitObjectComposerDistanceSnapping.cs | 2 +- .../Visual/Editing/TestSceneDistanceSnapGrid.cs | 2 +- .../UserInterface/TestSceneExpandingContainer.cs | 4 ++-- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- osu.Game/Beatmaps/EFBeatmapInfo.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- .../Settings/Sections/Gameplay/InputSettings.cs | 2 +- osu.Game/Overlays/Settings/Sections/SizeSlider.cs | 7 +++++-- .../Sections/UserInterface/GeneralSettings.cs | 2 +- .../Rulesets/Edit/DistancedHitObjectComposer.cs | 14 +++++++------- osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs | 2 +- .../Edit/Compose/Components/DistanceSnapGrid.cs | 2 +- 13 files changed, 24 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 4cccddddf3..2ba30c5f74 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); - public IBindable DistanceSpacingMultiplier { get; } = new BindableFloat(1); + public IBindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length; diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index 2c2c06ae0c..a2ee97210a 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -222,7 +222,7 @@ namespace osu.Game.Tests.Editing { public new EditorBeatmap EditorBeatmap => base.EditorBeatmap; - public new Bindable DistanceSpacingMultiplier => base.DistanceSpacingMultiplier; + public new Bindable DistanceSpacingMultiplier => base.DistanceSpacingMultiplier; public TestHitObjectComposer() : base(new OsuRuleset()) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 20bf9b3fa0..b9cfa84a5d 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.Editing public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); - public IBindable DistanceSpacingMultiplier { get; } = new BindableFloat(1); + public IBindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); public float GetBeatSnapDistanceAt(HitObject referenceObject) => 10; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs index f4920b4412..288c0cb140 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.UserInterface private TestExpandingContainer container; private SettingsToolboxGroup toolboxGroup; - private ExpandableSlider slider1; + private ExpandableSlider> slider1; private ExpandableSlider slider2; [SetUp] @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.UserInterface Width = 1, Children = new Drawable[] { - slider1 = new ExpandableSlider + slider1 = new ExpandableSlider> { Current = new BindableFloat { diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 1b364247a8..1a9703f478 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -109,7 +109,7 @@ namespace osu.Game.Beatmaps public bool SamplesMatchPlaybackRate { get; set; } = true; - public float DistanceSpacing { get; set; } = 1.0f; + public double DistanceSpacing { get; set; } = 1.0; public int BeatDivisor { get; set; } diff --git a/osu.Game/Beatmaps/EFBeatmapInfo.cs b/osu.Game/Beatmaps/EFBeatmapInfo.cs index 67c4dbe788..740adfd1c7 100644 --- a/osu.Game/Beatmaps/EFBeatmapInfo.cs +++ b/osu.Game/Beatmaps/EFBeatmapInfo.cs @@ -107,7 +107,7 @@ namespace osu.Game.Beatmaps [NotMapped] public int[] Bookmarks { get; set; } = Array.Empty(); - public float DistanceSpacing { get; set; } + public double DistanceSpacing { get; set; } public int BeatDivisor { get; set; } public int GridSize { get; set; } public double TimelineZoom { get; set; } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 0ba7a07010..79d8bd3bb3 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -240,7 +240,7 @@ namespace osu.Game.Beatmaps.Formats break; case @"DistanceSpacing": - beatmap.BeatmapInfo.DistanceSpacing = Math.Max(0, Parsing.ParseFloat(pair.Value)); + beatmap.BeatmapInfo.DistanceSpacing = Math.Max(0, Parsing.ParseDouble(pair.Value)); break; case @"BeatDivisor": diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs index 962572ca6e..83ea655601 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { Children = new Drawable[] { - new SettingsSlider + new SettingsSlider> { LabelText = SkinSettingsStrings.GameplayCursorSize, Current = config.GetBindable(OsuSetting.GameplayCursorSize), diff --git a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs index 8aeb440be1..c8a46162af 100644 --- a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs +++ b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Globalization; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; @@ -9,8 +11,9 @@ namespace osu.Game.Overlays.Settings.Sections /// /// A slider intended to show a "size" multiplier number, where 1x is 1.0. /// - internal class SizeSlider : OsuSliderBar + public class SizeSlider : OsuSliderBar + where T : struct, IEquatable, IComparable, IConvertible, IFormattable { - public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x"); + public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", NumberFormatInfo.CurrentInfo); } } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 6e1558f7d7..0e3b822c82 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface LabelText = UserInterfaceStrings.CursorRotation, Current = config.GetBindable(OsuSetting.CursorRotation) }, - new SettingsSlider + new SettingsSlider> { LabelText = UserInterfaceStrings.MenuCursorSize, Current = config.GetBindable(OsuSetting.MenuCursorSize), diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 1e1fbb4edf..0505f9ab0e 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -21,18 +21,18 @@ namespace osu.Game.Rulesets.Edit public abstract class DistancedHitObjectComposer : HitObjectComposer, IDistanceSnapProvider where TObject : HitObject { - protected Bindable DistanceSpacingMultiplier { get; } = new BindableFloat(1.0f) + protected Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1.0) { - MinValue = 0.1f, - MaxValue = 6.0f, - Precision = 0.01f, + MinValue = 0.1, + MaxValue = 6.0, + Precision = 0.01, }; - IBindable IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier; + IBindable IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier; protected ExpandingToolboxContainer RightSideToolboxContainer { get; private set; } - private ExpandableSlider distanceSpacingSlider; + private ExpandableSlider> distanceSpacingSlider; private bool distanceSpacingScrollActive; protected DistancedHitObjectComposer(Ruleset ruleset) @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Edit Origin = Anchor.TopRight, Child = new EditorToolboxGroup("snapping") { - Child = distanceSpacingSlider = new ExpandableSlider + Child = distanceSpacingSlider = new ExpandableSlider> { Current = { BindTarget = DistanceSpacingMultiplier }, KeyboardStep = 0.1f, diff --git a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs index 0eb572d22a..c6e866561e 100644 --- a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Edit /// The spacing multiplier applied to beat snap distances. /// /// - IBindable DistanceSpacingMultiplier { get; } + IBindable DistanceSpacingMultiplier { get; } /// /// Retrieves the distance between two points within a timing point that are one beat length apart. diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 13c3b713b1..2500bb1f60 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved] private BindableBeatDivisor beatDivisor { get; set; } - private IBindable distanceSpacingMultiplier; + private IBindable distanceSpacingMultiplier; private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit); private readonly double? endTime; From 173a6188eb0d46db4a762e81213f1d377127140e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 08:41:37 +0300 Subject: [PATCH 0659/1276] Add back `float` cast to distance spacing computation --- osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 2500bb1f60..5568c15514 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -92,7 +92,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updateSpacing() { - DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * distanceSpacingMultiplier.Value; + DistanceSpacing = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * distanceSpacingMultiplier.Value); if (endTime == null) MaxIntervals = int.MaxValue; From a9d67d3e92dd9f892d0843ac5961172294346373 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 10:10:19 +0300 Subject: [PATCH 0660/1276] Change random button text when holding shift key --- osu.Game/Screens/Select/FooterButtonRandom.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 5cdf381879..5f8fdce054 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Select { SelectedColour = colours.Green; DeselectedColour = SelectedColour.Opacity(0.5f); - Text = @"random"; + updateText(); Action = () => { @@ -59,6 +59,18 @@ namespace osu.Game.Screens.Select }; } + protected override bool OnKeyDown(KeyDownEvent e) + { + updateText(e.ShiftPressed); + return base.OnKeyDown(e); + } + + protected override void OnKeyUp(KeyUpEvent e) + { + updateText(e.ShiftPressed); + base.OnKeyUp(e); + } + protected override bool OnClick(ClickEvent e) { rewindSearch = e.ShiftPressed; @@ -91,5 +103,7 @@ namespace osu.Game.Screens.Select rewindSearch = false; } } + + private void updateText(bool rewind = false) => Text = rewind ? "rewind" : "random"; } } From 856ca96b66ec0073f42b290cbde260e04267c8eb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 10:12:20 +0300 Subject: [PATCH 0661/1276] Allow right-clicking to rewind on random button --- .../SongSelect/TestSceneSongSelectFooter.cs | 16 +++++++++++++- osu.Game/Screens/Select/FooterButtonRandom.cs | 21 ++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs index 6485123b29..f27615eea5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooter.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Select; +using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.SongSelect @@ -36,6 +37,8 @@ namespace osu.Game.Tests.Visual.SongSelect PreviousRandom = () => previousRandomCalled = true, }, null); footer.AddButton(new FooterButtonOptions(), null); + + InputManager.MoveMouseTo(Vector2.Zero); }); [Test] @@ -70,7 +73,7 @@ namespace osu.Game.Tests.Visual.SongSelect } [Test] - public void TestFooterRewindViaMouse() + public void TestFooterRewindViaShiftMouseLeft() { AddStep("shift + click button", () => { @@ -81,5 +84,16 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled); } + + [Test] + public void TestFooterRewindViaMouseRight() + { + AddStep("right click button", () => + { + InputManager.MoveMouseTo(randomButton); + InputManager.Click(MouseButton.Right); + }); + AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled); + } } } diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 5f8fdce054..48d5836826 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -10,6 +10,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; using osuTK; +using osuTK.Input; namespace osu.Game.Screens.Select { @@ -73,13 +74,27 @@ namespace osu.Game.Screens.Select protected override bool OnClick(ClickEvent e) { - rewindSearch = e.ShiftPressed; - return base.OnClick(e); + try + { + // this uses OR to handle rewinding when clicks are triggered by other sources (i.e. right button in OnMouseUp). + rewindSearch |= e.ShiftPressed; + return base.OnClick(e); + } + finally + { + rewindSearch = false; + } } protected override void OnMouseUp(MouseUpEvent e) { - rewindSearch = false; + if (e.Button == MouseButton.Right) + { + rewindSearch = true; + TriggerClick(); + return; + } + base.OnMouseUp(e); } From 3829d278451c71aa4223c70520331f970ad79cb7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:19:22 +0300 Subject: [PATCH 0662/1276] Update overlay ruleset selector design --- osu.Game/Overlays/OverlayRulesetTabItem.cs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 9d4afc94d1..4f58d4c189 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -5,14 +5,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osuTK.Graphics; using osuTK; using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays { @@ -26,7 +24,7 @@ namespace osu.Game.Overlays set { accentColour = value; - text.FadeColour(value, 120, Easing.OutQuint); + icon.FadeColour(value, 120, Easing.OutQuint); } } @@ -35,7 +33,7 @@ namespace osu.Game.Overlays [Resolved] private OverlayColourProvider colourProvider { get; set; } - private readonly OsuSpriteText text; + private readonly Drawable icon; public OverlayRulesetTabItem(RulesetInfo value) : base(value) @@ -48,15 +46,14 @@ namespace osu.Game.Overlays { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), - Child = text = new OsuSpriteText + Spacing = new Vector2(5f, 0), + Child = icon = new ConstrainedIconContainer { - Origin = Anchor.Centre, Anchor = Anchor.Centre, - Text = value.Name, - Font = OsuFont.GetFont(size: 14), - ShadowColour = Color4.Black.Opacity(0.75f) - } + Origin = Anchor.Centre, + Size = new Vector2(20f), + Icon = value.CreateInstance().CreateIcon(), + }, }, new HoverClickSounds() }); @@ -91,7 +88,6 @@ namespace osu.Game.Overlays private void updateState() { - text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium); AccentColour = Enabled.Value ? getActiveColour() : colourProvider.Foreground1; } From 4016fe1e19695637ea0a954b63ec633fcff2d5b4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:19:47 +0300 Subject: [PATCH 0663/1276] Adjust profile ruleset selector to new design Looks weird with `AlwaysPresent`. --- .../Profile/Header/Components/ProfileRulesetTabItem.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs index 3d20fba542..15640ddc5e 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Profile.Header.Components isDefault = value; - icon.FadeTo(isDefault ? 1 : 0, 200, Easing.OutQuint); + icon.Alpha = isDefault ? 1 : 0; } } @@ -47,7 +47,6 @@ namespace osu.Game.Overlays.Profile.Header.Components Origin = Anchor.Centre, Anchor = Anchor.Centre, Alpha = 0, - AlwaysPresent = true, Icon = FontAwesome.Solid.Star, Size = new Vector2(12), }); From fae8d86e154df9e31674a2b94f66a79c259ba709 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:20:33 +0300 Subject: [PATCH 0664/1276] Fix regressed profile ruleset selector test scene Due to the changes in `APIUser`, which change equality to be based on ID. --- .../Visual/Online/TestSceneProfileRulesetSelector.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs index cbbe8b8eac..4e572b665b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs @@ -32,14 +32,14 @@ namespace osu.Game.Tests.Visual.Online }; AddStep("set osu! as default", () => selector.SetDefaultRuleset(new OsuRuleset().RulesetInfo)); - AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo)); AddStep("set taiko as default", () => selector.SetDefaultRuleset(new TaikoRuleset().RulesetInfo)); AddStep("set catch as default", () => selector.SetDefaultRuleset(new CatchRuleset().RulesetInfo)); + AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo)); - AddStep("User with osu as default", () => user.Value = new APIUser { PlayMode = "osu" }); - AddStep("User with mania as default", () => user.Value = new APIUser { PlayMode = "mania" }); - AddStep("User with taiko as default", () => user.Value = new APIUser { PlayMode = "taiko" }); - AddStep("User with catch as default", () => user.Value = new APIUser { PlayMode = "fruits" }); + AddStep("User with osu as default", () => user.Value = new APIUser { Id = 0, PlayMode = "osu" }); + AddStep("User with taiko as default", () => user.Value = new APIUser { Id = 2, PlayMode = "taiko" }); + AddStep("User with catch as default", () => user.Value = new APIUser { Id = 3, PlayMode = "fruits" }); + AddStep("User with mania as default", () => user.Value = new APIUser { Id = 1, PlayMode = "mania" }); AddStep("null user", () => user.Value = null); } } From 5e19bdbf430b76c8124f81afd4e6d04a69978950 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:21:12 +0300 Subject: [PATCH 0665/1276] Refactor beatmap ruleset selector test scene --- .../Online/TestSceneBeatmapRulesetSelector.cs | 99 ++++++++++--------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs index 63741451f3..c550c9afda 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs @@ -4,11 +4,11 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Graphics.UserInterface; +using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; -using osu.Game.Rulesets; namespace osu.Game.Tests.Visual.Online { @@ -17,79 +17,86 @@ namespace osu.Game.Tests.Visual.Online [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); - private readonly TestRulesetSelector selector; + private BeatmapRulesetSelector selector; - public TestSceneBeatmapRulesetSelector() + [SetUp] + public void SetUp() => Schedule(() => Child = selector = new BeatmapRulesetSelector { - Add(selector = new TestRulesetSelector()); - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + BeatmapSet = new APIBeatmapSet(), + }); - [Resolved] - private IRulesetStore rulesets { get; set; } + [Test] + public void TestDisplay() + { + AddSliderStep("osu", 0, 100, 0, v => updateBeatmaps(0, v)); + AddSliderStep("taiko", 0, 100, 0, v => updateBeatmaps(1, v)); + AddSliderStep("fruits", 0, 100, 0, v => updateBeatmaps(2, v)); + AddSliderStep("mania", 0, 100, 0, v => updateBeatmaps(3, v)); + + void updateBeatmaps(int ruleset, int count) + { + if (selector == null) + return; + + selector.BeatmapSet = new APIBeatmapSet + { + Beatmaps = selector.BeatmapSet.Beatmaps + .Where(b => b.Ruleset.OnlineID != ruleset) + .Concat(Enumerable.Range(0, count).Select(_ => new APIBeatmap { RulesetID = ruleset })) + .ToArray(), + }; + } + } [Test] public void TestMultipleRulesetsBeatmapSet() { - var enabledRulesets = rulesets.AvailableRulesets.Skip(1).Take(2); - AddStep("load multiple rulesets beatmapset", () => - { - selector.BeatmapSet = new APIBeatmapSet - { - Beatmaps = enabledRulesets.Select(r => new APIBeatmap { RulesetID = r.OnlineID }).ToArray() - }; - }); - - var tabItems = selector.TabContainer.TabItems; - AddAssert("other rulesets disabled", () => tabItems.Except(tabItems.Where(t => enabledRulesets.Any(r => r.Equals(t.Value)))).All(t => !t.Enabled.Value)); - AddAssert("left-most ruleset selected", () => tabItems.First(t => t.Enabled.Value).Active.Value); - } - - [Test] - public void TestSingleRulesetBeatmapSet() - { - var enabledRuleset = rulesets.AvailableRulesets.Last(); - - AddStep("load single ruleset beatmapset", () => { selector.BeatmapSet = new APIBeatmapSet { Beatmaps = new[] { - new APIBeatmap - { - RulesetID = enabledRuleset.OnlineID - } + new APIBeatmap { RulesetID = 1 }, + new APIBeatmap { RulesetID = 2 }, } }; }); - AddAssert("single ruleset selected", () => selector.SelectedTab.Value.Equals(enabledRuleset)); + AddAssert("osu disabled", () => !selector.ChildrenOfType().Single(t => t.Value.OnlineID == 0).Enabled.Value); + AddAssert("mania disabled", () => !selector.ChildrenOfType().Single(t => t.Value.OnlineID == 3).Enabled.Value); + + AddAssert("taiko selected", () => selector.ChildrenOfType().Single(t => t.Active.Value).Value.OnlineID == 1); + } + + [Test] + public void TestSingleRulesetBeatmapSet() + { + AddStep("load single ruleset beatmapset", () => + { + selector.BeatmapSet = new APIBeatmapSet + { + Beatmaps = new[] { new APIBeatmap { RulesetID = 3 } } + }; + }); + + AddAssert("single ruleset selected", () => selector.ChildrenOfType().Single(t => t.Active.Value).Value.OnlineID == 3); } [Test] public void TestEmptyBeatmapSet() { AddStep("load empty beatmapset", () => selector.BeatmapSet = new APIBeatmapSet()); - - AddAssert("no ruleset selected", () => selector.SelectedTab == null); - AddAssert("all rulesets disabled", () => selector.TabContainer.TabItems.All(t => !t.Enabled.Value)); + AddAssert("all rulesets disabled", () => selector.ChildrenOfType().All(t => !t.Active.Value && !t.Enabled.Value)); } [Test] public void TestNullBeatmapSet() { AddStep("load null beatmapset", () => selector.BeatmapSet = null); - - AddAssert("no ruleset selected", () => selector.SelectedTab == null); - AddAssert("all rulesets disabled", () => selector.TabContainer.TabItems.All(t => !t.Enabled.Value)); - } - - private class TestRulesetSelector : BeatmapRulesetSelector - { - public new TabItem SelectedTab => base.SelectedTab; - - public new TabFillFlowContainer TabContainer => base.TabContainer; + AddAssert("all rulesets disabled", () => selector.ChildrenOfType().All(t => !t.Active.Value && !t.Enabled.Value)); } } } From 55c03dc04d0505f462c41876a50797acc3698f0b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:40:40 +0300 Subject: [PATCH 0666/1276] Fix silly mistake in ordering and update test colour scheme --- .../Visual/Online/TestSceneProfileRulesetSelector.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs index 4e572b665b..ae90872439 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online public class TestSceneProfileRulesetSelector : OsuTestScene { [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); public TestSceneProfileRulesetSelector() { @@ -37,9 +37,9 @@ namespace osu.Game.Tests.Visual.Online AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo)); AddStep("User with osu as default", () => user.Value = new APIUser { Id = 0, PlayMode = "osu" }); - AddStep("User with taiko as default", () => user.Value = new APIUser { Id = 2, PlayMode = "taiko" }); - AddStep("User with catch as default", () => user.Value = new APIUser { Id = 3, PlayMode = "fruits" }); - AddStep("User with mania as default", () => user.Value = new APIUser { Id = 1, PlayMode = "mania" }); + AddStep("User with taiko as default", () => user.Value = new APIUser { Id = 1, PlayMode = "taiko" }); + AddStep("User with catch as default", () => user.Value = new APIUser { Id = 2, PlayMode = "fruits" }); + AddStep("User with mania as default", () => user.Value = new APIUser { Id = 3, PlayMode = "mania" }); AddStep("null user", () => user.Value = null); } } From 9ea3e244e7d141b1a0d56862a03a2f15aa56a372 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Apr 2022 12:48:05 +0300 Subject: [PATCH 0667/1276] Adjust ruleset tab item content spacing to match web Not too noticeable, but better match web in any case. --- osu.Game/Overlays/OverlayRulesetTabItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 4f58d4c189..32177b2b96 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(5f, 0), + Spacing = new Vector2(4f, 0), Child = icon = new ConstrainedIconContainer { Anchor = Anchor.Centre, From 670b51324ea0d6e52c036391039329ca27f3e1b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Apr 2022 23:45:02 +0900 Subject: [PATCH 0668/1276] Add basic test at top of first run scene to make adjusting UI easier --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 836cf6caad..39298f56ba 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -65,6 +65,12 @@ namespace osu.Game.Tests.Visual.UserInterface }); } + [Test] + public void TestBasic() + { + AddAssert("overlay visible", () => overlay.State.Value == Visibility.Visible); + } + [Test] [Ignore("Enable when first run setup is being displayed on first run.")] public void TestDoesntOpenOnSecondRun() From 27ee990359ab4f412e589b53ae70f740c9de7e1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Apr 2022 23:45:17 +0900 Subject: [PATCH 0669/1276] Update first-run overlay footer buttons to use new sheared design --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 36 ++++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 4277f0f2ba..75778e6c4d 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -25,7 +25,6 @@ using osu.Game.Overlays.Mods; using osu.Game.Overlays.Notifications; using osu.Game.Screens; using osu.Game.Screens.Menu; -using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Overlays { @@ -45,8 +44,8 @@ namespace osu.Game.Overlays private ScreenStack? stack; - public PurpleTriangleButton NextButton = null!; - public DangerousTriangleButton BackButton = null!; + public ShearedButton NextButton = null!; + public ShearedButton BackButton = null!; private readonly Bindable showFirstRunSetup = new Bindable(); @@ -72,7 +71,7 @@ namespace osu.Game.Overlays private Container content = null!; [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { Header.Title = FirstRunSetupOverlayStrings.FirstRunSetupTitle; Header.Description = FirstRunSetupOverlayStrings.FirstRunSetupDescription; @@ -84,7 +83,11 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 70 * 1.2f }, + Padding = new MarginPadding + { + Horizontal = 70 * 1.2f, + Bottom = 20, + }, Child = new InputBlockingContainer { Masking = true, @@ -117,14 +120,15 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Width = 0.98f, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Margin = new MarginPadding { Vertical = PADDING }, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, ColumnDimensions = new[] { - new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.Absolute, 10), + new Dimension(GridSizeMode.AutoSize), new Dimension(), + new Dimension(GridSizeMode.Absolute, 10), }, RowDimensions = new[] { @@ -134,21 +138,25 @@ namespace osu.Game.Overlays { new[] { - BackButton = new DangerousTriangleButton + Empty(), + BackButton = new ShearedButton(300) { - Width = 300, Text = CommonStrings.Back, Action = showPreviousStep, Enabled = { Value = false }, + DarkerColour = colours.Pink2, + LighterColour = colours.Pink1, }, - Empty(), - NextButton = new PurpleTriangleButton + NextButton = new ShearedButton(0) { RelativeSizeAxes = Axes.X, Width = 1, Text = FirstRunSetupOverlayStrings.GetStarted, + DarkerColour = ColourProvider.Colour2, + LighterColour = ColourProvider.Colour1, Action = showNextStep - } + }, + Empty(), }, } }); From 3996972867507a1b46c551d7c921a5a973dad3c1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 30 Apr 2022 12:23:08 +0300 Subject: [PATCH 0670/1276] Remove unnecessary `f` suffix --- osu.Game/Overlays/OverlayRulesetTabItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 32177b2b96..84815b0cc1 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(4f, 0), + Spacing = new Vector2(4, 0), Child = icon = new ConstrainedIconContainer { Anchor = Anchor.Centre, From c7ab9a89285f457737a6e5b1f2261da4a46c4e0c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 30 Apr 2022 15:35:04 +0300 Subject: [PATCH 0671/1276] Add ruleset tab item tooltips --- osu.Game/Overlays/OverlayRulesetTabItem.cs | 6 ++++- .../Components/ProfileRulesetTabItem.cs | 23 +++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 84815b0cc1..8ed75a528b 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -10,11 +10,13 @@ using osu.Game.Rulesets; using osuTK.Graphics; using osuTK; using osu.Framework.Allocation; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays { - public class OverlayRulesetTabItem : TabItem + public class OverlayRulesetTabItem : TabItem, IHasTooltip { private Color4 accentColour; @@ -35,6 +37,8 @@ namespace osu.Game.Overlays private readonly Drawable icon; + public LocalisableString TooltipText => Value.Name; + public OverlayRulesetTabItem(RulesetInfo value) : base(value) { diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs index 15640ddc5e..4a44e285bf 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -42,14 +45,20 @@ namespace osu.Game.Overlays.Profile.Header.Components public ProfileRulesetTabItem(RulesetInfo value) : base(value) { - Add(icon = new SpriteIcon + Add(icon = new DefaultRulesetIcon { Alpha = 0 }); + } + + public class DefaultRulesetIcon : SpriteIcon, IHasTooltip + { + public LocalisableString TooltipText => UsersStrings.ShowEditDefaultPlaymodeIsDefaultTooltip; + + public DefaultRulesetIcon() { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Alpha = 0, - Icon = FontAwesome.Solid.Star, - Size = new Vector2(12), - }); + Origin = Anchor.Centre; + Anchor = Anchor.Centre; + Icon = FontAwesome.Solid.Star; + Size = new Vector2(12); + } } } } From ba5da8a52aa40cc4c1092854f33b9054b4fbe5e1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 30 Apr 2022 15:36:15 +0300 Subject: [PATCH 0672/1276] Fix tooltips not shown on selected tab --- osu.Game/Overlays/OverlayRulesetTabItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 8ed75a528b..1f11b98881 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -71,7 +71,7 @@ namespace osu.Game.Overlays Enabled.BindValueChanged(_ => updateState(), true); } - public override bool PropagatePositionalInputSubTree => Enabled.Value && !Active.Value && base.PropagatePositionalInputSubTree; + public override bool PropagatePositionalInputSubTree => Enabled.Value && base.PropagatePositionalInputSubTree; protected override bool OnHover(HoverEvent e) { From b6fb0197ab1ef3f03a9270af7e035d0f97839c86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 30 Apr 2022 23:52:36 +0900 Subject: [PATCH 0673/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index ff6499631d..b6260fd1d4 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 26891ad978..fa7563da55 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index d261e13ade..e472b5f1a8 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From de393f735fdb3d2f4b93939af1a47f8a17217a17 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Fri, 29 Apr 2022 21:33:32 +0100 Subject: [PATCH 0674/1276] Implement basic layout and behaviour of new chat overlay Provides initial implementation of new chat overlay in component `ChatOverlayV2`. Contains only the basic functionality required for a functioning chat overlay according to the new design with the intent of added the rest of the functionality in subsequent PRs. Backports existing tests for the current chat overlay except for ones testing keyboard shortcuts (since they haven't been added) and tab closing behaviour (since no tabs). --- .../Visual/Online/TestSceneChatOverlayV2.cs | 365 ++++++++++++++++++ .../Chat/ChannelList/ChannelListItem.cs | 18 +- osu.Game/Overlays/Chat/ChatOverlayTopBar.cs | 67 ++++ .../Chat/Listing/ChannelListingItem.cs | 18 +- osu.Game/Overlays/ChatOverlayV2.cs | 293 ++++++++++++++ 5 files changed, 743 insertions(+), 18 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs create mode 100644 osu.Game/Overlays/Chat/ChatOverlayTopBar.cs create mode 100644 osu.Game/Overlays/ChatOverlayV2.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs new file mode 100644 index 0000000000..312bda6460 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -0,0 +1,365 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using System.Collections.Generic; +using System.Net; +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; +using osu.Game.Overlays; +using osu.Game.Overlays.Chat; +using osu.Game.Overlays.Chat.Listing; +using osu.Game.Overlays.Chat.ChannelList; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneChatOverlayV2 : OsuManualInputManagerTestScene + { + private ChatOverlayV2 chatOverlay; + private ChannelManager channelManager; + + private readonly APIUser testUser; + private readonly Channel testPMChannel; + private readonly Channel[] testChannels; + private Channel testChannel1 => testChannels[0]; + private Channel testChannel2 => testChannels[1]; + + public TestSceneChatOverlayV2() + { + testUser = new APIUser { Username = "test user", Id = 5071479 }; + testPMChannel = new Channel(testUser); + testChannels = Enumerable.Range(1, 10).Select(createPublicChannel).ToArray(); + } + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(ChannelManager), channelManager = new ChannelManager()), + }, + Children = new Drawable[] + { + channelManager, + chatOverlay = new ChatOverlayV2 { RelativeSizeAxes = Axes.Both }, + }, + }; + }); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Setup request handler", () => + { + ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) + { + case GetUpdatesRequest getUpdates: + getUpdates.TriggerFailure(new WebException()); + return true; + + case JoinChannelRequest joinChannel: + joinChannel.TriggerSuccess(); + return true; + + case LeaveChannelRequest leaveChannel: + leaveChannel.TriggerSuccess(); + return true; + + case GetMessagesRequest getMessages: + getMessages.TriggerSuccess(createChannelMessages(getMessages.Channel)); + return true; + + case GetUserRequest getUser: + if (getUser.Lookup == testUser.Username) + getUser.TriggerSuccess(testUser); + else + getUser.TriggerFailure(new WebException()); + return true; + + case PostMessageRequest postMessage: + postMessage.TriggerSuccess(new Message(RNG.Next(0, 10000000)) + { + Content = postMessage.Message.Content, + ChannelId = postMessage.Message.ChannelId, + Sender = postMessage.Message.Sender, + Timestamp = new DateTimeOffset(DateTime.Now), + }); + return true; + + default: + Logger.Log($"Unhandled Request Type: {req.GetType()}"); + return false; + } + }; + }); + + AddStep("Add test channels", () => + { + (channelManager.AvailableChannels as BindableList)?.AddRange(testChannels); + }); + } + + [Test] + public void TestShowHide() + { + AddStep("Show overlay", () => chatOverlay.Show()); + AddAssert("Overlay is visible", () => chatOverlay.State.Value == Visibility.Visible); + AddStep("Hide overlay", () => chatOverlay.Hide()); + AddAssert("Overlay is hidden", () => chatOverlay.State.Value == Visibility.Hidden); + } + + [Test] + public void TestChannelSelection() + { + AddStep("Show overlay", () => chatOverlay.Show()); + AddAssert("Listing is visible", () => listingVisibility == Visibility.Visible); + AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); + AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); + AddAssert("Listing is hidden", () => listingVisibility == Visibility.Hidden); + AddAssert("Loading is hidden", () => loadingVisibility == Visibility.Hidden); + AddAssert("Current channel is correct", () => channelManager.CurrentChannel.Value == testChannel1); + AddAssert("DrawableChannel is correct", () => currentDrawableChannel.Channel == testChannel1); + } + + [Test] + public void TestSearchInListing() + { + AddStep("Show overlay", () => chatOverlay.Show()); + AddAssert("Listing is visible", () => listingVisibility == Visibility.Visible); + AddStep("Search for 'number 2'", () => chatOverlay.ChildrenOfType().Single().Text = "number 2"); + AddUntilStep("Only channel 2 visibile", () => + { + IEnumerable listingItems = chatOverlay.ChildrenOfType() + .Where(item => item.IsPresent); + return listingItems.Count() == 1 && listingItems.Single().Channel == testChannel2; + }); + } + + [Test] + public void TestChannelCloseButton() + { + AddStep("Show overlay", () => chatOverlay.Show()); + AddStep("Join PM and public channels", () => + { + channelManager.JoinChannel(testChannel1); + channelManager.JoinChannel(testPMChannel); + }); + AddStep("Select PM channel", () => clickDrawable(getChannelListItem(testPMChannel))); + AddStep("Click close button", () => + { + ChannelListItemCloseButton closeButton = getChannelListItem(testPMChannel).ChildrenOfType().Single(); + clickDrawable(closeButton); + }); + AddAssert("PM channel closed", () => !channelManager.JoinedChannels.Contains(testPMChannel)); + AddStep("Select normal channel", () => clickDrawable(getChannelListItem(testChannel1))); + AddStep("Click close button", () => + { + ChannelListItemCloseButton closeButton = getChannelListItem(testChannel1).ChildrenOfType().Single(); + clickDrawable(closeButton); + }); + AddAssert("Normal channel closed", () => !channelManager.JoinedChannels.Contains(testChannel1)); + } + + [Test] + public void TestChatCommand() + { + AddStep("Show overlay", () => chatOverlay.Show()); + AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); + AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); + AddStep("Open chat with user", () => channelManager.PostCommand($"chat {testUser.Username}")); + AddAssert("PM channel is selected", () => channelManager.CurrentChannel.Value == testPMChannel); + AddAssert("PM channel is visibile", () => currentDrawableChannel.Channel == testPMChannel); + AddStep("Open chat with non-existent user", () => channelManager.PostCommand("chat user_doesnt_exist")); + AddAssert("Last message is error", () => channelManager.CurrentChannel.Value.Messages.Last() is ErrorMessage); + // Make sure no unnecessary requests are made when the PM channel is already open. + AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); + AddStep("Unregister request handling", () => ((DummyAPIAccess)API).HandleRequest = null); + AddStep("Open chat with user", () => channelManager.PostCommand($"chat {testUser.Username}")); + AddAssert("PM channel is selected", () => channelManager.CurrentChannel.Value == testPMChannel); + AddAssert("PM channel is visibile", () => currentDrawableChannel.Channel == testPMChannel); + } + + [Test] + public void TestMultiplayerChannelIsNotShown() + { + Channel multiplayerChannel = null; + + AddStep("Show overlay", () => chatOverlay.Show()); + AddStep("Join multiplayer channel", () => channelManager.JoinChannel(multiplayerChannel = new Channel(new APIUser()) + { + Name = "#mp_1", + Type = ChannelType.Multiplayer, + })); + AddAssert("Channel is joined", () => channelManager.JoinedChannels.Contains(multiplayerChannel)); + AddUntilStep("Channel not present in listing", () => !chatOverlay.ChildrenOfType() + .Where(item => item.IsPresent) + .Select(item => item.Channel) + .Contains(multiplayerChannel)); + } + + [Test] + public void TestHighlightOnCurrentChannel() + { + Message message = null; + + AddStep("Show overlay", () => chatOverlay.Show()); + AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); + AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); + AddStep("Send message in channel 1", () => + { + testChannel1.AddNewMessages(message = new Message + { + ChannelId = testChannel1.Id, + Content = "Message to highlight!", + Timestamp = DateTimeOffset.Now, + Sender = testUser, + }); + }); + AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); + } + + [Test] + public void TestHighlightOnAnotherChannel() + { + Message message = null; + + AddStep("Show overlay", () => chatOverlay.Show()); + AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); + AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2)); + AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); + AddStep("Send message in channel 2", () => + { + testChannel2.AddNewMessages(message = new Message + { + ChannelId = testChannel2.Id, + Content = "Message to highlight!", + Timestamp = DateTimeOffset.Now, + Sender = testUser, + }); + }); + AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel2)); + AddAssert("Channel 2 is selected", () => channelManager.CurrentChannel.Value == testChannel2); + AddAssert("Channel 2 is visible", () => currentDrawableChannel.Channel == testChannel2); + } + + [Test] + public void TestHighlightOnLeftChannel() + { + Message message = null; + + AddStep("Show overlay", () => chatOverlay.Show()); + AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); + AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2)); + AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); + AddStep("Send message in channel 2", () => + { + testChannel2.AddNewMessages(message = new Message + { + ChannelId = testChannel2.Id, + Content = "Message to highlight!", + Timestamp = DateTimeOffset.Now, + Sender = testUser, + }); + }); + AddStep("Leave channel 2", () => channelManager.LeaveChannel(testChannel2)); + AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel2)); + AddAssert("Channel 2 is selected", () => channelManager.CurrentChannel.Value == testChannel2); + AddAssert("Channel 2 is visible", () => currentDrawableChannel.Channel == testChannel2); + } + + [Test] + public void TestHighlightWhileChatNeverOpen() + { + Message message = null; + + AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); + AddStep("Send message in channel 1", () => + { + testChannel1.AddNewMessages(message = new Message + { + ChannelId = testChannel1.Id, + Content = "Message to highlight!", + Timestamp = DateTimeOffset.Now, + Sender = testUser, + }); + }); + AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); + } + + [Test] + public void TestHighlightWithNullChannel() + { + Message message = null; + + AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); + AddStep("Send message in channel 1", () => + { + testChannel1.AddNewMessages(message = new Message + { + ChannelId = testChannel1.Id, + Content = "Message to highlight!", + Timestamp = DateTimeOffset.Now, + Sender = testUser, + }); + }); + AddStep("Set null channel", () => channelManager.CurrentChannel.Value = null); + AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); + } + + private Visibility listingVisibility => + chatOverlay.ChildrenOfType().Single().State.Value; + + private Visibility loadingVisibility => + chatOverlay.ChildrenOfType().Single().State.Value; + + private DrawableChannel currentDrawableChannel => + chatOverlay.ChildrenOfType>().Single().Child; + + private ChannelListItem getChannelListItem(Channel channel) => + chatOverlay.ChildrenOfType().Single(item => item.Channel == channel); + + private void clickDrawable(Drawable d) + { + InputManager.MoveMouseTo(d); + InputManager.Click(MouseButton.Left); + } + + private List createChannelMessages(Channel channel) + { + var message = new Message + { + ChannelId = channel.Id, + Content = $"Hello, this is a message in {channel.Name}", + Sender = testUser, + Timestamp = new DateTimeOffset(DateTime.Now), + }; + return new List { message }; + } + + private Channel createPublicChannel(int id) => new Channel + { + Id = id, + Name = $"#channel-{id}", + Topic = $"We talk about the number {id} here", + Type = ChannelType.Public, + }; + } +} diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index 7c4a72559b..fa8fae29e5 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -25,14 +25,14 @@ namespace osu.Game.Overlays.Chat.ChannelList public event Action? OnRequestSelect; public event Action? OnRequestLeave; + public readonly Channel Channel; + public readonly BindableInt Mentions = new BindableInt(); public readonly BindableBool Unread = new BindableBool(); public readonly BindableBool SelectorActive = new BindableBool(); - private readonly Channel channel; - private Box hoverBox = null!; private Box selectBox = null!; private OsuSpriteText text = null!; @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Chat.ChannelList public ChannelListItem(Channel channel) { - this.channel = channel; + Channel = channel; } [BackgroundDependencyLoader] @@ -92,7 +92,7 @@ namespace osu.Game.Overlays.Chat.ChannelList { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Text = channel.Name, + Text = Channel.Name, Font = OsuFont.Torus.With(size: 17, weight: FontWeight.SemiBold), Colour = colourProvider.Light3, Margin = new MarginPadding { Bottom = 2 }, @@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Chat.ChannelList Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Margin = new MarginPadding { Right = 3 }, - Action = () => OnRequestLeave?.Invoke(channel), + Action = () => OnRequestLeave?.Invoke(Channel), } } }, @@ -119,7 +119,7 @@ namespace osu.Game.Overlays.Chat.ChannelList }, }; - Action = () => OnRequestSelect?.Invoke(channel); + Action = () => OnRequestSelect?.Invoke(Channel); } protected override void LoadComplete() @@ -151,10 +151,10 @@ namespace osu.Game.Overlays.Chat.ChannelList private Drawable createIcon() { - if (channel.Type != ChannelType.PM) + if (Channel.Type != ChannelType.PM) return Drawable.Empty(); - return new UpdateableAvatar(channel.Users.First(), isInteractive: false) + return new UpdateableAvatar(Channel.Users.First(), isInteractive: false) { Size = new Vector2(20), Margin = new MarginPadding { Right = 5 }, @@ -167,7 +167,7 @@ namespace osu.Game.Overlays.Chat.ChannelList private void updateSelectState() { - if (selectedChannel.Value == channel && !SelectorActive.Value) + if (selectedChannel.Value == Channel && !SelectorActive.Value) selectBox.FadeIn(300, Easing.OutQuint); else selectBox.FadeOut(200, Easing.OutQuint); diff --git a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs new file mode 100644 index 0000000000..9ba7608d89 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs @@ -0,0 +1,67 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Overlays.Chat +{ + public class ChatOverlayTopBar : Container + { + // IsHovered is used by overlay + public override bool HandlePositionalInput => true; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider, TextureStore textures) + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background3, + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 50), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = textures.Get("Icons/Hexacons/messaging"), + Size = new Vector2(18), + }, + // Placeholder text + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "osu!chat", + Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), + Margin = new MarginPadding { Bottom = 2f }, + }, + }, + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs b/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs index 526cbcda87..1f0cbae7e2 100644 --- a/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs +++ b/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs @@ -25,11 +25,11 @@ namespace osu.Game.Overlays.Chat.Listing public event Action? OnRequestJoin; public event Action? OnRequestLeave; - public bool FilteringActive { get; set; } - public IEnumerable FilterTerms => new[] { channel.Name, channel.Topic ?? string.Empty }; - public bool MatchingFilter { set => this.FadeTo(value ? 1f : 0f, 100); } + public readonly Channel Channel; - private readonly Channel channel; + public bool FilteringActive { get; set; } + public IEnumerable FilterTerms => new[] { Channel.Name, Channel.Topic ?? string.Empty }; + public bool MatchingFilter { set => this.FadeTo(value ? 1f : 0f, 100); } private Box hoverBox = null!; private SpriteIcon checkbox = null!; @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Chat.Listing public ChannelListingItem(Channel channel) { - this.channel = channel; + Channel = channel; } [BackgroundDependencyLoader] @@ -94,7 +94,7 @@ namespace osu.Game.Overlays.Chat.Listing { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Text = channel.Name, + Text = Channel.Name, Font = OsuFont.Torus.With(size: text_size, weight: FontWeight.SemiBold), Margin = new MarginPadding { Bottom = 2 }, }, @@ -102,7 +102,7 @@ namespace osu.Game.Overlays.Chat.Listing { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Text = channel.Topic, + Text = Channel.Topic, Font = OsuFont.Torus.With(size: text_size), Margin = new MarginPadding { Bottom = 2 }, }, @@ -134,7 +134,7 @@ namespace osu.Game.Overlays.Chat.Listing { base.LoadComplete(); - channelJoined = channel.Joined.GetBoundCopy(); + channelJoined = Channel.Joined.GetBoundCopy(); channelJoined.BindValueChanged(change => { const double duration = 500; @@ -155,7 +155,7 @@ namespace osu.Game.Overlays.Chat.Listing } }, true); - Action = () => (channelJoined.Value ? OnRequestLeave : OnRequestJoin)?.Invoke(channel); + Action = () => (channelJoined.Value ? OnRequestLeave : OnRequestJoin)?.Invoke(Channel); } protected override bool OnHover(HoverEvent e) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs new file mode 100644 index 0000000000..01b8ed2a45 --- /dev/null +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -0,0 +1,293 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +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.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Configuration; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; +using osu.Game.Online.Chat; +using osu.Game.Overlays.Chat; +using osu.Game.Overlays.Chat.ChannelList; +using osu.Game.Overlays.Chat.Listing; + +namespace osu.Game.Overlays +{ + public class ChatOverlayV2 : OsuFocusedOverlayContainer, INamedOverlayComponent + { + public string IconTexture => "Icons/Hexacons/messaging"; + public LocalisableString Title => ChatStrings.HeaderTitle; + public LocalisableString Description => ChatStrings.HeaderDescription; + + private ChatOverlayTopBar topBar = null!; + private ChannelList channelList = null!; + private LoadingLayer loading = null!; + private ChannelListing channelListing = null!; + private ChatTextBar textBar = null!; + private Container currentChannelContainer = null!; + + private Bindable? chatHeight; + private bool isDraggingTopBar; + private float dragStartChatHeight; + + private const int transition_length = 500; + private const float default_chat_height = 0.4f; + private const float top_bar_height = 40; + private const float side_bar_width = 190; + private const float chat_bar_height = 60; + + private readonly BindableBool selectorActive = new BindableBool(); + + [Resolved] + private OsuConfigManager config { get; set; } = null!; + + [Resolved] + private ChannelManager channelManager { get; set; } = null!; + + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + + [Cached] + private readonly Bindable currentChannel = new Bindable(); + + [BackgroundDependencyLoader] + private void load() + { + // Width = 0.85f; // Matches OnlineOverlay + Height = default_chat_height; + RelativeSizeAxes = Axes.Both; + RelativePositionAxes = Axes.Both; + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + Masking = true; + CornerRadius = 7f; + Margin = new MarginPadding { Bottom = -10 }; + Padding = new MarginPadding { Bottom = 10 }; + + Children = new Drawable[] + { + topBar = new ChatOverlayTopBar + { + RelativeSizeAxes = Axes.X, + Height = top_bar_height, + }, + channelList = new ChannelList + { + RelativeSizeAxes = Axes.Y, + Width = side_bar_width, + Padding = new MarginPadding { Top = top_bar_height }, + SelectorActive = { BindTarget = selectorActive }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Padding = new MarginPadding + { + Top = top_bar_height, + Left = side_bar_width, + Bottom = chat_bar_height, + }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4, + }, + currentChannelContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + loading = new LoadingLayer(true), + channelListing = new ChannelListing + { + RelativeSizeAxes = Axes.Both, + }, + }, + }, + textBar = new ChatTextBar + { + RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Padding = new MarginPadding { Left = side_bar_width }, + ShowSearch = { BindTarget = selectorActive }, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + loading.Show(); + + chatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight).GetBoundCopy(); + chatHeight.BindValueChanged(height => { Height = height.NewValue; }, true); + + currentChannel.BindTo(channelManager.CurrentChannel); + channelManager.CurrentChannel.BindValueChanged(currentChannelChanged, true); + channelManager.JoinedChannels.BindCollectionChanged(joinedChannelsChanged, true); + channelManager.AvailableChannels.BindCollectionChanged(availableChannelsChanged, true); + + channelList.OnRequestSelect += channel => + { + // Manually selecting a channel should dismiss the selector + selectorActive.Value = false; + channelManager.CurrentChannel.Value = channel; + }; + channelList.OnRequestLeave += channel => channelManager.LeaveChannel(channel); + + channelListing.OnRequestJoin += channel => channelManager.JoinChannel(channel); + channelListing.OnRequestLeave += channel => channelManager.LeaveChannel(channel); + + textBar.OnSearchTermsChanged += searchTerms => channelListing.SearchTerm = searchTerms; + textBar.OnChatMessageCommitted += handleChatMessage; + + selectorActive.BindValueChanged(v => channelListing.State.Value = v.NewValue ? Visibility.Visible : Visibility.Hidden, true); + } + + /// + /// Highlights a certain message in the specified channel. + /// + /// The message to highlight. + /// The channel containing the message. + public void HighlightMessage(Message message, Channel channel) + { + Debug.Assert(channel.Id == message.ChannelId); + + if (currentChannel.Value?.Id != channel.Id) + { + if (!channel.Joined.Value) + channel = channelManager.JoinChannel(channel); + + channelManager.CurrentChannel.Value = channel; + } + + selectorActive.Value = false; + + channel.HighlightedMessage.Value = message; + + Show(); + } + + protected override bool OnDragStart(DragStartEvent e) + { + isDraggingTopBar = topBar.IsHovered; + + if (!isDraggingTopBar) + return base.OnDragStart(e); + + dragStartChatHeight = chatHeight!.Value; + return true; + } + + protected override void OnDrag(DragEvent e) + { + if (!isDraggingTopBar) + return; + + float targetChatHeight = dragStartChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; + chatHeight!.Value = targetChatHeight; + } + + protected override void OnDragEnd(DragEndEvent e) + { + isDraggingTopBar = false; + base.OnDragEnd(e); + } + + protected override void PopIn() + { + this.MoveToY(0, transition_length, Easing.OutQuint); + this.FadeIn(transition_length, Easing.OutQuint); + base.PopIn(); + } + + protected override void PopOut() + { + this.MoveToY(Height, transition_length, Easing.InSine); + this.FadeOut(transition_length, Easing.InSine); + base.PopOut(); + } + + private void currentChannelChanged(ValueChangedEvent e) + { + Channel? newChannel = e.NewValue; + + loading.Show(); + + // Channel is null when leaving the currently selected channel + if (newChannel == null) + { + // Find another channel to switch to + newChannel = channelManager.JoinedChannels.FirstOrDefault(chan => chan != e.OldValue); + + if (newChannel == null) + selectorActive.Value = true; + else + currentChannel.Value = newChannel; + + return; + } + + LoadComponentAsync(new DrawableChannel(newChannel), loaded => + { + currentChannelContainer.Clear(); + currentChannelContainer.Add(loaded); + loading.Hide(); + }); + } + + private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) + { + switch (args.Action) + { + case NotifyCollectionChangedAction.Add: + IEnumerable joinedChannels = filterChannels(args.NewItems); + foreach (var channel in joinedChannels) + channelList.AddChannel(channel); + break; + + case NotifyCollectionChangedAction.Remove: + IEnumerable leftChannels = filterChannels(args.OldItems); + foreach (var channel in leftChannels) + channelList.RemoveChannel(channel); + break; + } + } + + private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) + => channelListing.UpdateAvailableChannels(channelManager.AvailableChannels); + + private IEnumerable filterChannels(IList channels) + => channels.Cast().Where(c => c.Type == ChannelType.Public || c.Type == ChannelType.PM); + + private void handleChatMessage(string message) + { + if (string.IsNullOrWhiteSpace(message)) + return; + + if (message[0] == '/') + channelManager.PostCommand(message.Substring(1)); + else + channelManager.PostMessage(message); + } + } +} + From 5c13200c75d581b796306b04e5d21ad3082beb3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 1 May 2022 13:01:38 +0900 Subject: [PATCH 0675/1276] Update production endpoint to new version --- osu.Game/Online/ProductionEndpointConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/ProductionEndpointConfiguration.cs b/osu.Game/Online/ProductionEndpointConfiguration.cs index c6ddc03564..e44dad1db5 100644 --- a/osu.Game/Online/ProductionEndpointConfiguration.cs +++ b/osu.Game/Online/ProductionEndpointConfiguration.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online WebsiteRootUrl = APIEndpointUrl = @"https://osu.ppy.sh"; APIClientSecret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; APIClientID = "5"; - SpectatorEndpointUrl = "https://spectator.ppy.sh/spectator"; - MultiplayerEndpointUrl = "https://spectator.ppy.sh/multiplayer"; + SpectatorEndpointUrl = "https://spectator2.ppy.sh/spectator"; + MultiplayerEndpointUrl = "https://spectator2.ppy.sh/multiplayer"; } } } From 4bd1d091486feba7933432359758c531c40ec29b Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sun, 1 May 2022 12:20:11 +0100 Subject: [PATCH 0676/1276] Remove blank line --- osu.Game/Overlays/ChatOverlayV2.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 01b8ed2a45..d0b49d9ef3 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -290,4 +290,3 @@ namespace osu.Game.Overlays } } } - From bcce807311ae0a455ebf0771322c63ba9ed8c6d7 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sun, 1 May 2022 12:20:54 +0100 Subject: [PATCH 0677/1276] Fix chat command test as reference equality checks on PM channels doesn't seem to to work --- osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 312bda6460..c7bb6760c7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -185,16 +185,17 @@ namespace osu.Game.Tests.Visual.Online AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Open chat with user", () => channelManager.PostCommand($"chat {testUser.Username}")); - AddAssert("PM channel is selected", () => channelManager.CurrentChannel.Value == testPMChannel); - AddAssert("PM channel is visibile", () => currentDrawableChannel.Channel == testPMChannel); + AddAssert("PM channel is selected", () => + channelManager.CurrentChannel.Value.Type == ChannelType.PM && channelManager.CurrentChannel.Value.Users.Single() == testUser); AddStep("Open chat with non-existent user", () => channelManager.PostCommand("chat user_doesnt_exist")); AddAssert("Last message is error", () => channelManager.CurrentChannel.Value.Messages.Last() is ErrorMessage); + // Make sure no unnecessary requests are made when the PM channel is already open. AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddStep("Unregister request handling", () => ((DummyAPIAccess)API).HandleRequest = null); AddStep("Open chat with user", () => channelManager.PostCommand($"chat {testUser.Username}")); - AddAssert("PM channel is selected", () => channelManager.CurrentChannel.Value == testPMChannel); - AddAssert("PM channel is visibile", () => currentDrawableChannel.Channel == testPMChannel); + AddAssert("PM channel is selected", () => + channelManager.CurrentChannel.Value.Type == ChannelType.PM && channelManager.CurrentChannel.Value.Users.Single() == testUser); } [Test] From 4b30d0e59b84cc4a8a496cd5d2ab8fcf16f125f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 May 2022 17:42:56 +0900 Subject: [PATCH 0678/1276] Fix first-run overlay's song select applying track adjustments Closes https://github.com/ppy/osu/issues/18041. --- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 1bd82f6d99..862506add2 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -99,6 +99,8 @@ namespace osu.Game.Overlays.FirstRunSetup private class NestedSongSelect : PlaySongSelect { protected override bool ControlGlobalMusic => false; + + public override bool? AllowTrackAdjustments => false; } private class PinnedMainMenu : MainMenu From c3e0ba5c8db4bd3d2042445ad1d8db136e5b93e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 May 2022 20:27:00 +0900 Subject: [PATCH 0679/1276] Fix clicking anywhere in the beatmap overlay dismissing it --- osu.Game/Overlays/BeatmapSetOverlay.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index b9d3854066..bd63c997df 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -5,7 +5,6 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Events; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet; @@ -24,9 +23,6 @@ namespace osu.Game.Overlays private readonly Bindable beatmapSet = new Bindable(); - // receive input outside our bounds so we can trigger a close event on ourselves. - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - public BeatmapSetOverlay() : base(OverlayColourScheme.Blue) { @@ -71,12 +67,6 @@ namespace osu.Game.Overlays beatmapSet.Value = null; } - protected override bool OnClick(ClickEvent e) - { - Hide(); - return true; - } - public void FetchAndShowBeatmap(int beatmapId) { beatmapSet.Value = null; From 55949e8407bd91ad1bc32e09084d3db27cb986e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 May 2022 20:30:03 +0900 Subject: [PATCH 0680/1276] Make disabled sheared buttons darker --- osu.Game/Graphics/UserInterface/ShearedButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index c3c566782f..d46fa24409 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -176,8 +176,8 @@ namespace osu.Game.Graphics.UserInterface if (!Enabled.Value) { - colourDark = colourDark.Darken(0.3f); - colourLight = colourLight.Darken(0.3f); + colourDark = colourDark.Darken(1f); + colourLight = colourLight.Darken(1f); } else if (IsHovered) { From f9a1d9df56968b8ebe8ff094ae990c4615214690 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 May 2022 20:30:36 +0900 Subject: [PATCH 0681/1276] Reduce scale effect slightly (was feeling too bouncy) --- osu.Game/Graphics/UserInterface/ShearedButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index d46fa24409..dea44e6d99 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -158,7 +158,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnMouseDown(MouseDownEvent e) { - Content.ScaleTo(0.8f, 2000, Easing.OutQuint); + Content.ScaleTo(0.9f, 2000, Easing.OutQuint); return base.OnMouseDown(e); } From 18852b250965df3f8c51be41682c2fca9719f298 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 2 May 2022 15:31:47 +0300 Subject: [PATCH 0682/1276] Fix footer random button autosizing to text length --- osu.Game/Screens/Select/FooterButtonRandom.cs | 51 +++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 48d5836826..f855b80f75 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -19,6 +20,9 @@ namespace osu.Game.Screens.Select public Action NextRandom { get; set; } public Action PreviousRandom { get; set; } + private Container persistentText; + private OsuSpriteText randomSpriteText; + private OsuSpriteText rewindSpriteText; private bool rewindSearch; [BackgroundDependencyLoader] @@ -26,7 +30,32 @@ namespace osu.Game.Screens.Select { SelectedColour = colours.Green; DeselectedColour = SelectedColour.Opacity(0.5f); - updateText(); + + TextContainer.Add(persistentText = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AlwaysPresent = true, + AutoSizeAxes = Axes.Both, + Children = new[] + { + randomSpriteText = new OsuSpriteText + { + AlwaysPresent = true, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "random", + }, + rewindSpriteText = new OsuSpriteText + { + AlwaysPresent = true, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "rewind", + Alpha = 0f, + } + } + }); Action = () => { @@ -34,22 +63,22 @@ namespace osu.Game.Screens.Select { const double fade_time = 500; - OsuSpriteText rewindSpriteText; + OsuSpriteText fallingRewind; - TextContainer.Add(rewindSpriteText = new OsuSpriteText + TextContainer.Add(fallingRewind = new OsuSpriteText { Alpha = 0, - Text = @"rewind", + Text = rewindSpriteText.Text, AlwaysPresent = true, // make sure the button is sized large enough to always show this Anchor = Anchor.Centre, Origin = Anchor.Centre, }); - rewindSpriteText.FadeOutFromOne(fade_time, Easing.In); - rewindSpriteText.MoveTo(Vector2.Zero).MoveTo(new Vector2(0, 10), fade_time, Easing.In); - rewindSpriteText.Expire(); + fallingRewind.FadeOutFromOne(fade_time, Easing.In); + fallingRewind.MoveTo(Vector2.Zero).MoveTo(new Vector2(0, 10), fade_time, Easing.In); + fallingRewind.Expire(); - SpriteText.FadeInFromZero(fade_time, Easing.In); + persistentText.FadeInFromZero(fade_time, Easing.In); PreviousRandom.Invoke(); } @@ -119,6 +148,10 @@ namespace osu.Game.Screens.Select } } - private void updateText(bool rewind = false) => Text = rewind ? "rewind" : "random"; + private void updateText(bool rewind = false) + { + randomSpriteText.Alpha = rewind ? 0 : 1; + rewindSpriteText.Alpha = rewind ? 1 : 0; + } } } From c4c62ff4e7c478dcafdcc21b166cd34ecc64a11d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 May 2022 22:46:14 +0900 Subject: [PATCH 0683/1276] Fix countdown sounds playing every minute, rather than only on the last minute --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index a7e18622dc..62310bcaac 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -106,7 +106,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { updateButtonText(); - int secondsRemaining = countdownTimeRemaining.Seconds; + int secondsRemaining = (int)countdownTimeRemaining.TotalSeconds; playTickSound(secondsRemaining); From e6f1ac6bec24e483f42e985a4ca43cf0454d99f5 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 2 May 2022 20:08:33 +0100 Subject: [PATCH 0684/1276] Ensure "chatting in..." text is aligned with chat message --- osu.Game/Overlays/Chat/ChatTextBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index ef20149dac..15fa2d87db 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Chat private Container searchIconContainer = null!; private ChatTextBox chatTextBox = null!; - private const float chatting_text_width = 180; + private const float chatting_text_width = 240; private const float search_icon_width = 40; [BackgroundDependencyLoader] From 1473762e2567884d8e238e1ce3c713e76c0be42b Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 2 May 2022 20:57:39 +0100 Subject: [PATCH 0685/1276] Don't wrap "chatting in.." text in `ChatTextBar` --- osu.Game/Overlays/Chat/ChatTextBar.cs | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 15fa2d87db..316511d9a6 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -11,7 +11,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; using osuTK; @@ -28,7 +29,8 @@ namespace osu.Game.Overlays.Chat [Resolved] private Bindable currentChannel { get; set; } = null!; - private OsuTextFlowContainer chattingTextContainer = null!; + private Container chattingTextContainer = null!; + private OsuSpriteText chattingText = null!; private Container searchIconContainer = null!; private ChatTextBox chatTextBox = null!; @@ -61,16 +63,19 @@ namespace osu.Game.Overlays.Chat { new Drawable[] { - chattingTextContainer = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 20)) + chattingTextContainer = new Container { - Masking = true, - Width = chatting_text_width, - Padding = new MarginPadding { Left = 10 }, RelativeSizeAxes = Axes.Y, - TextAnchor = Anchor.CentreRight, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Colour = colourProvider.Background1, + Width = chatting_text_width, + Masking = true, + Child = chattingText = new OsuSpriteText + { + Font = OsuFont.Torus.With(size: 20), + Colour = colourProvider.Background1, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Truncate = true, + }, }, searchIconContainer = new Container { @@ -131,15 +136,15 @@ namespace osu.Game.Overlays.Chat switch (newChannel?.Type) { case ChannelType.Public: - chattingTextContainer.Text = $"chatting in {newChannel.Name}"; + chattingText.Text = $"chatting in {newChannel.Name}"; break; case ChannelType.PM: - chattingTextContainer.Text = $"chatting with {newChannel.Name}"; + chattingText.Text = $"chatting with {newChannel.Name}"; break; default: - chattingTextContainer.Text = string.Empty; + chattingText.Text = string.Empty; break; } }, true); From 7f8e00c1e3e8e6d66d4f9de132c0ffdddf9b37bf Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 2 May 2022 21:22:47 +0100 Subject: [PATCH 0686/1276] Change "Add more channels" to sentence case in "ChannelList" --- osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs index 57ab7584b5..f9dab74eb1 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Chat.ChannelList { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Text = "Add More Channels", + Text = "Add more channels", Font = OsuFont.Torus.With(size: 17, weight: FontWeight.SemiBold), Colour = colourProvider.Light3, Margin = new MarginPadding { Bottom = 2 }, From a931b1ecc37a089edc4fcf55f93c94543cf26d08 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 2 May 2022 22:32:25 +0100 Subject: [PATCH 0687/1276] Show selected channel text as white in `ChannelListItem` --- .../Chat/ChannelList/ChannelListItem.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index fa8fae29e5..e6a126f4d8 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -126,13 +126,9 @@ namespace osu.Game.Overlays.Chat.ChannelList { base.LoadComplete(); - selectedChannel.BindValueChanged(_ => updateSelectState(), true); - SelectorActive.BindValueChanged(_ => updateSelectState(), true); - - Unread.BindValueChanged(change => - { - text.FadeColour(change.NewValue ? colourProvider.Content1 : colourProvider.Light3, 300, Easing.OutQuint); - }, true); + selectedChannel.BindValueChanged(_ => updateState(), true); + SelectorActive.BindValueChanged(_ => updateState(), true); + Unread.BindValueChanged(_ => updateState(), true); } protected override bool OnHover(HoverEvent e) @@ -165,12 +161,21 @@ namespace osu.Game.Overlays.Chat.ChannelList }; } - private void updateSelectState() + private void updateState() { - if (selectedChannel.Value == Channel && !SelectorActive.Value) + if (showSelected) selectBox.FadeIn(300, Easing.OutQuint); else selectBox.FadeOut(200, Easing.OutQuint); + + if (showUnread || showSelected) + text.FadeColour(colourProvider.Content1, 300, Easing.OutQuint); + else + text.FadeColour(colourProvider.Light3, 200, Easing.OutQuint); } + + private bool showUnread => Unread.Value; + + private bool showSelected => selectedChannel.Value == Channel && !SelectorActive.Value; } } From d7919f462c32def081a8424c09731088495146d7 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Tue, 3 May 2022 05:36:12 +0700 Subject: [PATCH 0688/1276] Split `SearchTextBox` into `SearchTextBox` and `BasicSearchTextBox` --- .../UserInterface/BasicSearchTextBox.cs | 26 +++++++++++++++++++ .../Graphics/UserInterface/SearchTextBox.cs | 13 ---------- 2 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/BasicSearchTextBox.cs diff --git a/osu.Game/Graphics/UserInterface/BasicSearchTextBox.cs b/osu.Game/Graphics/UserInterface/BasicSearchTextBox.cs new file mode 100644 index 0000000000..aa101d7e08 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/BasicSearchTextBox.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Graphics.UserInterface +{ + public class BasicSearchTextBox : SearchTextBox + { + public BasicSearchTextBox() + { + Add(new SpriteIcon + { + Icon = FontAwesome.Solid.Search, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Margin = new MarginPadding { Right = 10 }, + Size = new Vector2(20), + }); + + TextFlow.Padding = new MarginPadding { Right = 35 }; + } + } +} \ No newline at end of file diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index dd9ed7c9e9..cc0bc2dc46 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -1,12 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Resources.Localisation.Web; -using osuTK; using osuTK.Input; namespace osu.Game.Graphics.UserInterface @@ -18,16 +15,6 @@ namespace osu.Game.Graphics.UserInterface public SearchTextBox() { Height = 35; - Add(new SpriteIcon - { - Icon = FontAwesome.Solid.Search, - Origin = Anchor.CentreRight, - Anchor = Anchor.CentreRight, - Margin = new MarginPadding { Right = 10 }, - Size = new Vector2(20), - }); - - TextFlow.Padding = new MarginPadding { Right = 35 }; PlaceholderText = HomeStrings.SearchPlaceholder; } From 81be69e72ec3884fcc1347235036b735162efa1f Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Tue, 3 May 2022 06:18:42 +0700 Subject: [PATCH 0689/1276] Update search textbox implementation to use `BasicSearchTextBox` instead --- osu.Game/Graphics/UserInterface/SeekLimitedSearchTextBox.cs | 2 +- osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs | 2 +- osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs | 2 +- osu.Game/Overlays/Music/FilterControl.cs | 2 +- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/SeekLimitedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/SeekLimitedSearchTextBox.cs index 6a9e8a5b8c..0b76ff658f 100644 --- a/osu.Game/Graphics/UserInterface/SeekLimitedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SeekLimitedSearchTextBox.cs @@ -6,7 +6,7 @@ namespace osu.Game.Graphics.UserInterface /// /// A which does not handle left/right arrow keys for seeking. /// - public class SeekLimitedSearchTextBox : SearchTextBox + public class SeekLimitedSearchTextBox : BasicSearchTextBox { public override bool HandleLeftRightArrows => false; } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 2474515802..eeaa31a013 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -163,7 +163,7 @@ namespace osu.Game.Overlays.BeatmapListing public void TakeFocus() => textBox.TakeFocus(); - private class BeatmapSearchTextBox : SearchTextBox + private class BeatmapSearchTextBox : BasicSearchTextBox { /// /// Any time the text box receives key events (even while masked). diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs index 231d7ca63c..9b0354e264 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs @@ -181,7 +181,7 @@ namespace osu.Game.Overlays.Chat.Selection base.PopOut(); } - private class HeaderSearchTextBox : SearchTextBox + private class HeaderSearchTextBox : BasicSearchTextBox { [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Overlays/Music/FilterControl.cs b/osu.Game/Overlays/Music/FilterControl.cs index 66adbeebe8..46c66b4bae 100644 --- a/osu.Game/Overlays/Music/FilterControl.cs +++ b/osu.Game/Overlays/Music/FilterControl.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Music Collection = collectionDropdown.Current.Value?.Collection }; - public class FilterTextBox : SearchTextBox + public class FilterTextBox : BasicSearchTextBox { protected override bool AllowCommit => true; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index ec55ae79ce..f2429c1944 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -129,7 +129,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { RelativeSizeAxes = Axes.X, Height = Header.HEIGHT, - Child = searchTextBox = new SearchTextBox + Child = searchTextBox = new BasicSearchTextBox { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, From e6fbb199518c3c07a2f8a11f7d9a7f0fe2ccae8f Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Thu, 21 Apr 2022 03:59:36 +0700 Subject: [PATCH 0690/1276] Implement new search textbox design --- .../TestSceneShearedSearchTextBox.cs | 46 +++++ .../UserInterface/ShearedSearchTextBox.cs | 192 ++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneShearedSearchTextBox.cs create mode 100644 osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedSearchTextBox.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedSearchTextBox.cs new file mode 100644 index 0000000000..e1dbf04133 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedSearchTextBox.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneShearedSearchTextBox : OsuTestScene + { + [Test] + public void TestAllColourSchemes() + { + foreach (var scheme in Enum.GetValues(typeof(OverlayColourScheme)).Cast()) + AddStep($"set {scheme} scheme", () => Child = createContent(scheme)); + } + + private Drawable createContent(OverlayColourScheme colourScheme) + { + var colourProvider = new OverlayColourProvider(colourScheme); + + return new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(OverlayColourProvider), colourProvider) + }, + Children = new Drawable[] + { + new ShearedSearchTextBox + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Width = 0.5f + } + } + }; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs new file mode 100644 index 0000000000..e3dc118faa --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -0,0 +1,192 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; + +namespace osu.Game.Graphics.UserInterface +{ + public class ShearedSearchTextBox : CompositeDrawable, IHasCurrentValue + { + private const float icon_container_width = 50; + private const float corner_radius = 10; + private const float height = 42; + private readonly Vector2 shear = new Vector2(0.2f, 0); + + public FocusedTextBox TextBox; + + public Bindable Current + { + get => TextBox.Current; + set => TextBox.Current = value; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Height = height; + Shear = shear; + Masking = true; + CornerRadius = corner_radius; + InternalChild = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Colour = colourProvider.Background3, + RelativeSizeAxes = Axes.Both + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new Container + { + Name = @"Search box container", + RelativeSizeAxes = Axes.Both, + CornerRadius = corner_radius, + Masking = true, + Children = new Drawable[] + { + new Box + { + Colour = colourProvider.Background4, + RelativeSizeAxes = Axes.Both + }, + TextBox = new SearchTextBox + { + Shear = -shear, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + Padding = new MarginPadding + { + Horizontal = corner_radius + } + } + } + }, + new Container + { + Name = @"Icon container", + RelativeSizeAxes = Axes.Y, + Width = icon_container_width, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.Solid.Search, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(16), + Shear = -shear + } + } + } + } + }, + ColumnDimensions = new[] { new Dimension(), new Dimension(GridSizeMode.AutoSize) } + } + } + }; + } + + public override bool HandleNonPositionalInput => TextBox.HandleNonPositionalInput; + + private class SearchTextBox : FocusedTextBox + { + protected virtual bool AllowCommit => false; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + BackgroundFocused = colourProvider.Background4; + BackgroundUnfocused = colourProvider.Background4; + Placeholder.Colour = Color4.White; + PlaceholderText = @"Search"; + } + + protected override SpriteText CreatePlaceholder() => new OsuSpriteText + { + Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold) + }; + + protected override Drawable GetDrawableCharacter(char c) => new FallingDownContainer + { + AutoSizeAxes = Axes.Both, + Child = new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold) }, + }; + + public override bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case PlatformAction.MoveBackwardLine: + case PlatformAction.MoveForwardLine: + // Shift+delete is handled via PlatformAction on macOS. this is not so useful in the context of a SearchTextBox + // as we do not allow arrow key navigation in the first place (ie. the caret should always be at the end of text) + // Avoid handling it here to allow other components to potentially consume the shortcut. + case PlatformAction.DeleteForwardChar: + return false; + } + + return base.OnPressed(e); + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (!e.ControlPressed && !e.ShiftPressed) + { + switch (e.Key) + { + case Key.Left: + case Key.Right: + case Key.Up: + case Key.Down: + return false; + } + } + + if (!AllowCommit) + { + switch (e.Key) + { + case Key.KeypadEnter: + case Key.Enter: + return false; + } + } + + if (e.ShiftPressed) + { + switch (e.Key) + { + case Key.Delete: + return false; + } + } + + return base.OnKeyDown(e); + } + } + } +} From a436ff877f19d509ee0eb0a81a76659997aa99b1 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Fri, 22 Apr 2022 22:01:56 +0700 Subject: [PATCH 0691/1276] Apply review --- osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index e3dc118faa..89373ec8db 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -12,6 +12,7 @@ using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; +using osu.Game.Overlays.Mods; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -21,9 +22,9 @@ namespace osu.Game.Graphics.UserInterface public class ShearedSearchTextBox : CompositeDrawable, IHasCurrentValue { private const float icon_container_width = 50; - private const float corner_radius = 10; + private const float corner_radius = 7; private const float height = 42; - private readonly Vector2 shear = new Vector2(0.2f, 0); + private readonly Vector2 shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); public FocusedTextBox TextBox; @@ -78,7 +79,7 @@ namespace osu.Game.Graphics.UserInterface RelativeSizeAxes = Axes.X, Padding = new MarginPadding { - Horizontal = corner_radius + Horizontal = corner_radius + shear.X } } } From e03d086305626e3fe30e69787911f6c6b636a7a3 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Tue, 3 May 2022 11:24:48 +0700 Subject: [PATCH 0692/1276] Inherit `SearchTextBox` instead --- .../UserInterface/ShearedSearchTextBox.cs | 58 +------------------ 1 file changed, 1 insertion(+), 57 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index 89373ec8db..04c060a5bc 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -8,14 +8,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; -using osu.Framework.Input.Events; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osuTK; using osuTK.Graphics; -using osuTK.Input; namespace osu.Game.Graphics.UserInterface { @@ -113,9 +110,8 @@ namespace osu.Game.Graphics.UserInterface public override bool HandleNonPositionalInput => TextBox.HandleNonPositionalInput; - private class SearchTextBox : FocusedTextBox + private class InnerSearchTextBox : SearchTextBox { - protected virtual bool AllowCommit => false; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) @@ -136,58 +132,6 @@ namespace osu.Game.Graphics.UserInterface AutoSizeAxes = Axes.Both, Child = new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold) }, }; - - public override bool OnPressed(KeyBindingPressEvent e) - { - switch (e.Action) - { - case PlatformAction.MoveBackwardLine: - case PlatformAction.MoveForwardLine: - // Shift+delete is handled via PlatformAction on macOS. this is not so useful in the context of a SearchTextBox - // as we do not allow arrow key navigation in the first place (ie. the caret should always be at the end of text) - // Avoid handling it here to allow other components to potentially consume the shortcut. - case PlatformAction.DeleteForwardChar: - return false; - } - - return base.OnPressed(e); - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - if (!e.ControlPressed && !e.ShiftPressed) - { - switch (e.Key) - { - case Key.Left: - case Key.Right: - case Key.Up: - case Key.Down: - return false; - } - } - - if (!AllowCommit) - { - switch (e.Key) - { - case Key.KeypadEnter: - case Key.Enter: - return false; - } - } - - if (e.ShiftPressed) - { - switch (e.Key) - { - case Key.Delete: - return false; - } - } - - return base.OnKeyDown(e); - } } } } From a35502a834189826e4435ec7faf1cc3654d12481 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Tue, 3 May 2022 11:25:14 +0700 Subject: [PATCH 0693/1276] Move implementation to ctor instead of bdl method --- .../UserInterface/ShearedSearchTextBox.cs | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index 04c060a5bc..4076c4f76b 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -22,17 +22,27 @@ namespace osu.Game.Graphics.UserInterface private const float corner_radius = 7; private const float height = 42; private readonly Vector2 shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); - - public FocusedTextBox TextBox; + private readonly Box background; + private readonly Box searchBoxBackground; + private readonly SearchTextBox textBox; public Bindable Current { - get => TextBox.Current; - set => TextBox.Current = value; + get => textBox.Current; + set => textBox.Current = value; } - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + public bool HoldFocus + { + get => textBox.HoldFocus; + set => textBox.HoldFocus = value; + } + + public void TakeFocus() => textBox.TakeFocus(); + + public void KillFocus() => textBox.KillFocus(); + + public ShearedSearchTextBox() { Height = height; Shear = shear; @@ -43,9 +53,8 @@ namespace osu.Game.Graphics.UserInterface RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Box + background = new Box { - Colour = colourProvider.Background3, RelativeSizeAxes = Axes.Both }, new GridContainer @@ -63,12 +72,11 @@ namespace osu.Game.Graphics.UserInterface Masking = true, Children = new Drawable[] { - new Box + searchBoxBackground = new Box { - Colour = colourProvider.Background4, RelativeSizeAxes = Axes.Both }, - TextBox = new SearchTextBox + textBox = new InnerSearchTextBox { Shear = -shear, Anchor = Anchor.CentreLeft, @@ -108,11 +116,17 @@ namespace osu.Game.Graphics.UserInterface }; } - public override bool HandleNonPositionalInput => TextBox.HandleNonPositionalInput; + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + background.Colour = colourProvider.Background3; + searchBoxBackground.Colour = colourProvider.Background4; + } + + public override bool HandleNonPositionalInput => textBox.HandleNonPositionalInput; private class InnerSearchTextBox : SearchTextBox { - [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { From 4f16da893286b09da833f3b3aa24037019fdb266 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 2 May 2022 20:04:34 +0800 Subject: [PATCH 0694/1276] Set EnforceCodeStyleInBuild --- .globalconfig | 55 +++++++++++++++++++++++++++++++++++++++++++ Directory.Build.props | 1 + osu.sln | 1 + 3 files changed, 57 insertions(+) create mode 100644 .globalconfig diff --git a/.globalconfig b/.globalconfig new file mode 100644 index 0000000000..607798492c --- /dev/null +++ b/.globalconfig @@ -0,0 +1,55 @@ +is_global = true + +# .NET Code Style +# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ + +# IDE0001: Simplify names +dotnet_diagnostic.IDE0001.severity = warning + +# IDE0002: Simplify member access +dotnet_diagnostic.IDE0002.severity = warning + +# IDE0003: Remove qualification +dotnet_diagnostic.IDE0003.severity = warning + +# IDE0004: Remove unnecessary cast +dotnet_diagnostic.IDE0004.severity = warning + +# IDE0005: Remove unnecessary imports +dotnet_diagnostic.IDE0005.severity = warning + +# IDE0034: Simplify default literal +dotnet_diagnostic.IDE0034.severity = warning + +# IDE0036: Sort modifiers +dotnet_diagnostic.IDE0036.severity = warning + +# IDE0040: Add accessibility modifier +dotnet_diagnostic.IDE0040.severity = warning + +# IDE0049: Use keyword for type name +dotnet_diagnostic.IDE0040.severity = warning + +# IDE0055: Fix formatting +dotnet_diagnostic.IDE0055.severity = warning + +# IDE0051: Private method is unused +dotnet_diagnostic.IDE0051.severity = silent + +# IDE0052: Private member is unused +dotnet_diagnostic.IDE0052.severity = warning + +# IDE0073: File header +dotnet_diagnostic.IDE0073.severity = warning + +# IDE0130: Namespace mismatch with folder +dotnet_diagnostic.IDE0130.severity = warning + +# IDE1006: Naming style +dotnet_diagnostic.IDE1006.severity = warning + +#Disable operator overloads requiring alternate named methods +dotnet_diagnostic.CA2225.severity = none + +# Banned APIs +dotnet_diagnostic.RS0030.severity = error diff --git a/Directory.Build.props b/Directory.Build.props index 709545bf1d..f3ddc68838 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,6 +20,7 @@ + true $(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset diff --git a/osu.sln b/osu.sln index b5018db362..aeec0843be 100644 --- a/osu.sln +++ b/osu.sln @@ -56,6 +56,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{10DF8F12-50FD-45D8-8A38-17BA764BF54D}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + .globalconfig = .globalconfig Directory.Build.props = Directory.Build.props osu.Android.props = osu.Android.props osu.iOS.props = osu.iOS.props From 7cf4dabe29961c7f9d74f88e5827269598ed45ce Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 2 May 2022 20:07:53 +0800 Subject: [PATCH 0695/1276] Fix IDE0005 and IDE0034 --- osu.Game/Migrations/20181007180454_StandardizePaths.cs | 4 +--- .../Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs | 2 +- .../Overlays/Profile/Header/Components/LevelProgressBar.cs | 2 +- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 2 +- osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs | 3 +-- .../Edit/Compose/Components/SelectionBoxRotationHandle.cs | 2 +- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.cs index 274b8030a9..11a020eb9c 100644 --- a/osu.Game/Migrations/20181007180454_StandardizePaths.cs +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.cs @@ -1,6 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using System.IO; +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 9ee002fd9d..f528f54cdf 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -119,7 +119,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x"); ppColumn.Alpha = value.BeatmapInfo.Status.GrantsPerformancePoints() ? 1 : 0; - ppColumn.Text = value.PP?.ToLocalisableString(@"N0") ?? default(LocalisableString); + ppColumn.Text = value.PP?.ToLocalisableString(@"N0") ?? default; statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(createStatisticsColumn); modsColumn.Mods = value.Mods; diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index ec9cb55042..d8eb5b65ac 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void updateProgress(APIUser user) { levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0; - levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'") ?? default(LocalisableString); + levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'") ?? default; } } } diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 48a4c31f30..c05c160463 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Rankings startDateColumn.Value = dateToString(response.Spotlight.StartDate); endDateColumn.Value = dateToString(response.Spotlight.EndDate); mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString(@"N0"); - participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString(@"N0") ?? default(LocalisableString); + participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString(@"N0") ?? default; } private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString(@"yyyy-MM-dd"); diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index bdbd2942d1..6c85ec2753 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; @@ -25,7 +24,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new RowText { Text = item.PP?.ToLocalisableString(@"N0") ?? default(LocalisableString), } + new RowText { Text = item.PP?.ToLocalisableString(@"N0") ?? default, } }; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 22479bd9b3..f13ed0456a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updateTooltipText() { - TooltipText = cumulativeRotation.Value?.ToLocalisableString("0.0°") ?? default(LocalisableString); + TooltipText = cumulativeRotation.Value?.ToLocalisableString("0.0°") ?? default; } } } From 5513710b2efbe9684cbf850ea7ef8c8ce04dcb38 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 2 May 2022 21:51:28 +0800 Subject: [PATCH 0696/1276] Fix IDE0055 --- .../CatchSelectionBlueprintTestScene.cs | 5 +- .../TestSceneManageCollectionsDialog.cs | 3 +- .../OnlinePlay/Components/DrawableGameType.cs | 76 ++++++++++--------- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs index e345e03c96..88fd3b36ba 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs @@ -29,13 +29,14 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor protected CatchSelectionBlueprintTestScene() { - EditorBeatmap = new EditorBeatmap(new CatchBeatmap + var catchBeatmap = new CatchBeatmap { BeatmapInfo = { Ruleset = new CatchRuleset().RulesetInfo, } - }) { Difficulty = { CircleSize = 0 } }; + }; + EditorBeatmap = new EditorBeatmap(catchBeatmap) { Difficulty = { CircleSize = 0 } }; EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 100 diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index 51ca55f37f..d5983ac827 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -141,7 +141,8 @@ namespace osu.Game.Tests.Visual.Collections { AddStep("add dropdown", () => { - Add(new CollectionFilterDropdown + Add( + new CollectionFilterDropdown { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, diff --git a/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs b/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs index 613f16563c..f360a80599 100644 --- a/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs +++ b/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs @@ -87,44 +87,46 @@ namespace osu.Game.Screens.OnlinePlay.Components }, }; - // case MatchType.TagCoop: - // return new SpriteIcon - // { - // Anchor = Anchor.Centre, - // Origin = Anchor.Centre, - // Size = new Vector2(size), - // Icon = FontAwesome.Solid.Sync, - // Colour = colours.Blue, - // - // Shadow = false - // }; +#pragma warning disable IDE0055 // Indentation of commented code + // case MatchType.TagCoop: + // return new SpriteIcon + // { + // Anchor = Anchor.Centre, + // Origin = Anchor.Centre, + // Size = new Vector2(size), + // Icon = FontAwesome.Solid.Sync, + // Colour = colours.Blue, + // + // Shadow = false + // }; - // case MatchType.TagTeamCoop: - // return new FillFlowContainer - // { - // Anchor = Anchor.Centre, - // Origin = Anchor.Centre, - // AutoSizeAxes = Axes.Both, - // Direction = FillDirection.Horizontal, - // Spacing = new Vector2(2f), - // Children = new[] - // { - // new SpriteIcon - // { - // Icon = FontAwesome.Solid.Sync, - // Size = new Vector2(size * 0.75f), - // Colour = colours.Blue, - // Shadow = false, - // }, - // new SpriteIcon - // { - // Icon = FontAwesome.Solid.Sync, - // Size = new Vector2(size * 0.75f), - // Colour = colours.Pink, - // Shadow = false, - // }, - // }, - // }; + // case MatchType.TagTeamCoop: + // return new FillFlowContainer + // { + // Anchor = Anchor.Centre, + // Origin = Anchor.Centre, + // AutoSizeAxes = Axes.Both, + // Direction = FillDirection.Horizontal, + // Spacing = new Vector2(2f), + // Children = new[] + // { + // new SpriteIcon + // { + // Icon = FontAwesome.Solid.Sync, + // Size = new Vector2(size * 0.75f), + // Colour = colours.Blue, + // Shadow = false, + // }, + // new SpriteIcon + // { + // Icon = FontAwesome.Solid.Sync, + // Size = new Vector2(size * 0.75f), + // Colour = colours.Pink, + // Shadow = false, + // }, + // }, + // }; +#pragma warning restore IDE0055 } } From 4a56297c1c187083878aa7c9e8b83c9fedf33cee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 May 2022 14:28:58 +0900 Subject: [PATCH 0697/1276] Add localisation and improve visuals of placeholder text --- .../UserInterface/ShearedSearchTextBox.cs | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index 4076c4f76b..5dda8598bc 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -11,8 +11,8 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Mods; +using osu.Game.Resources.Localisation.Web; using osuTK; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface { @@ -132,14 +132,29 @@ namespace osu.Game.Graphics.UserInterface { BackgroundFocused = colourProvider.Background4; BackgroundUnfocused = colourProvider.Background4; - Placeholder.Colour = Color4.White; - PlaceholderText = @"Search"; + + Placeholder.Font = OsuFont.GetFont(size: CalculatedTextSize, weight: FontWeight.SemiBold); + PlaceholderText = CommonStrings.InputSearch; } - protected override SpriteText CreatePlaceholder() => new OsuSpriteText + protected override SpriteText CreatePlaceholder() => new SearchPlaceholder(); + + internal class SearchPlaceholder : SpriteText { - Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold) - }; + public override void Show() + { + this + .MoveToY(0, 250, Easing.OutQuint) + .FadeIn(250, Easing.OutQuint); + } + + public override void Hide() + { + this + .MoveToY(3, 250, Easing.OutQuint) + .FadeOut(250, Easing.OutQuint); + } + } protected override Drawable GetDrawableCharacter(char c) => new FallingDownContainer { From 8e0235392f9df3fed2007cbb69f94fad0990be17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 May 2022 14:30:38 +0900 Subject: [PATCH 0698/1276] Inline single-use constants --- .../UserInterface/ShearedSearchTextBox.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index 5dda8598bc..639331196d 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -18,10 +18,8 @@ namespace osu.Game.Graphics.UserInterface { public class ShearedSearchTextBox : CompositeDrawable, IHasCurrentValue { - private const float icon_container_width = 50; private const float corner_radius = 7; - private const float height = 42; - private readonly Vector2 shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); + private readonly Box background; private readonly Box searchBoxBackground; private readonly SearchTextBox textBox; @@ -44,8 +42,8 @@ namespace osu.Game.Graphics.UserInterface public ShearedSearchTextBox() { - Height = height; - Shear = shear; + Height = 42; + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); Masking = true; CornerRadius = corner_radius; InternalChild = new Container @@ -78,13 +76,13 @@ namespace osu.Game.Graphics.UserInterface }, textBox = new InnerSearchTextBox { - Shear = -shear, + Shear = -new Vector2(ShearedOverlayContainer.SHEAR, 0), Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.X, Padding = new MarginPadding { - Horizontal = corner_radius + shear.X + Horizontal = corner_radius + new Vector2(ShearedOverlayContainer.SHEAR, 0).X } } } @@ -93,7 +91,7 @@ namespace osu.Game.Graphics.UserInterface { Name = @"Icon container", RelativeSizeAxes = Axes.Y, - Width = icon_container_width, + Width = 50, Origin = Anchor.CentreRight, Anchor = Anchor.CentreRight, Children = new Drawable[] @@ -104,13 +102,17 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.Centre, Anchor = Anchor.Centre, Size = new Vector2(16), - Shear = -shear + Shear = -new Vector2(ShearedOverlayContainer.SHEAR, 0) } } } } }, - ColumnDimensions = new[] { new Dimension(), new Dimension(GridSizeMode.AutoSize) } + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize) + } } } }; From bc88c4ee8e201f3734d9b60a725e77978e002cd7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 May 2022 14:34:18 +0900 Subject: [PATCH 0699/1276] Reduce container nesting by one level --- .../UserInterface/ShearedSearchTextBox.cs | 103 +++++++++--------- 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index 639331196d..8b49f1d90e 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -46,73 +46,70 @@ namespace osu.Game.Graphics.UserInterface Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); Masking = true; CornerRadius = corner_radius; - InternalChild = new Container + + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + background = new Box { - background = new Box + RelativeSizeAxes = Axes.Both + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] { - RelativeSizeAxes = Axes.Both - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] + new Drawable[] { - new Drawable[] + new Container { - new Container + Name = @"Search box container", + RelativeSizeAxes = Axes.Both, + CornerRadius = corner_radius, + Masking = true, + Children = new Drawable[] { - Name = @"Search box container", - RelativeSizeAxes = Axes.Both, - CornerRadius = corner_radius, - Masking = true, - Children = new Drawable[] + searchBoxBackground = new Box { - searchBoxBackground = new Box - { - RelativeSizeAxes = Axes.Both - }, - textBox = new InnerSearchTextBox - { - Shear = -new Vector2(ShearedOverlayContainer.SHEAR, 0), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - Padding = new MarginPadding - { - Horizontal = corner_radius + new Vector2(ShearedOverlayContainer.SHEAR, 0).X - } - } - } - }, - new Container - { - Name = @"Icon container", - RelativeSizeAxes = Axes.Y, - Width = 50, - Origin = Anchor.CentreRight, - Anchor = Anchor.CentreRight, - Children = new Drawable[] + RelativeSizeAxes = Axes.Both + }, + textBox = new InnerSearchTextBox { - new SpriteIcon + Shear = -Shear, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + Padding = new MarginPadding { - Icon = FontAwesome.Solid.Search, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(16), - Shear = -new Vector2(ShearedOverlayContainer.SHEAR, 0) + Horizontal = corner_radius + Shear.X } } } + }, + new Container + { + Name = @"Icon container", + RelativeSizeAxes = Axes.Y, + Width = 50, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.Solid.Search, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(16), + Shear = -Shear + } + } } - }, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.AutoSize) } + }, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize) } } }; From b5ea26f2fa7b4f2c99c733f49111c16b8704a2be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 May 2022 14:45:17 +0900 Subject: [PATCH 0700/1276] Fix incorrect shear masking on nested textbox --- .../UserInterface/ShearedSearchTextBox.cs | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index 8b49f1d90e..d16ae2498a 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -21,7 +21,6 @@ namespace osu.Game.Graphics.UserInterface private const float corner_radius = 7; private readonly Box background; - private readonly Box searchBoxBackground; private readonly SearchTextBox textBox; public Bindable Current @@ -60,30 +59,12 @@ namespace osu.Game.Graphics.UserInterface { new Drawable[] { - new Container + textBox = new InnerSearchTextBox { - Name = @"Search box container", + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.Both, - CornerRadius = corner_radius, - Masking = true, - Children = new Drawable[] - { - searchBoxBackground = new Box - { - RelativeSizeAxes = Axes.Both - }, - textBox = new InnerSearchTextBox - { - Shear = -Shear, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - Padding = new MarginPadding - { - Horizontal = corner_radius + Shear.X - } - } - } + Size = Vector2.One }, new Container { @@ -119,7 +100,6 @@ namespace osu.Game.Graphics.UserInterface private void load(OverlayColourProvider colourProvider) { background.Colour = colourProvider.Background3; - searchBoxBackground.Colour = colourProvider.Background4; } public override bool HandleNonPositionalInput => textBox.HandleNonPositionalInput; @@ -134,6 +114,9 @@ namespace osu.Game.Graphics.UserInterface Placeholder.Font = OsuFont.GetFont(size: CalculatedTextSize, weight: FontWeight.SemiBold); PlaceholderText = CommonStrings.InputSearch; + + CornerRadius = corner_radius; + TextContainer.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0); } protected override SpriteText CreatePlaceholder() => new SearchPlaceholder(); From 983eed32fc8f328837cb50428b621a0ac1b93385 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 May 2022 14:48:00 +0900 Subject: [PATCH 0701/1276] Reduce complexity of icon container nesting --- .../UserInterface/ShearedSearchTextBox.cs | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index d16ae2498a..0c34a04a65 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -66,31 +66,20 @@ namespace osu.Game.Graphics.UserInterface RelativeSizeAxes = Axes.Both, Size = Vector2.One }, - new Container + new SpriteIcon { - Name = @"Icon container", - RelativeSizeAxes = Axes.Y, - Width = 50, - Origin = Anchor.CentreRight, - Anchor = Anchor.CentreRight, - Children = new Drawable[] - { - new SpriteIcon - { - Icon = FontAwesome.Solid.Search, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(16), - Shear = -Shear - } - } + Icon = FontAwesome.Solid.Search, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(16), + Shear = -Shear } } }, ColumnDimensions = new[] { new Dimension(), - new Dimension(GridSizeMode.AutoSize) + new Dimension(GridSizeMode.Absolute, 50), } } }; From a555c472127a9d900ac239b91ae82f1802c8f8d7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 3 May 2022 15:02:57 +0900 Subject: [PATCH 0702/1276] Reduce sorting complexity in worst-case of diffcalc --- osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index bbd2f079aa..d13d4ebf2e 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -65,6 +65,10 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// private void saveCurrentPeak() { + // Ignore sections with 0 strain to avoid edge cases of long maps with a lot of spacing between objects (e.g. /b/2351871). + if (currentSectionPeak == 0) + return; + strainPeaks.Add(currentSectionPeak); } From 547038f13b9840e263ce1bc6c0d57841acf8aeb6 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 3 May 2022 14:28:53 +0800 Subject: [PATCH 0703/1276] Align editorconfig with framework --- .editorconfig | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/.editorconfig b/.editorconfig index 840fa98334..35ac84fca0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,14 @@ # EditorConfig is awesome: http://editorconfig.org root = true +[*.{csproj,props,targets}] +charset = utf-8-bom +end_of_line = crlf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + [*.cs] end_of_line = crlf insert_final_newline = true @@ -8,8 +16,19 @@ indent_style = space indent_size = 4 trim_trailing_whitespace = true +#license header +file_header_template = Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\nSee the LICENCE file in the repository root for full licence text. + #Roslyn naming styles +#PascalCase for public and protected members +dotnet_naming_style.pascalcase.capitalization = pascal_case +dotnet_naming_symbols.public_members.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event +dotnet_naming_rule.public_members_pascalcase.severity = error +dotnet_naming_rule.public_members_pascalcase.symbols = public_members +dotnet_naming_rule.public_members_pascalcase.style = pascalcase + #camelCase for private members dotnet_naming_style.camelcase.capitalization = camel_case @@ -157,7 +176,7 @@ csharp_style_unused_value_assignment_preference = discard_variable:warning #Style - variable declaration csharp_style_inlined_variable_declaration = true:warning -csharp_style_deconstructed_variable_declaration = false:silent +csharp_style_deconstructed_variable_declaration = true:warning #Style - other C# 7.x features dotnet_style_prefer_inferred_tuple_names = true:warning @@ -168,28 +187,15 @@ dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent #Style - C# 8 features csharp_prefer_static_local_function = true:warning csharp_prefer_simple_using_statement = true:silent -csharp_style_prefer_index_operator = false:silent -csharp_style_prefer_range_operator = false:silent +csharp_style_prefer_index_operator = true:warning +csharp_style_prefer_range_operator = true:warning csharp_style_prefer_switch_expression = false:none -#Supressing roslyn built-in analyzers -# Suppress: EC112 - -#Private method is unused -dotnet_diagnostic.IDE0051.severity = silent -#Private member is unused -dotnet_diagnostic.IDE0052.severity = silent - -#Rules for disposable -dotnet_diagnostic.IDE0067.severity = none -dotnet_diagnostic.IDE0068.severity = none -dotnet_diagnostic.IDE0069.severity = none - -#Disable operator overloads requiring alternate named methods -dotnet_diagnostic.CA2225.severity = none - -# Banned APIs -dotnet_diagnostic.RS0030.severity = error +[*.{yaml,yml}] +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true dotnet_diagnostic.OLOC001.words_in_name = 5 dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. From 1202c29ea1aa4318a111e196f583c430700ff951 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 3 May 2022 14:33:14 +0800 Subject: [PATCH 0704/1276] Add license headers to EF migration files --- osu.Game/Migrations/20171019041408_InitialCreate.cs | 5 ++++- osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs | 5 ++++- .../20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs | 5 ++++- .../Migrations/20171209034410_AddRulesetInfoShortName.cs | 5 ++++- osu.Game/Migrations/20180125143340_Settings.cs | 5 ++++- osu.Game/Migrations/20180131154205_AddMuteBinding.cs | 5 ++++- osu.Game/Migrations/20180219060912_AddSkins.cs | 5 ++++- .../Migrations/20180529055154_RemoveUniqueHashConstraints.cs | 5 ++++- .../Migrations/20180621044111_UpdateTaikoDefaultBindings.cs | 5 ++++- osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs | 5 ++++- osu.Game/Migrations/20180913080842_AddRankStatus.cs | 5 ++++- osu.Game/Migrations/20181007180454_StandardizePaths.cs | 5 ++++- osu.Game/Migrations/20181128100659_AddSkinInfoHash.cs | 5 ++++- osu.Game/Migrations/20181130113755_AddScoreInfoTables.cs | 5 ++++- osu.Game/Migrations/20190225062029_AddUserIDColumn.cs | 5 ++++- osu.Game/Migrations/20190525060824_SkinSettings.cs | 5 ++++- .../20190605091246_AddDateAddedColumnToBeatmapSet.cs | 5 ++++- osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs | 5 ++++- osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs | 5 ++++- osu.Game/Migrations/20200302094919_RefreshVolumeBindings.cs | 5 ++++- osu.Game/Migrations/20201019224408_AddEpilepsyWarning.cs | 5 ++++- .../Migrations/20210412045700_RefreshVolumeBindingsAgain.cs | 5 ++++- .../Migrations/20210511060743_AddSkinInstantiationInfo.cs | 5 ++++- .../20210514062639_AddAuthorIdToBeatmapMetadata.cs | 5 ++++- osu.Game/Migrations/20210824185035_AddCountdownSettings.cs | 5 ++++- .../Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs | 5 ++++- 26 files changed, 104 insertions(+), 26 deletions(-) diff --git a/osu.Game/Migrations/20171019041408_InitialCreate.cs b/osu.Game/Migrations/20171019041408_InitialCreate.cs index 9b6881f98c..08ab64fd08 100644 --- a/osu.Game/Migrations/20171019041408_InitialCreate.cs +++ b/osu.Game/Migrations/20171019041408_InitialCreate.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs index c9fc59c5a2..4ec3952941 100644 --- a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs +++ b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs index 084ae67940..6aba12f86f 100644 --- a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs +++ b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs index 09cf0af89c..5688455f79 100644 --- a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs +++ b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20180125143340_Settings.cs b/osu.Game/Migrations/20180125143340_Settings.cs index 166d3c086d..1feb37531f 100644 --- a/osu.Game/Migrations/20180125143340_Settings.cs +++ b/osu.Game/Migrations/20180125143340_Settings.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20180131154205_AddMuteBinding.cs b/osu.Game/Migrations/20180131154205_AddMuteBinding.cs index 5564a30bbf..8646d1d76b 100644 --- a/osu.Game/Migrations/20180131154205_AddMuteBinding.cs +++ b/osu.Game/Migrations/20180131154205_AddMuteBinding.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Infrastructure; using osu.Game.Database; using osu.Game.Input.Bindings; diff --git a/osu.Game/Migrations/20180219060912_AddSkins.cs b/osu.Game/Migrations/20180219060912_AddSkins.cs index a0270ab0fd..319748bed6 100644 --- a/osu.Game/Migrations/20180219060912_AddSkins.cs +++ b/osu.Game/Migrations/20180219060912_AddSkins.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs index 27269cc5fc..91eabe8868 100644 --- a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs +++ b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs index 71304ea979..d888ccd5a2 100644 --- a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs +++ b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs index 506d65f761..fdea636ac6 100644 --- a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs +++ b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20180913080842_AddRankStatus.cs b/osu.Game/Migrations/20180913080842_AddRankStatus.cs index bba4944bb7..bb147dff84 100644 --- a/osu.Game/Migrations/20180913080842_AddRankStatus.cs +++ b/osu.Game/Migrations/20180913080842_AddRankStatus.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.cs index 11a020eb9c..30f27043a0 100644 --- a/osu.Game/Migrations/20181007180454_StandardizePaths.cs +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20181128100659_AddSkinInfoHash.cs b/osu.Game/Migrations/20181128100659_AddSkinInfoHash.cs index 860264a7dd..ee825a1e9c 100644 --- a/osu.Game/Migrations/20181128100659_AddSkinInfoHash.cs +++ b/osu.Game/Migrations/20181128100659_AddSkinInfoHash.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20181130113755_AddScoreInfoTables.cs b/osu.Game/Migrations/20181130113755_AddScoreInfoTables.cs index 2b6f94c5a4..58980132f3 100644 --- a/osu.Game/Migrations/20181130113755_AddScoreInfoTables.cs +++ b/osu.Game/Migrations/20181130113755_AddScoreInfoTables.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations diff --git a/osu.Game/Migrations/20190225062029_AddUserIDColumn.cs b/osu.Game/Migrations/20190225062029_AddUserIDColumn.cs index 0720e0eac7..f2eef600dc 100644 --- a/osu.Game/Migrations/20190225062029_AddUserIDColumn.cs +++ b/osu.Game/Migrations/20190225062029_AddUserIDColumn.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20190525060824_SkinSettings.cs b/osu.Game/Migrations/20190525060824_SkinSettings.cs index 99237419b7..7779b55bb7 100644 --- a/osu.Game/Migrations/20190525060824_SkinSettings.cs +++ b/osu.Game/Migrations/20190525060824_SkinSettings.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs index 55dc18b6a3..0620a0624f 100644 --- a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs +++ b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations diff --git a/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs index f5963ebf5e..f8ce354aa1 100644 --- a/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs +++ b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs index 9ed0943acd..af82b4db20 100644 --- a/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs +++ b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20200302094919_RefreshVolumeBindings.cs b/osu.Game/Migrations/20200302094919_RefreshVolumeBindings.cs index ec4475971c..3d2ddbf6fc 100644 --- a/osu.Game/Migrations/20200302094919_RefreshVolumeBindings.cs +++ b/osu.Game/Migrations/20200302094919_RefreshVolumeBindings.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20201019224408_AddEpilepsyWarning.cs b/osu.Game/Migrations/20201019224408_AddEpilepsyWarning.cs index be6968aa5d..58a35a7bf3 100644 --- a/osu.Game/Migrations/20201019224408_AddEpilepsyWarning.cs +++ b/osu.Game/Migrations/20201019224408_AddEpilepsyWarning.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs index 155d6670a8..4d3941dd20 100644 --- a/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs +++ b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs index 1d5b0769a4..887635fa85 100644 --- a/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs +++ b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs b/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs index 98fe9b5e13..7b579e27b9 100644 --- a/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs +++ b/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20210824185035_AddCountdownSettings.cs b/osu.Game/Migrations/20210824185035_AddCountdownSettings.cs index 564f5f4520..d1b09e2c1d 100644 --- a/osu.Game/Migrations/20210824185035_AddCountdownSettings.cs +++ b/osu.Game/Migrations/20210824185035_AddCountdownSettings.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { diff --git a/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs b/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs index bf3f855d5f..f6fc1f4420 100644 --- a/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs +++ b/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore.Migrations; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Migrations; namespace osu.Game.Migrations { From 2a388ba3eaba2df84a1f5b5a6c7e3d22149f1a7e Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 3 May 2022 14:34:54 +0800 Subject: [PATCH 0705/1276] Turn IDE0052 to silent according to the occurrences --- .globalconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.globalconfig b/.globalconfig index 607798492c..462dbc74ed 100644 --- a/.globalconfig +++ b/.globalconfig @@ -37,7 +37,7 @@ dotnet_diagnostic.IDE0055.severity = warning dotnet_diagnostic.IDE0051.severity = silent # IDE0052: Private member is unused -dotnet_diagnostic.IDE0052.severity = warning +dotnet_diagnostic.IDE0052.severity = silent # IDE0073: File header dotnet_diagnostic.IDE0073.severity = warning From 2896612c5cd09cc4c6436f2f307d60218b13b8f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 May 2022 16:06:04 +0900 Subject: [PATCH 0706/1276] Make exiting multiplayer a dangerous operation, requiring hold --- .../Containers/HoldToConfirmContainer.cs | 8 ++++-- .../Dialog/PopupDialogDangerousButton.cs | 4 ++- .../Screens/Play/HUD/HoldForMenuButton.cs | 27 ++++++++++++++----- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index 1b802a0a14..c74245461d 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -40,9 +40,13 @@ namespace osu.Game.Graphics.Containers private Bindable holdActivationDelay; - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + [Resolved] + private OsuConfigManager config { get; set; } + + protected override void LoadComplete() { + base.LoadComplete(); + holdActivationDelay = HoldActivationDelay != null ? new Bindable(HoldActivationDelay.Value) : config.GetBindable(OsuSetting.UIHoldActivationDelay); diff --git a/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs b/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs index adc627e15b..6c775f44f8 100644 --- a/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs +++ b/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs @@ -12,6 +12,8 @@ namespace osu.Game.Overlays.Dialog { public class PopupDialogDangerousButton : PopupDialogButton { + public const double DANGEROUS_HOLD_ACTIVATION_DELAY = 500; + private Box progressBox; private DangerousConfirmContainer confirmContainer; @@ -42,7 +44,7 @@ namespace osu.Game.Overlays.Dialog private class DangerousConfirmContainer : HoldToConfirmContainer { - protected override double? HoldActivationDelay => 500; + protected override double? HoldActivationDelay => DANGEROUS_HOLD_ACTIVATION_DELAY; protected override bool OnMouseDown(MouseDownEvent e) { diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 3da63ec2cc..c6aa3fbe08 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -18,6 +18,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; +using osu.Game.Overlays.Dialog; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -37,6 +38,14 @@ namespace osu.Game.Screens.Play.HUD private readonly OsuSpriteText text; + [Resolved] + private OsuConfigManager config { get; set; } + + [Resolved(canBeNull: true)] + private Player player { get; set; } + + private readonly Bindable activationDelay = new Bindable(); + public HoldForMenuButton() { Direction = FillDirection.Horizontal; @@ -60,14 +69,15 @@ namespace osu.Game.Screens.Play.HUD AutoSizeAxes = Axes.Both; } - [Resolved] - private OsuConfigManager config { get; set; } - - private Bindable activationDelay; - protected override void LoadComplete() { - activationDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); + if (player?.Configuration.AllowRestart == false) + { + activationDelay.Value = PopupDialogDangerousButton.DANGEROUS_HOLD_ACTIVATION_DELAY; + } + else + config.BindWith(OsuSetting.UIHoldActivationDelay, activationDelay); + activationDelay.BindValueChanged(v => { text.Text = v.NewValue > 0 @@ -115,6 +125,11 @@ namespace osu.Game.Screens.Play.HUD public Action HoverGained; public Action HoverLost; + [Resolved(canBeNull: true)] + private Player player { get; set; } + + protected override double? HoldActivationDelay => player?.Configuration.AllowRestart == false ? PopupDialogDangerousButton.DANGEROUS_HOLD_ACTIVATION_DELAY : (double?)null; + [BackgroundDependencyLoader] private void load(OsuColour colours) { From 637f81769609d8d56223d2d70263b57d6617444a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 3 May 2022 16:06:20 +0900 Subject: [PATCH 0707/1276] Ignore zero-sections on a per-case basis --- .../Difficulty/Skills/OsuStrainSkill.cs | 6 +++++- .../Difficulty/TaikoDifficultyCalculator.cs | 8 +++++++- osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs | 10 +++++----- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index e47edc37cc..dcd2c7d321 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -38,7 +38,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double difficulty = 0; double weight = 1; - List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). + // These sections will not contribute to the difficulty. + var peaks = GetCurrentStrainPeaks().Where(p => p > 0); + + List strains = peaks.OrderByDescending(d => d).ToList(); // We are reducing the highest strains first to account for extreme difficulty spikes for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 6afdef3f3c..a9d512f076 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -141,7 +141,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty double colourPeak = colourPeaks[i] * colour_skill_multiplier; double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier; double staminaPeak = (staminaRightPeaks[i] + staminaLeftPeaks[i]) * stamina_skill_multiplier * staminaPenalty; - peaks.Add(norm(2, colourPeak, rhythmPeak, staminaPeak)); + + double peak = norm(2, colourPeak, rhythmPeak, staminaPeak); + + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). + // These sections will not contribute to the difficulty. + if (peak > 0) + peaks.Add(peak); } double difficulty = 0; diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index d13d4ebf2e..97266562e4 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -65,10 +65,6 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// private void saveCurrentPeak() { - // Ignore sections with 0 strain to avoid edge cases of long maps with a lot of spacing between objects (e.g. /b/2351871). - if (currentSectionPeak == 0) - return; - strainPeaks.Add(currentSectionPeak); } @@ -104,9 +100,13 @@ namespace osu.Game.Rulesets.Difficulty.Skills double difficulty = 0; double weight = 1; + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). + // These sections will not contribute to the difficulty. + var peaks = GetCurrentStrainPeaks().Where(p => p > 0); + // Difficulty is the weighted sum of the highest strains from every section. // We're sorting from highest to lowest strain. - foreach (double strain in GetCurrentStrainPeaks().OrderByDescending(d => d)) + foreach (double strain in peaks.OrderByDescending(d => d)) { difficulty += strain * weight; weight *= DecayWeight; From 521ec1a225f72931941c1ab23714954aa646ec93 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 3 May 2022 04:44:55 +0300 Subject: [PATCH 0708/1276] Add keybind for distance grid spacing activation --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 4 ++++ .../Localisation/GlobalActionKeyBindingStrings.cs | 5 +++++ .../Rulesets/Edit/DistancedHitObjectComposer.cs | 14 ++++++++------ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 47cb7be2cf..5656c79975 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -79,6 +79,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.F5 }, GlobalAction.EditorTestGameplay), new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.EditorFlipHorizontally), new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically), + new KeyBinding(new[] { InputKey.Control, InputKey.Alt }, GlobalAction.EditorDistanceSpacing), }; public IEnumerable InGameKeyBindings => new[] @@ -301,5 +302,8 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorFlipVertically))] EditorFlipVertically, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorDistanceSpacing))] + EditorDistanceSpacing, } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 777e97d1e3..73cc7546ab 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -239,6 +239,11 @@ namespace osu.Game.Localisation /// public static LocalisableString EditorFlipVertically => new TranslatableString(getKey(@"editor_flip_vertically"), @"Flip selection vertically"); + /// + /// "Distance grid spacing (hold)" + /// + public static LocalisableString EditorDistanceSpacing => new TranslatableString(getKey(@"editor_distance_spacing"), @"Distance grid spacing (hold)"); + /// /// "Toggle skin editor" /// diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 0505f9ab0e..1a4ea845d0 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -4,9 +4,11 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Overlays.Settings.Sections; using osu.Game.Rulesets.Objects; using osuTK; @@ -18,7 +20,7 @@ namespace osu.Game.Rulesets.Edit /// /// The base type of supported objects. [Cached(typeof(IDistanceSnapProvider))] - public abstract class DistancedHitObjectComposer : HitObjectComposer, IDistanceSnapProvider + public abstract class DistancedHitObjectComposer : HitObjectComposer, IDistanceSnapProvider, IKeyBindingHandler where TObject : HitObject { protected Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1.0) @@ -75,21 +77,21 @@ namespace osu.Game.Rulesets.Edit } } - protected override bool OnKeyDown(KeyDownEvent e) + public bool OnPressed(KeyBindingPressEvent e) { - if (!DistanceSpacingMultiplier.Disabled && e.ControlPressed && e.AltPressed && !e.Repeat) + if (!DistanceSpacingMultiplier.Disabled && e.Action == GlobalAction.EditorDistanceSpacing) { RightSideToolboxContainer.Expanded.Value = true; distanceSpacingScrollActive = true; return true; } - return base.OnKeyDown(e); + return false; } - protected override void OnKeyUp(KeyUpEvent e) + public void OnReleased(KeyBindingReleaseEvent e) { - if (!DistanceSpacingMultiplier.Disabled && distanceSpacingScrollActive && (!e.AltPressed || !e.ControlPressed)) + if (!DistanceSpacingMultiplier.Disabled && e.Action == GlobalAction.EditorDistanceSpacing) { RightSideToolboxContainer.Expanded.Value = false; distanceSpacingScrollActive = false; From b8287f3687e06c88b6a565994cf23233f285d2e2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 3 May 2022 10:30:32 +0300 Subject: [PATCH 0709/1276] Display toast notification on editor distance spacing change --- .../Edit/DistancedHitObjectComposer.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 1a4ea845d0..a19944712e 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -3,12 +3,16 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Overlays; +using osu.Game.Overlays.OSD; using osu.Game.Overlays.Settings.Sections; using osu.Game.Rulesets.Objects; using osuTK; @@ -37,6 +41,9 @@ namespace osu.Game.Rulesets.Edit private ExpandableSlider> distanceSpacingSlider; private bool distanceSpacingScrollActive; + [Resolved] + private OnScreenDisplay onScreenDisplay { get; set; } + protected DistancedHitObjectComposer(Ruleset ruleset) : base(ruleset) { @@ -72,6 +79,10 @@ namespace osu.Game.Rulesets.Edit { distanceSpacingSlider.ContractedLabelText = $"D. S. ({v.NewValue:0.##x})"; distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; + + if (v.NewValue != v.OldValue) + onScreenDisplay.Display(new DistanceSpacingToast(v.NewValue.ToLocalisableString(@"0.##x"))); + EditorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; }, true); } @@ -81,7 +92,6 @@ namespace osu.Game.Rulesets.Edit { if (!DistanceSpacingMultiplier.Disabled && e.Action == GlobalAction.EditorDistanceSpacing) { - RightSideToolboxContainer.Expanded.Value = true; distanceSpacingScrollActive = true; return true; } @@ -92,10 +102,7 @@ namespace osu.Game.Rulesets.Edit public void OnReleased(KeyBindingReleaseEvent e) { if (!DistanceSpacingMultiplier.Disabled && e.Action == GlobalAction.EditorDistanceSpacing) - { - RightSideToolboxContainer.Expanded.Value = false; distanceSpacingScrollActive = false; - } } protected override bool OnScroll(ScrollEvent e) @@ -160,5 +167,13 @@ namespace osu.Game.Rulesets.Edit FillFlow.Spacing = new Vector2(10); } } + + private class DistanceSpacingToast : Toast + { + public DistanceSpacingToast(LocalisableString value) + : base("Distance Spacing", value, string.Empty) + { + } + } } } From 0dd2e1652c2a040e6bd831c2cbdab12d22f33210 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 3 May 2022 11:15:28 +0300 Subject: [PATCH 0710/1276] Mark `OnScreenDisplay` dependency as nullable --- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index a19944712e..75b87fb6e8 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Edit private ExpandableSlider> distanceSpacingSlider; private bool distanceSpacingScrollActive; - [Resolved] + [Resolved(canBeNull: true)] private OnScreenDisplay onScreenDisplay { get; set; } protected DistancedHitObjectComposer(Ruleset ruleset) @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Edit distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; if (v.NewValue != v.OldValue) - onScreenDisplay.Display(new DistanceSpacingToast(v.NewValue.ToLocalisableString(@"0.##x"))); + onScreenDisplay?.Display(new DistanceSpacingToast(v.NewValue.ToLocalisableString(@"0.##x"))); EditorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; }, true); From 89d8ed8e20b2850cb96af75fd0d5b69adf575c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 20:26:16 +0200 Subject: [PATCH 0711/1276] Port existing test coverage --- .../UserInterface/TestSceneModSelectScreen.cs | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index ec6e962c6a..bd63023efb 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -2,17 +2,21 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osuTK.Input; @@ -169,6 +173,206 @@ namespace osu.Game.Tests.Visual.UserInterface assertCustomisationToggleState(disabled: true, active: false); // config was dismissed without explicit user action. } + /// + /// Ensure that two mod overlays are not cross polluting via central settings instances. + /// + [Test] + public void TestSettingsNotCrossPolluting() + { + Bindable> selectedMods2 = null; + ModSelectScreen modSelectScreen2 = null; + + createScreen(); + AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() }); + + AddStep("set setting", () => modSelectScreen.ChildrenOfType>().First().Current.Value = 8); + + AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); + + AddStep("create second bindable", () => selectedMods2 = new Bindable>(new Mod[] { new OsuModDifficultyAdjust() })); + + AddStep("create second overlay", () => + { + Add(modSelectScreen2 = new UserModSelectScreen().With(d => + { + d.Origin = Anchor.TopCentre; + d.Anchor = Anchor.TopCentre; + d.SelectedMods.BindTarget = selectedMods2; + })); + }); + + AddStep("show", () => modSelectScreen2.Show()); + + AddAssert("ensure first is unchanged", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); + AddAssert("ensure second is default", () => selectedMods2.Value.OfType().Single().CircleSize.Value == null); + } + + [Test] + public void TestSettingsResetOnDeselection() + { + var osuModDoubleTime = new OsuModDoubleTime { SpeedChange = { Value = 1.2 } }; + + createScreen(); + changeRuleset(0); + + AddStep("set dt mod with custom rate", () => { SelectedMods.Value = new[] { osuModDoubleTime }; }); + + AddAssert("selected mod matches", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.Value == 1.2); + + AddStep("deselect", () => getPanelForMod(typeof(OsuModDoubleTime)).TriggerClick()); + AddAssert("selected mods empty", () => SelectedMods.Value.Count == 0); + + AddStep("reselect", () => getPanelForMod(typeof(OsuModDoubleTime)).TriggerClick()); + AddAssert("selected mod has default value", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.IsDefault == true); + } + + [Test] + public void TestAnimationFlushOnClose() + { + createScreen(); + changeRuleset(0); + + AddStep("Select all fun mods", () => + { + modSelectScreen.ChildrenOfType() + .Single(c => c.ModType == ModType.DifficultyIncrease) + .SelectAll(); + }); + + AddUntilStep("many mods selected", () => SelectedMods.Value.Count >= 5); + + AddStep("trigger deselect and close overlay", () => + { + modSelectScreen.ChildrenOfType() + .Single(c => c.ModType == ModType.DifficultyIncrease) + .DeselectAll(); + + modSelectScreen.Hide(); + }); + + AddAssert("all mods deselected", () => SelectedMods.Value.Count == 0); + } + + [Test] + public void TestRulesetChanges() + { + createScreen(); + changeRuleset(0); + + var noFailMod = new OsuRuleset().GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail); + + AddStep("set mods externally", () => { SelectedMods.Value = new[] { noFailMod }; }); + + changeRuleset(0); + + AddAssert("ensure mods still selected", () => SelectedMods.Value.SingleOrDefault(m => m is OsuModNoFail) != null); + + changeRuleset(3); + + AddAssert("ensure mods not selected", () => SelectedMods.Value.Count == 0); + + changeRuleset(0); + + AddAssert("ensure mods not selected", () => SelectedMods.Value.Count == 0); + } + + [Test] + public void TestExternallySetCustomizedMod() + { + createScreen(); + changeRuleset(0); + + AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }); + + AddAssert("ensure button is selected and customized accordingly", () => + { + var button = getPanelForMod(SelectedMods.Value.Single().GetType()); + return ((OsuModDoubleTime)button.Mod).SpeedChange.Value == 1.01; + }); + } + + [Test] + public void TestSettingsAreRetainedOnReload() + { + createScreen(); + changeRuleset(0); + + AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }); + AddAssert("setting remains", () => (SelectedMods.Value.SingleOrDefault() as OsuModDoubleTime)?.SpeedChange.Value == 1.01); + + createScreen(); + AddAssert("setting remains", () => (SelectedMods.Value.SingleOrDefault() as OsuModDoubleTime)?.SpeedChange.Value == 1.01); + } + + [Test] + public void TestExternallySetModIsReplacedByOverlayInstance() + { + Mod external = new OsuModDoubleTime(); + Mod overlayButtonMod = null; + + createScreen(); + changeRuleset(0); + + AddStep("set mod externally", () => { SelectedMods.Value = new[] { external }; }); + + AddAssert("ensure button is selected", () => + { + var button = getPanelForMod(SelectedMods.Value.Single().GetType()); + overlayButtonMod = button.Mod; + return button.Active.Value; + }); + + // Right now, when an external change occurs, the ModSelectOverlay will replace the global instance with its own + AddAssert("mod instance doesn't match", () => external != overlayButtonMod); + + AddAssert("one mod present in global selected", () => SelectedMods.Value.Count == 1); + AddAssert("globally selected matches button's mod instance", () => SelectedMods.Value.Contains(overlayButtonMod)); + AddAssert("globally selected doesn't contain original external change", () => !SelectedMods.Value.Contains(external)); + } + + [Test] + public void TestChangeIsValidChangesButtonVisibility() + { + createScreen(); + changeRuleset(0); + + AddAssert("double time visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).Any(panel => !panel.Filtered.Value)); + + AddStep("make double time invalid", () => modSelectScreen.IsValidMod = m => !(m is OsuModDoubleTime)); + AddUntilStep("double time not visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).All(panel => panel.Filtered.Value)); + AddAssert("nightcore still visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModNightcore).Any(panel => !panel.Filtered.Value)); + + AddStep("make double time valid again", () => modSelectScreen.IsValidMod = m => true); + AddUntilStep("double time visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).Any(panel => !panel.Filtered.Value)); + AddAssert("nightcore still visible", () => modSelectScreen.ChildrenOfType().Where(b => b.Mod is OsuModNightcore).Any(panel => !panel.Filtered.Value)); + } + + [Test] + public void TestChangeIsValidPreservesSelection() + { + createScreen(); + changeRuleset(0); + + AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); + AddAssert("DT + HD selected", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + + AddStep("make NF invalid", () => modSelectScreen.IsValidMod = m => !(m is ModNoFail)); + AddAssert("DT + HD still selected", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + } + + [Test] + public void TestUnimplementedModIsUnselectable() + { + var testRuleset = new TestUnimplementedModOsuRuleset(); + + createScreen(); + + AddStep("set ruleset", () => Ruleset.Value = testRuleset.RulesetInfo); + waitForColumnLoad(); + + AddAssert("unimplemented mod panel is filtered", () => getPanelForMod(typeof(TestUnimplementedMod)).Filtered.Value); + } + private void waitForColumnLoad() => AddUntilStep("all column content loaded", () => modSelectScreen.ChildrenOfType().Any() && modSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); @@ -188,5 +392,26 @@ namespace osu.Game.Tests.Visual.UserInterface private ModPanel getPanelForMod(Type modType) => modSelectScreen.ChildrenOfType().Single(panel => panel.Mod.GetType() == modType); + + private class TestUnimplementedMod : Mod + { + public override string Name => "Unimplemented mod"; + public override string Acronym => "UM"; + public override string Description => "A mod that is not implemented."; + public override double ScoreMultiplier => 1; + public override ModType Type => ModType.Conversion; + } + + private class TestUnimplementedModOsuRuleset : OsuRuleset + { + public override string ShortName => "unimplemented"; + + public override IEnumerable GetModsFor(ModType type) + { + if (type == ModType.Conversion) return base.GetModsFor(type).Concat(new[] { new TestUnimplementedMod() }); + + return base.GetModsFor(type); + } + } } } From 746a4a740316f6bd1fbdd17f8bcc1d63c3474e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 21:23:54 +0200 Subject: [PATCH 0712/1276] Fix mod column using wrong equality type --- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 018922c074..9e32602a85 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -296,7 +296,7 @@ namespace osu.Game.Overlays.Mods private void updateActiveState() { foreach (var panel in panelFlow) - panel.Active.Value = SelectedMods.Value.Contains(panel.Mod, EqualityComparer.Default); + panel.Active.Value = SelectedMods.Value.Any(selected => selected.GetType() == panel.Mod.GetType()); } #region Bulk select / deselect From fe59f4ae58ae848af1f6992d9c4b63f9f3d3a0e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 20:32:45 +0200 Subject: [PATCH 0713/1276] Fix multiselection operation not flushing on close --- osu.Game/Overlays/Mods/ModColumn.cs | 9 +++++++++ osu.Game/Overlays/Mods/ModSelectScreen.cs | 10 ++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 9e32602a85..c839305746 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -364,6 +364,15 @@ namespace osu.Game.Overlays.Mods pendingSelectionOperations.Enqueue(() => button.Active.Value = false); } + /// + /// Play out all remaining animations immediately to leave mods in a good (final) state. + /// + public void FlushAnimation() + { + while (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) + dequeuedAction(); + } + private class ToggleAllCheckbox : OsuCheckbox { private Color4 accentColour; diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index ffd6e9a52c..20c5b5a321 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -313,10 +313,12 @@ namespace osu.Game.Overlays.Mods { const float distance = 700; - columnFlow[i].Column - .TopLevelContent - .MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint) - .FadeOut(fade_out_duration, Easing.OutQuint); + var column = columnFlow[i].Column; + + column.FlushAnimation(); + column.TopLevelContent + .MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint) + .FadeOut(fade_out_duration, Easing.OutQuint); } } From 7c04bf5c53442b3fe2d0adf05928c674a378923c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 22:12:06 +0200 Subject: [PATCH 0714/1276] Refactor mod reference management to meet test expectations --- osu.Game/Overlays/Mods/ModColumn.cs | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index c839305746..c2273abf53 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; using System.Linq; @@ -10,6 +12,7 @@ using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -25,8 +28,6 @@ using osuTK; using osuTK.Graphics; using osuTK.Input; -#nullable enable - namespace osu.Game.Overlays.Mods { public class ModColumn : CompositeDrawable @@ -256,7 +257,9 @@ namespace osu.Game.Overlays.Mods private void updateMods() { - var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty()).ToList(); + var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty()) + .Select(m => m.DeepClone()) + .ToList(); if (newMods.SequenceEqual(panelFlow.Children.Select(p => p.Mod))) return; @@ -280,9 +283,16 @@ namespace osu.Game.Overlays.Mods panel.Active.BindValueChanged(_ => { updateToggleAllState(); - SelectedMods.Value = panel.Active.Value - ? SelectedMods.Value.Append(panel.Mod).ToArray() - : SelectedMods.Value.Except(new[] { panel.Mod }).ToArray(); + + var newSelectedMods = SelectedMods.Value; + + var matchingModInstance = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == panel.Mod.GetType()); + if (matchingModInstance != null && (matchingModInstance != panel.Mod || !panel.Active.Value)) + newSelectedMods = newSelectedMods.Except(matchingModInstance.Yield()).ToArray(); + if (panel.Active.Value) + newSelectedMods = newSelectedMods.Append(panel.Mod).ToArray(); + + SelectedMods.Value = newSelectedMods.ToArray(); }); } }, (cancellationTokenSource = new CancellationTokenSource()).Token); @@ -296,7 +306,12 @@ namespace osu.Game.Overlays.Mods private void updateActiveState() { foreach (var panel in panelFlow) - panel.Active.Value = SelectedMods.Value.Any(selected => selected.GetType() == panel.Mod.GetType()); + { + var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == panel.Mod.GetType()); + panel.Active.Value = matchingSelectedMod != null; + if (matchingSelectedMod != null) + panel.Mod.CopyFrom(matchingSelectedMod); + } } #region Bulk select / deselect From f91ee4b0422bd8b91585b4f0f9a689719a8b0f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 22:14:28 +0200 Subject: [PATCH 0715/1276] Reset panel mod instance settings to defaults on deselect --- osu.Game/Overlays/Mods/ModColumn.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index c2273abf53..d480382fcb 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -309,8 +309,10 @@ namespace osu.Game.Overlays.Mods { var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == panel.Mod.GetType()); panel.Active.Value = matchingSelectedMod != null; - if (matchingSelectedMod != null) + if (panel.Active.Value) panel.Mod.CopyFrom(matchingSelectedMod); + else + panel.Mod.ResetSettingsToDefaults(); } } From e3641213e1832c627def0b48aae6213f455e4b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 22:16:59 +0200 Subject: [PATCH 0716/1276] Always hide unimplemented mods on mod select screen --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 20c5b5a321..2f6eae0afb 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -203,7 +203,7 @@ namespace osu.Game.Overlays.Mods private void updateAvailableMods() { foreach (var column in columnFlow.Columns) - column.Filter = isValidMod; + column.Filter = m => m.HasImplementation && isValidMod.Invoke(m); } private void updateCustomisation(ValueChangedEvent> valueChangedEvent) diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs index c85a4fc38b..5a7a60b479 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.OnlinePlay public new Func IsValidMod { get => base.IsValidMod; - set => base.IsValidMod = m => m.HasImplementation && m.UserPlayable && value.Invoke(m); + set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m); } public FreeModSelectScreen() From 970361676b374995f5c768805e9d9097e23ec855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 2 May 2022 23:05:35 +0200 Subject: [PATCH 0717/1276] Fix setting items not creating if mods initially not empty --- .../Visual/UserInterface/TestSceneModSelectScreen.cs | 2 ++ osu.Game/Overlays/Mods/ModSettingsArea.cs | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index bd63023efb..7690029ba2 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -68,6 +68,7 @@ namespace osu.Game.Tests.Visual.UserInterface return Precision.AlmostEquals(multiplier, modSelectScreen.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); + AddAssert("setting items created", () => modSelectScreen.ChildrenOfType().Any()); } [Test] @@ -82,6 +83,7 @@ namespace osu.Game.Tests.Visual.UserInterface return Precision.AlmostEquals(multiplier, modSelectScreen.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); + AddAssert("setting items created", () => modSelectScreen.ChildrenOfType().Any()); } [Test] diff --git a/osu.Game/Overlays/Mods/ModSettingsArea.cs b/osu.Game/Overlays/Mods/ModSettingsArea.cs index be72c1e3e3..9c5f3b7f11 100644 --- a/osu.Game/Overlays/Mods/ModSettingsArea.cs +++ b/osu.Game/Overlays/Mods/ModSettingsArea.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -21,7 +22,7 @@ namespace osu.Game.Overlays.Mods { public class ModSettingsArea : CompositeDrawable { - public Bindable> SelectedMods { get; } = new Bindable>(); + public Bindable> SelectedMods { get; } = new Bindable>(Array.Empty()); public const float HEIGHT = 250; @@ -77,7 +78,7 @@ namespace osu.Game.Overlays.Mods protected override void LoadComplete() { base.LoadComplete(); - SelectedMods.BindValueChanged(_ => updateMods()); + SelectedMods.BindValueChanged(_ => updateMods(), true); } private void updateMods() From 216dfb7e9121b5e0f8b662d3f9039aeb7860fbcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 2 May 2022 23:29:30 +0200 Subject: [PATCH 0718/1276] Expand incompatibility test to cover logic more thoroughly --- .../Visual/UserInterface/TestSceneModSelectScreen.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 7690029ba2..0bd138275b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -104,17 +104,25 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("activate DT", () => getPanelForMod(typeof(OsuModDoubleTime)).TriggerClick()); AddAssert("DT active", () => SelectedMods.Value.Single().GetType() == typeof(OsuModDoubleTime)); + AddAssert("DT panel active", () => getPanelForMod(typeof(OsuModDoubleTime)).Active.Value); AddStep("activate NC", () => getPanelForMod(typeof(OsuModNightcore)).TriggerClick()); AddAssert("only NC active", () => SelectedMods.Value.Single().GetType() == typeof(OsuModNightcore)); + AddAssert("DT panel not active", () => !getPanelForMod(typeof(OsuModDoubleTime)).Active.Value); + AddAssert("NC panel active", () => getPanelForMod(typeof(OsuModNightcore)).Active.Value); AddStep("activate HR", () => getPanelForMod(typeof(OsuModHardRock)).TriggerClick()); AddAssert("NC+HR active", () => SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModNightcore)) && SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModHardRock))); + AddAssert("NC panel active", () => getPanelForMod(typeof(OsuModNightcore)).Active.Value); + AddAssert("HR panel active", () => getPanelForMod(typeof(OsuModHardRock)).Active.Value); AddStep("activate MR", () => getPanelForMod(typeof(OsuModMirror)).TriggerClick()); AddAssert("NC+MR active", () => SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModNightcore)) && SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModMirror))); + AddAssert("NC panel active", () => getPanelForMod(typeof(OsuModNightcore)).Active.Value); + AddAssert("HR panel not active", () => !getPanelForMod(typeof(OsuModHardRock)).Active.Value); + AddAssert("MR panel active", () => getPanelForMod(typeof(OsuModMirror)).Active.Value); } [Test] From f5fa41356e33a528039742f9909d45dff71e451e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 3 May 2022 21:44:44 +0200 Subject: [PATCH 0719/1276] Rewrite mod instance management again to pass tests --- .../UserInterface/TestSceneModSelectScreen.cs | 4 +- osu.Game/Overlays/Mods/ModColumn.cs | 129 ++++++++++++------ osu.Game/Overlays/Mods/ModSelectScreen.cs | 33 ++--- osu.Game/Overlays/Mods/UserModSelectScreen.cs | 7 +- 4 files changed, 111 insertions(+), 62 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 0bd138275b..bdb423a43c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -336,8 +336,8 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("mod instance doesn't match", () => external != overlayButtonMod); AddAssert("one mod present in global selected", () => SelectedMods.Value.Count == 1); - AddAssert("globally selected matches button's mod instance", () => SelectedMods.Value.Contains(overlayButtonMod)); - AddAssert("globally selected doesn't contain original external change", () => !SelectedMods.Value.Contains(external)); + AddAssert("globally selected matches button's mod instance", () => SelectedMods.Value.Any(mod => ReferenceEquals(mod, overlayButtonMod))); + AddAssert("globally selected doesn't contain original external change", () => !SelectedMods.Value.Any(mod => ReferenceEquals(mod, external))); } [Test] diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index d480382fcb..f9332dd28b 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -53,9 +53,22 @@ namespace osu.Game.Overlays.Mods } } - public Bindable> SelectedMods = new Bindable>(Array.Empty()); public Bindable Active = new BindableBool(true); + /// + /// List of mods marked as selected in this column. + /// + /// + /// Note that the mod instances returned by this property are owned solely by this column + /// (as in, they are locally-managed clones, to ensure proper isolation from any other external instances). + /// + public IReadOnlyList SelectedMods { get; private set; } = Array.Empty(); + + /// + /// Invoked when a mod panel has been selected interactively by the user. + /// + public event Action? SelectionChangedByUser; + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => base.ReceivePositionalInputAtSubTree(screenSpacePos) && Active.Value; protected virtual ModPanel CreateModPanel(Mod mod) => new ModPanel(mod); @@ -64,6 +77,15 @@ namespace osu.Game.Overlays.Mods private readonly Bindable>> availableMods = new Bindable>>(); + /// + /// All mods that are available for the current ruleset in this particular column. + /// + /// + /// Note that the mod instances in this list are owned solely by this column + /// (as in, they are locally-managed clones, to ensure proper isolation from any other external instances). + /// + private IReadOnlyList localAvailableMods = Array.Empty(); + private readonly TextFlowContainer headerText; private readonly Box headerBackground; private readonly Container contentContainer; @@ -227,6 +249,9 @@ namespace osu.Game.Overlays.Mods private void load(OsuGameBase game, OverlayColourProvider colourProvider, OsuColour colours) { availableMods.BindTo(game.AvailableMods); + // this `BindValueChanged` callback is intentionally here, to ensure that local available mods are constructed as early as possible. + // this is needed to make sure no external changes to mods are dropped while mod panels are asynchronously loading. + availableMods.BindValueChanged(_ => updateLocalAvailableMods(), true); headerBackground.Colour = accentColour = colours.ForModType(ModType); @@ -240,33 +265,26 @@ namespace osu.Game.Overlays.Mods contentBackground.Colour = colourProvider.Background4; } - protected override void LoadComplete() - { - base.LoadComplete(); - availableMods.BindValueChanged(_ => Scheduler.AddOnce(updateMods)); - SelectedMods.BindValueChanged(_ => - { - // if a load is in progress, don't try to update the selection - the load flow will do so. - if (latestLoadTask == null) - updateActiveState(); - }); - updateMods(); - } - - private CancellationTokenSource? cancellationTokenSource; - - private void updateMods() + private void updateLocalAvailableMods() { var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty()) .Select(m => m.DeepClone()) .ToList(); - if (newMods.SequenceEqual(panelFlow.Children.Select(p => p.Mod))) + if (newMods.SequenceEqual(localAvailableMods)) return; + localAvailableMods = newMods; + Scheduler.AddOnce(loadPanels); + } + + private CancellationTokenSource? cancellationTokenSource; + + private void loadPanels() + { cancellationTokenSource?.Cancel(); - var panels = newMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0))); + var panels = localAvailableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0))); Task? loadTask; @@ -280,20 +298,7 @@ namespace osu.Game.Overlays.Mods foreach (var panel in panelFlow) { - panel.Active.BindValueChanged(_ => - { - updateToggleAllState(); - - var newSelectedMods = SelectedMods.Value; - - var matchingModInstance = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == panel.Mod.GetType()); - if (matchingModInstance != null && (matchingModInstance != panel.Mod || !panel.Active.Value)) - newSelectedMods = newSelectedMods.Except(matchingModInstance.Yield()).ToArray(); - if (panel.Active.Value) - newSelectedMods = newSelectedMods.Append(panel.Mod).ToArray(); - - SelectedMods.Value = newSelectedMods.ToArray(); - }); + panel.Active.BindValueChanged(_ => panelStateChanged(panel)); } }, (cancellationTokenSource = new CancellationTokenSource()).Token); loadTask.ContinueWith(_ => @@ -306,14 +311,62 @@ namespace osu.Game.Overlays.Mods private void updateActiveState() { foreach (var panel in panelFlow) + panel.Active.Value = SelectedMods.Contains(panel.Mod); + } + + /// + /// This flag helps to determine the source of changes to . + /// If the value is false, then are changing due to a user selection on the UI. + /// If the value is true, then are changing due to an external call. + /// + private bool externalSelectionUpdateInProgress; + + private void panelStateChanged(ModPanel panel) + { + updateToggleAllState(); + + var newSelectedMods = panel.Active.Value + ? SelectedMods.Append(panel.Mod) + : SelectedMods.Except(panel.Mod.Yield()); + + SelectedMods = newSelectedMods.ToArray(); + if (!externalSelectionUpdateInProgress) + SelectionChangedByUser?.Invoke(); + } + + /// + /// Adjusts the set of selected mods in this column to match the passed in . + /// + /// + /// This method exists to be able to receive mod instances that come from potentially-external sources and to copy the changes across to this column's state. + /// uses this to substitute any external mod references in + /// to references that are owned by this column. + /// + internal void SetSelection(IReadOnlyList mods) + { + externalSelectionUpdateInProgress = true; + + var newSelection = new List(); + + foreach (var mod in localAvailableMods) { - var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == panel.Mod.GetType()); - panel.Active.Value = matchingSelectedMod != null; - if (panel.Active.Value) - panel.Mod.CopyFrom(matchingSelectedMod); + var matchingSelectedMod = mods.SingleOrDefault(selected => selected.GetType() == mod.GetType()); + + if (matchingSelectedMod != null) + { + mod.CopyFrom(matchingSelectedMod); + newSelection.Add(mod); + } else - panel.Mod.ResetSettingsToDefaults(); + { + mod.ResetSettingsToDefaults(); + } } + + SelectedMods = newSelection; + updateActiveState(); + + externalSelectionUpdateInProgress = false; } #region Bulk select / deselect diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 2f6eae0afb..bb12143d7c 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -179,7 +179,7 @@ namespace osu.Game.Overlays.Mods foreach (var column in columnFlow.Columns) { - column.SelectedMods.BindValueChanged(updateBindableFromSelection); + column.SelectionChangedByUser += updateBindableFromSelection; } customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); @@ -250,33 +250,26 @@ namespace osu.Game.Overlays.Mods private void updateSelectionFromBindable() { - // note that selectionBindableSyncInProgress is purposefully not checked here. - // this is because in the case of mod selection in solo gameplay, a user selection of a mod can actually lead to deselection of other incompatible mods. - // to synchronise state correctly, updateBindableFromSelection() computes the final mods (including incompatibility rules) and updates SelectedMods, - // and this method then runs unconditionally again to make sure the new visual selection accurately reflects the final set of selected mods. - // selectionBindableSyncInProgress ensures that mutual infinite recursion does not happen after that unconditional call. + // `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.SelectedMods.Value = SelectedMods.Value.Where(mod => mod.Type == column.ModType).ToArray(); + column.SetSelection(SelectedMods.Value); + + // and then, when done, replace the potentially-external mod references in `SelectedMods` with ones we own. + updateBindableFromSelection(); } - private bool selectionBindableSyncInProgress; - - private void updateBindableFromSelection(ValueChangedEvent> modSelectionChange) + private void updateBindableFromSelection() { - if (selectionBindableSyncInProgress) + var candidateSelection = columnFlow.Columns.SelectMany(column => column.SelectedMods).ToArray(); + + if (candidateSelection.SequenceEqual(SelectedMods.Value)) return; - selectionBindableSyncInProgress = true; - - SelectedMods.Value = ComputeNewModsFromSelection( - modSelectionChange.NewValue.Except(modSelectionChange.OldValue), - modSelectionChange.OldValue.Except(modSelectionChange.NewValue)); - - selectionBindableSyncInProgress = false; + SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection); } - protected virtual IReadOnlyList ComputeNewModsFromSelection(IEnumerable addedMods, IEnumerable removedMods) - => columnFlow.Columns.SelectMany(column => column.SelectedMods.Value).ToArray(); + protected virtual IReadOnlyList ComputeNewModsFromSelection(IReadOnlyList oldSelection, IReadOnlyList newSelection) => newSelection; protected override void PopIn() { diff --git a/osu.Game/Overlays/Mods/UserModSelectScreen.cs b/osu.Game/Overlays/Mods/UserModSelectScreen.cs index ed0a07521b..ca33d35605 100644 --- a/osu.Game/Overlays/Mods/UserModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/UserModSelectScreen.cs @@ -14,9 +14,12 @@ namespace osu.Game.Overlays.Mods { protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys); - protected override IReadOnlyList ComputeNewModsFromSelection(IEnumerable addedMods, IEnumerable removedMods) + protected override IReadOnlyList ComputeNewModsFromSelection(IReadOnlyList oldSelection, IReadOnlyList newSelection) { - IEnumerable modsAfterRemoval = SelectedMods.Value.Except(removedMods).ToList(); + var addedMods = newSelection.Except(oldSelection); + var removedMods = oldSelection.Except(newSelection); + + IEnumerable modsAfterRemoval = newSelection.Except(removedMods).ToList(); // the preference is that all new mods should override potential incompatible old mods. // in general that's a bit difficult to compute if more than one mod is added at a time, From 50aee8b665bcb9285fd6c8b099135f9aa56b32f4 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 3 May 2022 22:32:01 +0100 Subject: [PATCH 0720/1276] Ensure `ChannelListSelector` text also turns white when selected --- .../Overlays/Chat/ChannelList/ChannelListSelector.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs index f9dab74eb1..9cba93ffa5 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs @@ -21,6 +21,10 @@ namespace osu.Game.Overlays.Chat.ChannelList private Box hoverBox = null!; private Box selectBox = null!; + private OsuSpriteText text = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) @@ -46,7 +50,7 @@ namespace osu.Game.Overlays.Chat.ChannelList { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = 18, Right = 10 }, - Child = new OsuSpriteText + Child = text = new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -68,9 +72,15 @@ namespace osu.Game.Overlays.Chat.ChannelList SelectorActive.BindValueChanged(selector => { if (selector.NewValue) + { + text.FadeColour(colourProvider.Content1, 300, Easing.OutQuint); selectBox.FadeIn(300, Easing.OutQuint); + } else + { + text.FadeColour(colourProvider.Light3, 200, Easing.OutQuint); selectBox.FadeOut(200, Easing.OutQuint); + } }, true); Action = () => SelectorActive.Value = true; From c17edb2848e32dd803e88b6b95efe4c46d74ab2a Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 3 May 2022 22:32:51 +0100 Subject: [PATCH 0721/1276] Add padding to text in `ChatTextBar` to separate it from the textbox --- osu.Game/Overlays/Chat/ChatTextBar.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 316511d9a6..3de7b67b0f 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -68,6 +68,7 @@ namespace osu.Game.Overlays.Chat RelativeSizeAxes = Axes.Y, Width = chatting_text_width, Masking = true, + Padding = new MarginPadding { Right = 5 }, Child = chattingText = new OsuSpriteText { Font = OsuFont.Torus.With(size: 20), From 60999e83e03647f71460c65ba57aa3d038d994ae Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 3 May 2022 22:33:36 +0100 Subject: [PATCH 0722/1276] Ensure `ChatTextBox` takes/leaves focus on chat overlay pop in/out --- osu.Game/Overlays/Chat/ChatTextBar.cs | 4 ++++ osu.Game/Overlays/ChatOverlayV2.cs | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 3de7b67b0f..0fa3613d38 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -26,6 +26,10 @@ namespace osu.Game.Overlays.Chat public event Action? OnSearchTermsChanged; + public void TextBoxTakeFocus() => chatTextBox.TakeFocus(); + + public void TextBoxKillFocus() => chatTextBox.KillFocus(); + [Resolved] private Bindable currentChannel { get; set; } = null!; diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index d0b49d9ef3..f6190bbe20 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -214,16 +214,22 @@ namespace osu.Game.Overlays protected override void PopIn() { + base.PopIn(); + this.MoveToY(0, transition_length, Easing.OutQuint); this.FadeIn(transition_length, Easing.OutQuint); - base.PopIn(); + + textBar.TextBoxTakeFocus(); } protected override void PopOut() { + base.PopOut(); + this.MoveToY(Height, transition_length, Easing.InSine); this.FadeOut(transition_length, Easing.InSine); - base.PopOut(); + + textBar.TextBoxKillFocus(); } private void currentChannelChanged(ValueChangedEvent e) From d52a1a5d2324a5d0041fe7d7e6e548e56f907aa2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 May 2022 03:52:10 +0300 Subject: [PATCH 0723/1276] Add key binding for beatmap selection in song select --- .../Input/Bindings/GlobalActionContainer.cs | 10 +++++++- .../GlobalActionKeyBindingStrings.cs | 10 ++++++++ osu.Game/Screens/Select/BeatmapCarousel.cs | 24 ++++--------------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 47cb7be2cf..ec368a6fda 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -100,10 +100,12 @@ namespace osu.Game.Input.Bindings public IEnumerable SongSelectKeyBindings => new[] { + new KeyBinding(InputKey.Left, GlobalAction.SelectPreviousBeatmap), + new KeyBinding(InputKey.Right, GlobalAction.SelectNextBeatmap), new KeyBinding(InputKey.F1, GlobalAction.ToggleModSelection), new KeyBinding(InputKey.F2, GlobalAction.SelectNextRandom), new KeyBinding(new[] { InputKey.Shift, InputKey.F2 }, GlobalAction.SelectPreviousRandom), - new KeyBinding(InputKey.F3, GlobalAction.ToggleBeatmapOptions) + new KeyBinding(InputKey.F3, GlobalAction.ToggleBeatmapOptions), }; public IEnumerable AudioControlKeyBindings => new[] @@ -301,5 +303,11 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorFlipVertically))] EditorFlipVertically, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectPreviousBeatmap))] + SelectPreviousBeatmap, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectNextBeatmap))] + SelectNextBeatmap, } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 777e97d1e3..08e13e24ae 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -194,6 +194,16 @@ namespace osu.Game.Localisation /// public static LocalisableString ToggleInGameInterface => new TranslatableString(getKey(@"toggle_in_game_interface"), @"Toggle in-game interface"); + /// + /// "Previous beatmap selection" + /// + public static LocalisableString SelectPreviousBeatmap => new TranslatableString(getKey(@"select_previous_beatmap"), @"Previous beatmap selection"); + + /// + /// "Next beatmap selection" + /// + public static LocalisableString SelectNextBeatmap => new TranslatableString(getKey(@"select_next_beatmap"), @"Next beatmap selection"); + /// /// "Toggle Mod Select" /// diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index c3d340ac61..85b5af049e 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -604,34 +604,20 @@ namespace osu.Game.Screens.Select public void ScrollToSelected(bool immediate = false) => pendingScrollOperation = immediate ? PendingScrollOperation.Immediate : PendingScrollOperation.Standard; - #region Key / button selection logic - - protected override bool OnKeyDown(KeyDownEvent e) - { - switch (e.Key) - { - case Key.Left: - SelectNext(-1); - return true; - - case Key.Right: - SelectNext(); - return true; - } - - return false; - } + #region Button selection logic public bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) { case GlobalAction.SelectNext: - SelectNext(1, false); + case GlobalAction.SelectNextBeatmap: + SelectNext(1, e.Action == GlobalAction.SelectNextBeatmap); return true; case GlobalAction.SelectPrevious: - SelectNext(-1, false); + case GlobalAction.SelectPreviousBeatmap: + SelectNext(-1, e.Action == GlobalAction.SelectPreviousBeatmap); return true; } From bb086800b1e528205cfcd635c093798bc87e2a0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 13:41:30 +0900 Subject: [PATCH 0724/1276] Remove ugly playfield border --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index bd26a99e51..ba7c6e9d33 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -59,11 +59,6 @@ namespace osu.Game.Rulesets.Osu.Edit { LayerBelowRuleset.AddRange(new Drawable[] { - new PlayfieldBorder - { - RelativeSizeAxes = Axes.Both, - PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners } - }, distanceSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both From 0bb90c7b07169096984972b9741b6f9a9dbebb2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 13:41:55 +0900 Subject: [PATCH 0725/1276] Fix gridline centering and ensure lines are always rendered using a fixed screen-space width --- .../Components/RectangularPositionSnapGrid.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs index 95b4b2fe53..4c0b4f8beb 100644 --- a/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Layout; +using osu.Framework.Utils; using osuTK; namespace osu.Game.Screens.Edit.Compose.Components @@ -72,25 +73,29 @@ namespace osu.Game.Screens.Edit.Compose.Components int index = 0; float currentPosition = startPosition; - while ((endPosition - currentPosition) * Math.Sign(step) > 0) + // Make lines the same width independent of display resolution. + float lineWidth = DrawWidth / ScreenSpaceDrawQuad.Width; + + while (Precision.AlmostBigger((endPosition - currentPosition) * Math.Sign(step), 0)) { var gridLine = new Box { Colour = Colour4.White, Alpha = index == 0 ? 0.3f : 0.1f, - EdgeSmoothness = new Vector2(0.2f) }; if (direction == Direction.Horizontal) { + gridLine.Origin = Anchor.CentreLeft; gridLine.RelativeSizeAxes = Axes.X; - gridLine.Height = 1; + gridLine.Height = lineWidth; gridLine.Y = currentPosition; } else { + gridLine.Origin = Anchor.TopCentre; gridLine.RelativeSizeAxes = Axes.Y; - gridLine.Width = 1; + gridLine.Width = lineWidth; gridLine.X = currentPosition; } From df530cb5ab2d2c598fcf8f02d20475d20abde36a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 13:49:02 +0900 Subject: [PATCH 0726/1276] Add highlighting to the position snap grid edges in addition to centre lines --- .../Components/RectangularPositionSnapGrid.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs index 4c0b4f8beb..f0d26c7b6a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs @@ -2,6 +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.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -76,12 +78,14 @@ namespace osu.Game.Screens.Edit.Compose.Components // Make lines the same width independent of display resolution. float lineWidth = DrawWidth / ScreenSpaceDrawQuad.Width; + List generatedLines = new List(); + while (Precision.AlmostBigger((endPosition - currentPosition) * Math.Sign(step), 0)) { var gridLine = new Box { Colour = Colour4.White, - Alpha = index == 0 ? 0.3f : 0.1f, + Alpha = 0.1f, }; if (direction == Direction.Horizontal) @@ -99,11 +103,19 @@ namespace osu.Game.Screens.Edit.Compose.Components gridLine.X = currentPosition; } - AddInternal(gridLine); + generatedLines.Add(gridLine); index += 1; currentPosition = startPosition + index * step; } + + if (generatedLines.Count == 0) + return; + + generatedLines.First().Alpha = 0.3f; + generatedLines.Last().Alpha = 0.3f; + + AddRangeInternal(generatedLines); } public Vector2 GetSnappedPosition(Vector2 original) From 8e96af8ff0b7caaaf7e2542856daa6a7ecd001af Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 4 May 2022 13:43:59 +0800 Subject: [PATCH 0727/1276] Update indentation to be clearer --- .../TestSceneManageCollectionsDialog.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index d5983ac827..888002eb36 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -141,15 +141,13 @@ namespace osu.Game.Tests.Visual.Collections { AddStep("add dropdown", () => { - Add( - new CollectionFilterDropdown - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.X, - Width = 0.4f, - } - ); + Add(new CollectionFilterDropdown + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.X, + Width = 0.4f, + }); }); AddStep("add two collections with same name", () => manager.Collections.AddRange(new[] { From 35eeeba4c60b9240296e077ae5968d40100877ee Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 4 May 2022 13:57:53 +0800 Subject: [PATCH 0728/1276] Disable code style analysis on Xamarin projects --- osu.Android.props | 3 ++- osu.iOS.props | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b6260fd1d4..299f236220 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -1,4 +1,4 @@ - + 8.0 bin\$(Configuration) @@ -19,6 +19,7 @@ cjk,mideast,other,rare,west SdkOnly prompt + false True diff --git a/osu.iOS.props b/osu.iOS.props index e472b5f1a8..d16c6803f4 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -1,4 +1,4 @@ - + 8.0 {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} @@ -11,6 +11,7 @@ iPhone Developer true + false From 813d6fed487d06959f18f19e982639e08a95d9dc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 May 2022 09:00:54 +0300 Subject: [PATCH 0729/1276] Split activation keybind to separate increase/decrease keybinds --- .../Input/Bindings/GlobalActionContainer.cs | 10 +++-- .../GlobalActionKeyBindingStrings.cs | 9 ++++- .../Edit/DistancedHitObjectComposer.cs | 40 +++++++++++-------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 5656c79975..3e7051cbf5 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -79,7 +79,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.F5 }, GlobalAction.EditorTestGameplay), new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.EditorFlipHorizontally), new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically), - new KeyBinding(new[] { InputKey.Control, InputKey.Alt }, GlobalAction.EditorDistanceSpacing), + new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.EditorDecreaseDistanceSpacing), + new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelUp }, GlobalAction.EditorIncreaseDistanceSpacing), }; public IEnumerable InGameKeyBindings => new[] @@ -303,7 +304,10 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorFlipVertically))] EditorFlipVertically, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorDistanceSpacing))] - EditorDistanceSpacing, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorIncreaseDistanceSpacing))] + EditorIncreaseDistanceSpacing, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorDecreaseDistanceSpacing))] + EditorDecreaseDistanceSpacing, } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 73cc7546ab..5086e7f4c7 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -240,9 +240,14 @@ namespace osu.Game.Localisation public static LocalisableString EditorFlipVertically => new TranslatableString(getKey(@"editor_flip_vertically"), @"Flip selection vertically"); /// - /// "Distance grid spacing (hold)" + /// "Increase distance grid spacing" /// - public static LocalisableString EditorDistanceSpacing => new TranslatableString(getKey(@"editor_distance_spacing"), @"Distance grid spacing (hold)"); + public static LocalisableString EditorIncreaseDistanceSpacing => new TranslatableString(getKey(@"editor_increase_distance_spacing"), @"Increase distance grid spacing"); + + /// + /// "Decrease distance grid spacing" + /// + public static LocalisableString EditorDecreaseDistanceSpacing => new TranslatableString(getKey(@"editor_decrease_distance_spacing"), @"Decrease distance grid spacing"); /// /// "Toggle skin editor" diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 1a4ea845d0..e6bc36622f 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Edit /// /// The base type of supported objects. [Cached(typeof(IDistanceSnapProvider))] - public abstract class DistancedHitObjectComposer : HitObjectComposer, IDistanceSnapProvider, IKeyBindingHandler + public abstract class DistancedHitObjectComposer : HitObjectComposer, IDistanceSnapProvider, IScrollBindingHandler where TObject : HitObject { protected Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1.0) @@ -35,7 +35,6 @@ namespace osu.Game.Rulesets.Edit protected ExpandingToolboxContainer RightSideToolboxContainer { get; private set; } private ExpandableSlider> distanceSpacingSlider; - private bool distanceSpacingScrollActive; protected DistancedHitObjectComposer(Ruleset ruleset) : base(ruleset) @@ -79,11 +78,11 @@ namespace osu.Game.Rulesets.Edit public bool OnPressed(KeyBindingPressEvent e) { - if (!DistanceSpacingMultiplier.Disabled && e.Action == GlobalAction.EditorDistanceSpacing) + switch (e.Action) { - RightSideToolboxContainer.Expanded.Value = true; - distanceSpacingScrollActive = true; - return true; + case GlobalAction.EditorIncreaseDistanceSpacing: + case GlobalAction.EditorDecreaseDistanceSpacing: + return adjustDistanceSpacing(e.Action, 0.1f); } return false; @@ -91,22 +90,31 @@ namespace osu.Game.Rulesets.Edit public void OnReleased(KeyBindingReleaseEvent e) { - if (!DistanceSpacingMultiplier.Disabled && e.Action == GlobalAction.EditorDistanceSpacing) - { - RightSideToolboxContainer.Expanded.Value = false; - distanceSpacingScrollActive = false; - } } - protected override bool OnScroll(ScrollEvent e) + public bool OnScroll(KeyBindingScrollEvent e) { - if (distanceSpacingScrollActive) + switch (e.Action) { - DistanceSpacingMultiplier.Value += e.ScrollDelta.Y * (e.IsPrecise ? 0.01f : 0.1f); - return true; + case GlobalAction.EditorIncreaseDistanceSpacing: + case GlobalAction.EditorDecreaseDistanceSpacing: + return adjustDistanceSpacing(e.Action, e.IsPrecise ? 0.01f : 0.1f); } - return base.OnScroll(e); + return false; + } + + private bool adjustDistanceSpacing(GlobalAction action, float amount) + { + if (DistanceSpacingMultiplier.Disabled) + return false; + + if (action == GlobalAction.EditorIncreaseDistanceSpacing) + DistanceSpacingMultiplier.Value += amount; + else if (action == GlobalAction.EditorDecreaseDistanceSpacing) + DistanceSpacingMultiplier.Value -= amount; + + return true; } public virtual float GetBeatSnapDistanceAt(HitObject referenceObject) From a17bbd62b7d05491a1ed2d5575819feb0c7fcdc5 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 4 May 2022 14:06:01 +0800 Subject: [PATCH 0730/1276] Disable EnforceCodeStyleInBuild in Xamarin CI run --- .github/workflows/ci.yml | 4 ++-- Directory.Build.props | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2066f27de..b4d7f894d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,7 +128,7 @@ jobs: # cannot accept .sln(f) files as arguments. # Build just the main game for now. - name: Build - run: msbuild osu.Android/osu.Android.csproj /restore /p:Configuration=Debug + run: msbuild osu.Android/osu.Android.csproj /restore /p:Configuration=Debug /p:EnforceCodeStyleInBuild=false build-only-ios: name: Build only (iOS) @@ -147,4 +147,4 @@ jobs: # cannot accept .sln(f) files as arguments. # Build just the main game for now. - name: Build - run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug \ No newline at end of file + run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug /p:EnforceCodeStyleInBuild=false diff --git a/Directory.Build.props b/Directory.Build.props index f3ddc68838..65ffe02584 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,4 +1,4 @@ - + 8.0 @@ -20,7 +20,7 @@ - true + true $(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset From 504ca5be31d011b8909ea05518265529758a63fb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 May 2022 09:51:53 +0300 Subject: [PATCH 0731/1276] Fix scrolling no longer adjusting distance spacing by amount --- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index e6bc36622f..66a78640c6 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Edit { case GlobalAction.EditorIncreaseDistanceSpacing: case GlobalAction.EditorDecreaseDistanceSpacing: - return adjustDistanceSpacing(e.Action, e.IsPrecise ? 0.01f : 0.1f); + return adjustDistanceSpacing(e.Action, e.ScrollAmount * (e.IsPrecise ? 0.01f : 0.1f)); } return false; From 732739715aee8149791a0f2bc35fa4b5d14ba3c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 16:31:20 +0900 Subject: [PATCH 0732/1276] Remove "grid" from strings --- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 5086e7f4c7..58afb284b5 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -240,14 +240,14 @@ namespace osu.Game.Localisation public static LocalisableString EditorFlipVertically => new TranslatableString(getKey(@"editor_flip_vertically"), @"Flip selection vertically"); /// - /// "Increase distance grid spacing" + /// "Increase distance spacing" /// - public static LocalisableString EditorIncreaseDistanceSpacing => new TranslatableString(getKey(@"editor_increase_distance_spacing"), @"Increase distance grid spacing"); + public static LocalisableString EditorIncreaseDistanceSpacing => new TranslatableString(getKey(@"editor_increase_distance_spacing"), @"Increase distance spacing"); /// - /// "Decrease distance grid spacing" + /// "Decrease distance spacing" /// - public static LocalisableString EditorDecreaseDistanceSpacing => new TranslatableString(getKey(@"editor_decrease_distance_spacing"), @"Decrease distance grid spacing"); + public static LocalisableString EditorDecreaseDistanceSpacing => new TranslatableString(getKey(@"editor_decrease_distance_spacing"), @"Decrease distance spacing"); /// /// "Toggle skin editor" From a66743266f6460dc7c50ad873e2b231a813a8d0b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 17:20:58 +0900 Subject: [PATCH 0733/1276] Remove unused `ScrollingToolboxGroup` class --- .../Rulesets/Edit/ScrollingToolboxGroup.cs | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs diff --git a/osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs b/osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs deleted file mode 100644 index 98e026c49a..0000000000 --- a/osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Rulesets.Edit -{ - public class ScrollingToolboxGroup : EditorToolboxGroup - { - protected readonly OsuScrollContainer Scroll; - - protected readonly FillFlowContainer FillFlow; - - protected override Container Content { get; } - - public ScrollingToolboxGroup(string title, float scrollAreaHeight) - : base(title) - { - base.Content.Add(Scroll = new OsuScrollContainer - { - RelativeSizeAxes = Axes.X, - Height = scrollAreaHeight, - Child = Content = FillFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - }, - }); - } - } -} From b325f0ee0b3ebc286f13ba82d95e590707ece455 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 17:41:29 +0900 Subject: [PATCH 0734/1276] Combine editor toolbox container implementation and fix input blocking Until now, toolbox scroll areas would block input from arriving behind them, even when no visible element was clicked. In addition, clicking on a button inside a toolbox would still send a `MouseDown` event to things behind it. Specifically, the editor's `HitObjectComposer` would receive these events and also place objects when the user does not expect them to be placed. This fixes another regression that occurred due to `ScrollContainer`s no longer blocking input theirselves. --- .../Edit/DistancedHitObjectComposer.cs | 19 ++--------- .../Edit/ExpandingToolboxContainer.cs | 34 +++++++++++++++++++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 16 ++------- 3 files changed, 38 insertions(+), 31 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 66a78640c6..370e8ccbda 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -6,12 +6,10 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Overlays.Settings.Sections; using osu.Game.Rulesets.Objects; -using osuTK; namespace osu.Game.Rulesets.Edit { @@ -44,8 +42,9 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load() { - AddInternal(RightSideToolboxContainer = new ExpandingToolboxContainer + AddInternal(RightSideToolboxContainer = new ExpandingToolboxContainer(130, 250) { + Padding = new MarginPadding { Right = 10 }, Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -154,19 +153,5 @@ namespace osu.Game.Rulesets.Edit return DurationToDistance(referenceObject, snappedEndTime - startTime); } - - protected class ExpandingToolboxContainer : ExpandingContainer - { - protected override double HoverExpansionDelay => 250; - - public ExpandingToolboxContainer() - : base(130, 250) - { - RelativeSizeAxes = Axes.Y; - Padding = new MarginPadding { Left = 10 }; - - FillFlow.Spacing = new Vector2(10); - } - } } } diff --git a/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs b/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs new file mode 100644 index 0000000000..e807dbd482 --- /dev/null +++ b/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Containers; +using osuTK; + +namespace osu.Game.Rulesets.Edit +{ + public class ExpandingToolboxContainer : ExpandingContainer + { + protected override double HoverExpansionDelay => 250; + + public ExpandingToolboxContainer(float contractedWidth, float expandedWidth) + : base(contractedWidth, expandedWidth) + { + RelativeSizeAxes = Axes.Y; + + FillFlow.Spacing = new Vector2(10); + } + + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => base.ReceivePositionalInputAtSubTree(screenSpacePos) && anyToolboxHovered(screenSpacePos); + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) && anyToolboxHovered(screenSpacePos); + + private bool anyToolboxHovered(Vector2 screenSpacePos) => FillFlow.Children.Any(d => d.ScreenSpaceDrawQuad.Contains(screenSpacePos)); + + protected override bool OnMouseDown(MouseDownEvent e) => true; + + protected override bool OnClick(ClickEvent e) => true; + } +} diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index a235a5bc60..1c388fe68f 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -13,7 +13,6 @@ using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Game.Beatmaps; -using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; @@ -115,8 +114,9 @@ namespace osu.Game.Rulesets.Edit .WithChild(BlueprintContainer = CreateBlueprintContainer()) } }, - new LeftToolboxFlow + new ExpandingToolboxContainer(80, 200) { + Padding = new MarginPadding { Left = 10 }, Children = new Drawable[] { new EditorToolboxGroup("toolbox (1-9)") @@ -382,18 +382,6 @@ namespace osu.Game.Rulesets.Edit } #endregion - - private class LeftToolboxFlow : ExpandingButtonContainer - { - public LeftToolboxFlow() - : base(80, 200) - { - RelativeSizeAxes = Axes.Y; - Padding = new MarginPadding { Right = 10 }; - - FillFlow.Spacing = new Vector2(10); - } - } } /// From 0b8fd2e39f9c4e93d91c43f93da1c08e27244f33 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 May 2022 11:41:33 +0300 Subject: [PATCH 0735/1276] Improve distance spacing toast inline with key binding changes --- .../Edit/DistancedHitObjectComposer.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index afd6909874..466b6cba4b 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -3,11 +3,13 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; @@ -80,7 +82,7 @@ namespace osu.Game.Rulesets.Edit distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; if (v.NewValue != v.OldValue) - onScreenDisplay?.Display(new DistanceSpacingToast(v.NewValue.ToLocalisableString(@"0.##x"))); + onScreenDisplay?.Display(new DistanceSpacingToast(v.NewValue.ToLocalisableString(@"0.##x"), v)); EditorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; }, true); @@ -182,10 +184,23 @@ namespace osu.Game.Rulesets.Edit private class DistanceSpacingToast : Toast { - public DistanceSpacingToast(LocalisableString value) - : base("Distance Spacing", value, string.Empty) + private readonly ValueChangedEvent change; + + public DistanceSpacingToast(LocalisableString value, ValueChangedEvent change) + : base(getAction(change).GetLocalisableDescription(), value, string.Empty) { + this.change = change; } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + ShortcutText.Text = config.LookupKeyBindings(getAction(change)); + } + + private static GlobalAction getAction(ValueChangedEvent change) => change.NewValue - change.OldValue > 0 + ? GlobalAction.EditorIncreaseDistanceSpacing + : GlobalAction.EditorDecreaseDistanceSpacing; } } } From f899c3e68fa5ad4eb53196de2dd509fd3d9d46b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 17:56:38 +0900 Subject: [PATCH 0736/1276] Add test coverage of circle placement around editor toolboxes --- .../Editing/TestSceneHitObjectComposer.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index ae1b691767..231a1f408f 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -19,6 +19,7 @@ using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.RadioButtons; +using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; using osuTK.Input; @@ -87,6 +88,65 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is HitCircleCompositionTool); } + [Test] + public void TestPlacementFailsWhenClickingButton() + { + AddStep("clear all control points and hitobjects", () => + { + editorBeatmap.ControlPointInfo.Clear(); + editorBeatmap.Clear(); + }); + + AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); + + AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").TriggerClick()); + + AddStep("move mouse to overlapping toggle button", () => + { + var playfield = hitObjectComposer.Playfield.ScreenSpaceDrawQuad; + var button = hitObjectComposer + .ChildrenOfType().First() + .ChildrenOfType().First(b => playfield.Contains(b.ScreenSpaceDrawQuad.Centre)); + + InputManager.MoveMouseTo(button); + }); + + AddAssert("no circles placed", () => editorBeatmap.HitObjects.Count == 0); + + AddStep("attempt place circle", () => InputManager.Click(MouseButton.Left)); + + AddAssert("no circles placed", () => editorBeatmap.HitObjects.Count == 0); + } + + [Test] + public void TestPlacementWithinToolboxScrollArea() + { + AddStep("clear all control points and hitobjects", () => + { + editorBeatmap.ControlPointInfo.Clear(); + editorBeatmap.Clear(); + }); + + AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); + + AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").TriggerClick()); + + AddStep("move mouse to scroll area", () => + { + // Specifically wanting to test the area of overlap between the left expanding toolbox container + // and the playfield/composer. + var scrollArea = hitObjectComposer.ChildrenOfType().First().ScreenSpaceDrawQuad; + var playfield = hitObjectComposer.Playfield.ScreenSpaceDrawQuad; + InputManager.MoveMouseTo(new Vector2(scrollArea.TopLeft.X, playfield.Centre.Y)); + }); + + AddAssert("no circles placed", () => editorBeatmap.HitObjects.Count == 0); + + AddStep("place circle", () => InputManager.Click(MouseButton.Left)); + + AddAssert("circle placed", () => editorBeatmap.HitObjects.Count == 1); + } + [Test] public void TestDistanceSpacingHotkeys() { From f5d4f02200aa1118485de0eddfa977aa4ec09325 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 May 2022 11:59:29 +0300 Subject: [PATCH 0737/1276] Use `ToUpper` for key binding text --- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 466b6cba4b..3ca443cc85 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - ShortcutText.Text = config.LookupKeyBindings(getAction(change)); + ShortcutText.Text = config.LookupKeyBindings(getAction(change)).ToUpper(); } private static GlobalAction getAction(ValueChangedEvent change) => change.NewValue - change.OldValue > 0 From 4e0f899159aebc9b5de32b04faefc7f98f8cc654 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 18:13:30 +0900 Subject: [PATCH 0738/1276] Rename value changed variable --- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 3ca443cc85..081610a62b 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -76,15 +76,15 @@ namespace osu.Game.Rulesets.Edit if (!DistanceSpacingMultiplier.Disabled) { DistanceSpacingMultiplier.Value = EditorBeatmap.BeatmapInfo.DistanceSpacing; - DistanceSpacingMultiplier.BindValueChanged(v => + DistanceSpacingMultiplier.BindValueChanged(multiplier => { - distanceSpacingSlider.ContractedLabelText = $"D. S. ({v.NewValue:0.##x})"; - distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})"; + distanceSpacingSlider.ContractedLabelText = $"D. S. ({multiplier.NewValue:0.##x})"; + distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({multiplier.NewValue:0.##x})"; - if (v.NewValue != v.OldValue) - onScreenDisplay?.Display(new DistanceSpacingToast(v.NewValue.ToLocalisableString(@"0.##x"), v)); + if (multiplier.NewValue != multiplier.OldValue) + onScreenDisplay?.Display(new DistanceSpacingToast(multiplier.NewValue.ToLocalisableString(@"0.##x"), multiplier)); - EditorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue; + EditorBeatmap.BeatmapInfo.DistanceSpacing = multiplier.NewValue; }, true); } } From a759e641ebf17f71cbd825491fb34fc95045f382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 11:56:29 +0200 Subject: [PATCH 0739/1276] Enforce composer aspect ratio to avoid depending on ambient window size --- osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 231a1f408f..fb179a448c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -71,6 +71,11 @@ namespace osu.Game.Tests.Visual.Editing Child = editorBeatmapContainer = new EditorBeatmapContainer(Beatmap.Value) { Child = hitObjectComposer = new OsuHitObjectComposer(new OsuRuleset()) + { + // force the composer to fully overlap the playfield area by setting a 4:3 aspect ratio. + FillMode = FillMode.Fit, + FillAspectRatio = 4 / 3f + } }; }); } From 6380216263c0cff2c46417322362da11631aa63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 11:56:44 +0200 Subject: [PATCH 0740/1276] Nudge click location to avoid placement failures --- osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index fb179a448c..c9d44fdab7 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -142,7 +142,7 @@ namespace osu.Game.Tests.Visual.Editing // and the playfield/composer. var scrollArea = hitObjectComposer.ChildrenOfType().First().ScreenSpaceDrawQuad; var playfield = hitObjectComposer.Playfield.ScreenSpaceDrawQuad; - InputManager.MoveMouseTo(new Vector2(scrollArea.TopLeft.X, playfield.Centre.Y)); + InputManager.MoveMouseTo(new Vector2(scrollArea.TopLeft.X + 1, playfield.Centre.Y)); }); AddAssert("no circles placed", () => editorBeatmap.HitObjects.Count == 0); From 2f12c7d9e1e9c1231c82c2bd4766f198dfa709a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 19:09:22 +0900 Subject: [PATCH 0741/1276] Change `ChatTextBox` to not handle up/down arrows --- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 6a7da52416..df210fcaf8 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Chat; using osu.Game.Resources.Localisation.Web; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Online.Chat { @@ -119,6 +120,20 @@ namespace osu.Game.Online.Chat public class ChatTextBox : FocusedTextBox { + protected override bool OnKeyDown(KeyDownEvent e) + { + // Chat text boxes are generally used in places where they retain focus, but shouldn't block interaction with other + // elements on the same screen. + switch (e.Key) + { + case Key.Up: + case Key.Down: + return false; + } + + return base.OnKeyDown(e); + } + protected override void LoadComplete() { base.LoadComplete(); From df1f4aecdceee273e3fbafbe7e7fa2c208e50419 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 19:09:40 +0900 Subject: [PATCH 0742/1276] Add support for traversing playlist items using next/previous bindings Addresses https://github.com/ppy/osu/discussions/18061. --- .../OnlinePlay/DrawableRoomPlaylist.cs | 53 ++++++++++++++++++- .../Lounge/Components/RoomsContainer.cs | 2 +- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs index 57bb4253cb..d1b21ca1b0 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs @@ -6,7 +6,10 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; +using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; using osuTK; @@ -15,7 +18,7 @@ namespace osu.Game.Screens.OnlinePlay /// /// A scrollable list which displays the s in a . /// - public class DrawableRoomPlaylist : OsuRearrangeableListContainer + public class DrawableRoomPlaylist : OsuRearrangeableListContainer, IKeyBindingHandler { /// /// The currently-selected item. Selection is visually represented with a border. @@ -169,5 +172,53 @@ namespace osu.Game.Screens.OnlinePlay }); protected virtual DrawableRoomPlaylistItem CreateDrawablePlaylistItem(PlaylistItem item) => new DrawableRoomPlaylistItem(item); + + #region Key selection logic (shared with BeatmapCarousel and RoomsContainer) + + public bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case GlobalAction.SelectNext: + selectNext(1); + return true; + + case GlobalAction.SelectPrevious: + selectNext(-1); + return true; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + + private void selectNext(int direction) + { + if (SelectedItem.Disabled) + return; + + var visibleRooms = ListContainer.AsEnumerable().Where(r => r.IsPresent); + + PlaylistItem item; + + if (SelectedItem.Value == null) + item = visibleRooms.FirstOrDefault()?.Model; + else + { + if (direction < 0) + visibleRooms = visibleRooms.Reverse(); + + item = visibleRooms.SkipWhile(r => r.Model != SelectedItem.Value).Skip(1).FirstOrDefault()?.Model; + } + + // we already have a valid selection only change selection if we still have a room to switch to. + if (item != null) + SelectedItem.Value = item; + } + + #endregion } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 175cd2c44e..9bcda90e6d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -139,7 +139,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components return base.OnClick(e); } - #region Key selection logic (shared with BeatmapCarousel) + #region Key selection logic (shared with BeatmapCarousel and RoomsContainer) public bool OnPressed(KeyBindingPressEvent e) { From 3b04daddaa9ec3de743eb590003df4f4cc17592d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 12:36:43 +0200 Subject: [PATCH 0743/1276] Fix self-reference in region name --- osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 9bcda90e6d..74e4225f11 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -139,7 +139,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components return base.OnClick(e); } - #region Key selection logic (shared with BeatmapCarousel and RoomsContainer) + #region Key selection logic (shared with BeatmapCarousel and DrawableRoomPlaylist) public bool OnPressed(KeyBindingPressEvent e) { From 3eead5a6a39c3add1966990f280d655c2d71f3c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 19:40:08 +0900 Subject: [PATCH 0744/1276] Rename `FlushAnimation` to `FlushPendingSelections` to better match purpose --- osu.Game/Overlays/Mods/ModColumn.cs | 4 ++-- osu.Game/Overlays/Mods/ModSection.cs | 4 ++-- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index f9332dd28b..a792c0a81e 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -435,9 +435,9 @@ namespace osu.Game.Overlays.Mods } /// - /// Play out all remaining animations immediately to leave mods in a good (final) state. + /// Run any delayed selections (due to animation) immediately to leave mods in a good (final) state. /// - public void FlushAnimation() + public void FlushPendingSelections() { while (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) dequeuedAction(); diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 5bf8cddd0c..a70191a864 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -250,9 +250,9 @@ namespace osu.Game.Overlays.Mods protected virtual ModButton CreateModButton(Mod mod) => new ModButton(mod); /// - /// Play out all remaining animations immediately to leave mods in a good (final) state. + /// Run any delayed selections (due to animation) immediately to leave mods in a good (final) state. /// - public void FlushAnimation() + public void FlushPendingSelections() { while (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) dequeuedAction(); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 9ce79c25f7..cf57322594 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -369,7 +369,7 @@ namespace osu.Game.Overlays.Mods foreach (var section in ModSectionsContainer) { - section.FlushAnimation(); + section.FlushPendingSelections(); } FooterContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index bb12143d7c..8060bca65f 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -308,7 +308,7 @@ namespace osu.Game.Overlays.Mods var column = columnFlow[i].Column; - column.FlushAnimation(); + column.FlushPendingSelections(); column.TopLevelContent .MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint) .FadeOut(fade_out_duration, Easing.OutQuint); From ec27fa8e857a357e7e07f205dcdc6c3891666416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 13:12:21 +0200 Subject: [PATCH 0745/1276] Add test coverage for keyboard selection --- .../TestSceneDrawableRoomPlaylist.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 76353323d6..566a8db608 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -147,6 +147,40 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("item 1 is selected", () => playlist.SelectedItem.Value == playlist.Items[1]); } + [Test] + public void KeyboardSelection() + { + createPlaylist(p => p.AllowSelection = true); + + AddStep("press down", () => InputManager.Key(Key.Down)); + AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]); + + AddStep("press down", () => InputManager.Key(Key.Down)); + AddAssert("item 1 is selected", () => playlist.SelectedItem.Value == playlist.Items[1]); + + AddStep("press up", () => InputManager.Key(Key.Up)); + AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]); + + AddUntilStep("navigate to last item via keyboard", () => + { + InputManager.Key(Key.Down); + return playlist.SelectedItem.Value == playlist.Items.Last(); + }); + AddAssert("last item is selected", () => playlist.SelectedItem.Value == playlist.Items.Last()); + AddUntilStep("last item is scrolled into view", () => + { + var drawableItem = playlist.ItemMap[playlist.Items.Last()]; + return playlist.ScreenSpaceDrawQuad.Contains(drawableItem.ScreenSpaceDrawQuad.TopLeft) + && playlist.ScreenSpaceDrawQuad.Contains(drawableItem.ScreenSpaceDrawQuad.BottomRight); + }); + + AddStep("press down", () => InputManager.Key(Key.Down)); + AddAssert("last item is selected", () => playlist.SelectedItem.Value == playlist.Items.Last()); + + AddStep("press up", () => InputManager.Key(Key.Up)); + AddAssert("second last item is selected", () => playlist.SelectedItem.Value == playlist.Items.Reverse().ElementAt(1)); + } + [Test] public void TestDownloadButtonHiddenWhenBeatmapExists() { From 0405c1c34a18ca1c06be95fa5426f7deeeaf0daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 13:15:26 +0200 Subject: [PATCH 0746/1276] Ensure selected playlist item is always scrolled into view --- .../Screens/OnlinePlay/DrawableRoomPlaylist.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs index d1b21ca1b0..fe654c4ab8 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -173,6 +174,21 @@ namespace osu.Game.Screens.OnlinePlay protected virtual DrawableRoomPlaylistItem CreateDrawablePlaylistItem(PlaylistItem item) => new DrawableRoomPlaylistItem(item); + protected override void LoadComplete() + { + base.LoadComplete(); + + SelectedItem.BindValueChanged(_ => scrollToSelection(), true); + } + + private void scrollToSelection() + { + if (SelectedItem.Value == null) return; + + Debug.Assert(ItemMap.TryGetValue(SelectedItem.Value, out var drawableItem)); + ScrollContainer.ScrollIntoView(drawableItem); + } + #region Key selection logic (shared with BeatmapCarousel and RoomsContainer) public bool OnPressed(KeyBindingPressEvent e) From cf220f171b22ac1a65e65c91daa9b35499f5bea3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 20:14:30 +0900 Subject: [PATCH 0747/1276] Add test coverage of cross-thread transforms if a dialog is pushed too early --- .../UserInterface/TestSceneDialogOverlay.cs | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index 405461eec8..aecb3f6c71 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Threading; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Overlays.Dialog; @@ -15,15 +17,11 @@ namespace osu.Game.Tests.Visual.UserInterface { private DialogOverlay overlay; - [SetUpSteps] - public void SetUpSteps() - { - AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay()); - } - [Test] public void TestBasic() { + AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay()); + TestPopupDialog firstDialog = null; TestPopupDialog secondDialog = null; @@ -37,12 +35,12 @@ namespace osu.Game.Tests.Visual.UserInterface new PopupDialogOkButton { Text = @"I never want to see this again.", - Action = () => System.Console.WriteLine(@"OK"), + Action = () => Console.WriteLine(@"OK"), }, new PopupDialogCancelButton { Text = @"Firetruck, I still want quick ranks!", - Action = () => System.Console.WriteLine(@"Cancel"), + Action = () => Console.WriteLine(@"Cancel"), }, }, })); @@ -87,9 +85,51 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("first dialog is not part of hierarchy", () => firstDialog.Parent == null); } + [Test] + public void TestPushBeforeLoad() + { + PopupDialog dialog = null; + + AddStep("create dialog overlay", () => overlay = new SlowLoadingDialogOverlay()); + + AddStep("start loading overlay", () => LoadComponentAsync(overlay, Add)); + + AddStep("push dialog before loaded", () => + { + overlay.Push(dialog = new TestPopupDialog + { + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton { Text = @"OK" }, + }, + }); + }); + + AddAssert("dialog not displayed", () => overlay.CurrentDialog != dialog); + + AddStep("complete load", () => ((SlowLoadingDialogOverlay)overlay).LoadEvent.Set()); + + AddUntilStep("wait for load", () => overlay.IsLoaded); + + AddAssert("dialog displayed", () => overlay.CurrentDialog == dialog); + } + + public class SlowLoadingDialogOverlay : DialogOverlay + { + public ManualResetEventSlim LoadEvent = new ManualResetEventSlim(); + + [BackgroundDependencyLoader] + private void load() + { + LoadEvent.Wait(10000); + } + } + [Test] public void TestDismissBeforePush() { + AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay()); + TestPopupDialog testDialog = null; AddStep("dismissed dialog push", () => { @@ -106,6 +146,8 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestDismissBeforePushViaButtonPress() { + AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay()); + TestPopupDialog testDialog = null; AddStep("dismissed dialog push", () => { From 383245f43afec71c7e5d44102c04395032a582e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 20:22:52 +0900 Subject: [PATCH 0748/1276] Fix dialoged pushed to `DialogOverlay` too early potentially doing cross-thread transforms As seen at https://github.com/ppy/osu/runs/6287700705?check_suite_focus=true. --- osu.Game/Overlays/DialogOverlay.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 15d89a561a..0699c69691 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -47,6 +47,12 @@ namespace osu.Game.Overlays public void Push(PopupDialog dialog) { + if (!IsLoaded) + { + Schedule(() => Push(dialog)); + return; + } + if (dialog == CurrentDialog || dialog.State.Value != Visibility.Visible) return; // if any existing dialog is being displayed, dismiss it before showing a new one. @@ -60,7 +66,7 @@ namespace osu.Game.Overlays Show(); } - public override bool IsPresent => dialogContainer.Children.Count > 0; + public override bool IsPresent => Scheduler.HasPendingTasks || dialogContainer.Children.Count > 0; protected override bool BlockNonPositionalInput => true; @@ -88,16 +94,14 @@ namespace osu.Game.Overlays protected override void PopOut() { base.PopOut(); + this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); + + // PopOut is called as part of VisibilityContainer's initialisation logic, but we don't want it to interact with a potentially waiting dialog. + if (!IsLoaded) return; lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 100, Easing.InCubic); - if (CurrentDialog?.State.Value == Visibility.Visible) - { - CurrentDialog.Hide(); - return; - } - - this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); + if (CurrentDialog?.State.Value == Visibility.Visible) CurrentDialog.Hide(); } public override bool OnPressed(KeyBindingPressEvent e) From e6fdef2d7afbb769583bc5a4cab62eba438e5773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 13:51:47 +0200 Subject: [PATCH 0749/1276] Fix test failures due to selection/item collection desyncs --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs index fe654c4ab8..18cf454be3 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -178,14 +177,19 @@ namespace osu.Game.Screens.OnlinePlay { base.LoadComplete(); - SelectedItem.BindValueChanged(_ => scrollToSelection(), true); + // schedules added as the properties may change value while the drawable items haven't been created yet. + SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(scrollToSelection)); + Items.BindCollectionChanged((_, __) => Scheduler.AddOnce(scrollToSelection), true); } private void scrollToSelection() { - if (SelectedItem.Value == null) return; + // SelectedItem and ItemMap/drawable items are managed separately, + // so if the item can't be unmapped to a drawable, don't try to scroll to it. + // best effort is made to not drop any updates, by subscribing to both sources. + if (SelectedItem.Value == null || !ItemMap.TryGetValue(SelectedItem.Value, out var drawableItem)) + return; - Debug.Assert(ItemMap.TryGetValue(SelectedItem.Value, out var drawableItem)); ScrollContainer.ScrollIntoView(drawableItem); } From 81ce0e6565f244f6d1c59f00653ca972ab49a63d Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 4 May 2022 12:55:22 +0100 Subject: [PATCH 0750/1276] Reimplement sliderticks --- .../Mods/OsuModStrictTracking.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs index ab45e5192d..a1e6946157 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs @@ -70,6 +70,11 @@ namespace osu.Game.Rulesets.Osu.Mods drawableRuleset.Playfield.RegisterPool(10, 100); } + private class StrictTrackingSliderTick : SliderTick + { + public override Judgement CreateJudgement() => new OsuIgnoreJudgement(); + } + private class StrictTrackingSliderTailCircle : SliderTailCircle { public StrictTrackingSliderTailCircle(Slider slider) @@ -109,6 +114,18 @@ namespace osu.Game.Rulesets.Osu.Mods { switch (e.Type) { + case SliderEventType.Tick: + AddNested(new StrictTrackingSliderTick + { + SpanIndex = e.SpanIndex, + SpanStartTime = e.SpanStartTime, + StartTime = e.Time, + Position = Position + Path.PositionAt(e.PathProgress), + StackHeight = StackHeight, + Scale = Scale, + }); + break; + case SliderEventType.Head: AddNested(HeadCircle = new SliderHeadCircle { From ddab3c6d8084a0c19289c56f53bdb779481ec4b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 May 2022 21:00:11 +0900 Subject: [PATCH 0751/1276] Tidy up state variables --- osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index e6a126f4d8..c5ac87f527 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -163,19 +163,17 @@ namespace osu.Game.Overlays.Chat.ChannelList private void updateState() { - if (showSelected) + bool selected = selectedChannel.Value == Channel && !SelectorActive.Value; + + if (selected) selectBox.FadeIn(300, Easing.OutQuint); else selectBox.FadeOut(200, Easing.OutQuint); - if (showUnread || showSelected) + if (Unread.Value || selected) text.FadeColour(colourProvider.Content1, 300, Easing.OutQuint); else text.FadeColour(colourProvider.Light3, 200, Easing.OutQuint); } - - private bool showUnread => Unread.Value; - - private bool showSelected => selectedChannel.Value == Channel && !SelectorActive.Value; } } From 1a85e1267bc0ccf3700360f831b69f5e5cc26312 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 4 May 2022 14:43:40 +0100 Subject: [PATCH 0752/1276] Ensure focus is directed to `ChatTextBox` from `ChatOverlay` and add tests --- .../Visual/Online/TestSceneChatOverlayV2.cs | 27 ++++++++++++++++++- osu.Game/Overlays/ChatOverlayV2.cs | 8 ++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index c7bb6760c7..9d4de11c5a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -144,7 +144,7 @@ namespace osu.Game.Tests.Visual.Online { AddStep("Show overlay", () => chatOverlay.Show()); AddAssert("Listing is visible", () => listingVisibility == Visibility.Visible); - AddStep("Search for 'number 2'", () => chatOverlay.ChildrenOfType().Single().Text = "number 2"); + AddStep("Search for 'number 2'", () => chatOverlayTextBox.Text = "number 2"); AddUntilStep("Only channel 2 visibile", () => { IEnumerable listingItems = chatOverlay.ChildrenOfType() @@ -325,6 +325,28 @@ namespace osu.Game.Tests.Visual.Online AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); } + [Test] + public void TextBoxRetainsFocus() + { + AddStep("Show overlay", () => chatOverlay.Show()); + AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); + AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); + AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); + AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); + AddStep("Click selector", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); + AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); + AddStep("Click listing", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); + AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); + AddStep("Click drawable channel", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); + AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); + AddStep("Click channel list", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); + AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); + AddStep("Click top bar", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); + AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); + AddStep("Hide overlay", () => chatOverlay.Hide()); + AddAssert("TextBox is not focused", () => InputManager.FocusedDrawable == null); + } + private Visibility listingVisibility => chatOverlay.ChildrenOfType().Single().State.Value; @@ -337,6 +359,9 @@ namespace osu.Game.Tests.Visual.Online private ChannelListItem getChannelListItem(Channel channel) => chatOverlay.ChildrenOfType().Single(item => item.Channel == channel); + private ChatTextBox chatOverlayTextBox => + chatOverlay.ChildrenOfType().Single(); + private void clickDrawable(Drawable d) { InputManager.MoveMouseTo(d); diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index f6190bbe20..658a28bfdb 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -218,8 +218,6 @@ namespace osu.Game.Overlays this.MoveToY(0, transition_length, Easing.OutQuint); this.FadeIn(transition_length, Easing.OutQuint); - - textBar.TextBoxTakeFocus(); } protected override void PopOut() @@ -232,6 +230,12 @@ namespace osu.Game.Overlays textBar.TextBoxKillFocus(); } + protected override void OnFocus(FocusEvent e) + { + textBar.TextBoxTakeFocus(); + base.OnFocus(e); + } + private void currentChannelChanged(ValueChangedEvent e) { Channel? newChannel = e.NewValue; From 9416346c942ad20baa7b8000ebcb0a028a8c796e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 May 2022 16:46:23 +0300 Subject: [PATCH 0753/1276] Globalise beatmap selection key bindings as "group" selection --- .../Input/Bindings/GlobalActionContainer.cs | 13 ++++++------ .../GlobalActionKeyBindingStrings.cs | 20 +++++++++---------- osu.Game/Overlays/Volume/VolumeMeter.cs | 4 ++-- osu.Game/Screens/Select/BeatmapCarousel.cs | 8 ++++---- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index ec368a6fda..9cb8f2dfcc 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -59,6 +59,9 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.Up, GlobalAction.SelectPrevious), new KeyBinding(InputKey.Down, GlobalAction.SelectNext), + new KeyBinding(InputKey.Left, GlobalAction.SelectPreviousGroup), + new KeyBinding(InputKey.Right, GlobalAction.SelectNextGroup), + new KeyBinding(InputKey.Space, GlobalAction.Select), new KeyBinding(InputKey.Enter, GlobalAction.Select), new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select), @@ -100,8 +103,6 @@ namespace osu.Game.Input.Bindings public IEnumerable SongSelectKeyBindings => new[] { - new KeyBinding(InputKey.Left, GlobalAction.SelectPreviousBeatmap), - new KeyBinding(InputKey.Right, GlobalAction.SelectNextBeatmap), new KeyBinding(InputKey.F1, GlobalAction.ToggleModSelection), new KeyBinding(InputKey.F2, GlobalAction.SelectNextRandom), new KeyBinding(new[] { InputKey.Shift, InputKey.F2 }, GlobalAction.SelectPreviousRandom), @@ -304,10 +305,10 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorFlipVertically))] EditorFlipVertically, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectPreviousBeatmap))] - SelectPreviousBeatmap, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectPreviousGroup))] + SelectPreviousGroup, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectNextBeatmap))] - SelectNextBeatmap, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectNextGroup))] + SelectNextGroup, } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 08e13e24ae..129706261b 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -129,6 +129,16 @@ namespace osu.Game.Localisation /// public static LocalisableString SelectNext => new TranslatableString(getKey(@"select_next"), @"Next selection"); + /// + /// "Previous group selection" + /// + public static LocalisableString SelectPreviousGroup => new TranslatableString(getKey(@"select_previous_group"), @"Previous group selection"); + + /// + /// "Next group selection" + /// + public static LocalisableString SelectNextGroup => new TranslatableString(getKey(@"select_next_group"), @"Next group selection"); + /// /// "Home" /// @@ -194,16 +204,6 @@ namespace osu.Game.Localisation /// public static LocalisableString ToggleInGameInterface => new TranslatableString(getKey(@"toggle_in_game_interface"), @"Toggle in-game interface"); - /// - /// "Previous beatmap selection" - /// - public static LocalisableString SelectPreviousBeatmap => new TranslatableString(getKey(@"select_previous_beatmap"), @"Previous beatmap selection"); - - /// - /// "Next beatmap selection" - /// - public static LocalisableString SelectNextBeatmap => new TranslatableString(getKey(@"select_next_beatmap"), @"Next beatmap selection"); - /// /// "Toggle Mod Select" /// diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index e2afd46c18..46b8b35da2 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -372,12 +372,12 @@ namespace osu.Game.Overlays.Volume switch (e.Action) { - case GlobalAction.SelectPrevious: + case GlobalAction.SelectPreviousGroup: State = SelectionState.Selected; adjust(1, false); return true; - case GlobalAction.SelectNext: + case GlobalAction.SelectNextGroup: State = SelectionState.Selected; adjust(-1, false); return true; diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 85b5af049e..a59f14647d 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -611,13 +611,13 @@ namespace osu.Game.Screens.Select switch (e.Action) { case GlobalAction.SelectNext: - case GlobalAction.SelectNextBeatmap: - SelectNext(1, e.Action == GlobalAction.SelectNextBeatmap); + case GlobalAction.SelectNextGroup: + SelectNext(1, e.Action == GlobalAction.SelectNextGroup); return true; case GlobalAction.SelectPrevious: - case GlobalAction.SelectPreviousBeatmap: - SelectNext(-1, e.Action == GlobalAction.SelectPreviousBeatmap); + case GlobalAction.SelectPreviousGroup: + SelectNext(-1, e.Action == GlobalAction.SelectPreviousGroup); return true; } From 20e277d2e587069d0534bf457efa8999d0ac9629 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 May 2022 17:08:41 +0300 Subject: [PATCH 0754/1276] Apply proposed naming changes --- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 2 +- osu.Game/Rulesets/Mods/IMod.cs | 4 ++-- osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | 2 +- osu.Game/Rulesets/Mods/ModRateAdjust.cs | 2 +- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- osu.Game/Rulesets/Mods/ModUsage.cs | 6 +++--- osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs | 2 +- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 4 ++-- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 2 +- .../Screens/Play/PlayerSettings/BeatmapOffsetControl.cs | 2 +- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- osu.Game/Utils/ModUtils.cs | 4 ++-- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index caeb757d7a..17ea117018 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet return; modsContainer.Add(new ModButton(new ModNoMod())); - modsContainer.AddRange(rulesetInstance.AllMods.Where(m => m.IsPlayable(ModUsage.User)).Select(m => new ModButton(m))); + modsContainer.AddRange(rulesetInstance.AllMods.Where(m => m.IsPlayable(ModUsage.SoloLocal)).Select(m => new ModButton(m))); modsContainer.ForEach(button => { diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index 325b75b76e..d37ef53a66 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -39,8 +39,8 @@ namespace osu.Game.Rulesets.Mods /// /// /// Should be always false for cases where the user is not interacting with the game. - /// Should be false in for mods that make gameplay duration dependent on user input (e.g. ). - /// Should be false in for mods that affect the gameplay duration (e.g. and ). + /// Should be false in for mods that make gameplay duration dependent on user input (e.g. ). + /// Should be false in for mods that affect the gameplay duration (e.g. and ). /// /// /// The mod usage. diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs index 801cd3cba7..700ad30c08 100644 --- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs +++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 1; - public override bool IsPlayable(ModUsage usage) => usage == ModUsage.User; + public override bool IsPlayable(ModUsage usage) => usage == ModUsage.SoloLocal; public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModTimeRamp) }; diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index 59d9026f3b..acb39bfbe6 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModRateAdjust : Mod, IApplicableToRate { - public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerPerPlayer; + public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerLocal; public abstract BindableNumber SpeedChange { get; } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 96b38301b5..c554c39010 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] public abstract BindableBool AdjustPitch { get; } - public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerPerPlayer; + public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerLocal; public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModAdaptiveSpeed) }; diff --git a/osu.Game/Rulesets/Mods/ModUsage.cs b/osu.Game/Rulesets/Mods/ModUsage.cs index 04d99149dd..3f25154d73 100644 --- a/osu.Game/Rulesets/Mods/ModUsage.cs +++ b/osu.Game/Rulesets/Mods/ModUsage.cs @@ -11,17 +11,17 @@ namespace osu.Game.Rulesets.Mods /// /// This mod can be used for a per-user gameplay session. /// - User, + SoloLocal, /// /// This mod can be used in multiplayer but must be applied to all users. /// This is generally the case for mods which affect the length of gameplay. /// - MultiplayerRoomWide, + MultiplayerGlobal, /// /// This mod can be used in multiplayer either at a room or per-player level (i.e. "free mod"). /// - MultiplayerPerPlayer, + MultiplayerLocal, } } diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 37cffd8343..13d5dd8927 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay public new Func IsValidMod { get => base.IsValidMod; - set => base.IsValidMod = m => m.HasImplementation && m.IsPlayable(ModUsage.User) && value(m); + set => base.IsValidMod = m => m.HasImplementation && m.IsPlayable(ModUsage.SoloLocal) && value(m); } public FreeModSelectOverlay() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 5bf217031a..d7e752623d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -95,8 +95,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && mod.IsPlayable(ModUsage.MultiplayerRoomWide); + protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && mod.IsPlayable(ModUsage.MultiplayerGlobal); - protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && mod.IsPlayable(ModUsage.MultiplayerPerPlayer); + protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && mod.IsPlayable(ModUsage.MultiplayerLocal); } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index d9da230f58..b8b0604c79 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -170,7 +170,7 @@ namespace osu.Game.Screens.OnlinePlay /// /// The to check. /// Whether is a valid mod for online play. - protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && ModUtils.FlattenMod(mod).All(m => m.IsPlayable(ModUsage.User)); + protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && ModUtils.FlattenMod(mod).All(m => m.IsPlayable(ModUsage.SoloLocal)); /// /// Checks whether a given is valid for per-player free-mod selection. diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 554b58f481..af2cecfba5 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -188,7 +188,7 @@ namespace osu.Game.Screens.Play.PlayerSettings if (score.NewValue == null) return; - if (score.NewValue.Mods.Any(m => !m.IsPlayable(ModUsage.User))) + if (score.NewValue.Mods.Any(m => !m.IsPlayable(ModUsage.SoloLocal))) return; var hitEvents = score.NewValue.HitEvents; diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 6846992b13..b33ff87d55 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Play // Token request construction should happen post-load to allow derived classes to potentially prepare DI backings that are used to create the request. var tcs = new TaskCompletionSource(); - if (Mods.Value.Any(m => !m.IsPlayable(ModUsage.User))) + if (Mods.Value.Any(m => !m.IsPlayable(ModUsage.SoloLocal))) { handleTokenFailure(new InvalidOperationException("Non-user playable mod selected.")); return false; diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index ee2c00a135..e0ae0309ce 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -146,7 +146,7 @@ namespace osu.Game.Screens.Ranking if (Score != null) { // only show flair / animation when arriving after watching a play that isn't autoplay. - bool shouldFlair = player != null && Score.Mods.All(m => m.IsPlayable(ModUsage.User)); + bool shouldFlair = player != null && Score.Mods.All(m => m.IsPlayable(ModUsage.SoloLocal)); ScorePanelList.AddScore(Score, shouldFlair); } diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index c5cf469f7c..e19020a959 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -121,7 +121,7 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidRequiredModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerRoomWide), out invalidMods); + => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerGlobal), out invalidMods); /// /// Checks that all s in a combination are valid as "free mods" in a multiplayer match session. @@ -130,7 +130,7 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidFreeModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerPerPlayer), out invalidMods); + => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerLocal), out invalidMods); private static bool checkValid(IEnumerable mods, Predicate valid, [NotNullWhen(false)] out List? invalidMods) { From 8f04db5df5136f9431e62a6fffce273f2ba8d6bc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 May 2022 17:21:19 +0300 Subject: [PATCH 0755/1276] Bring back behaviour of checking incompatibility on gameplay validity --- osu.Game.Tests/Mods/ModUtilsTest.cs | 12 ++++++++++++ osu.Game/OsuGame.cs | 15 +++++---------- osu.Game/Utils/ModUtils.cs | 15 +++++++++++---- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index e858abfe44..4c126f0a3b 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -134,6 +134,18 @@ namespace osu.Game.Tests.Mods private static readonly object[] invalid_mod_test_scenarios = { + // incompatible pair. + new object[] + { + new Mod[] { new OsuModDoubleTime(), new OsuModHalfTime() }, + new[] { typeof(OsuModDoubleTime), typeof(OsuModHalfTime) } + }, + // incompatible pair with derived class. + new object[] + { + new Mod[] { new OsuModNightcore(), new OsuModHalfTime() }, + new[] { typeof(OsuModNightcore), typeof(OsuModHalfTime) } + }, // system mod. new object[] { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 29985b0893..e9fe8c43de 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -577,16 +577,11 @@ namespace osu.Game if (SelectedMods.Disabled) return; - var validMods = mods.NewValue; - - if (!ModUtils.CheckCompatibleSet(validMods, out var incompatible)) - validMods = validMods.Except(incompatible).ToArray(); - - if (!ModUtils.CheckValidForGameplay(validMods, out var invalid)) - validMods = validMods.Except(invalid).ToArray(); - - // ensure we always have a valid set of mods. - SelectedMods.Value = validMods; + if (!ModUtils.CheckValidForGameplay(mods.NewValue, out var invalid)) + { + // ensure we always have a valid set of mods. + SelectedMods.Value = mods.NewValue.Except(invalid).ToArray(); + } } #endregion diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index e19020a959..c18125054a 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -112,7 +112,7 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidForGameplay(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && !(m is MultiMod), out invalidMods); + => checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && !(m is MultiMod), out invalidMods, true); /// /// Checks that all s in a combination are valid as "required mods" in a multiplayer match session. @@ -121,7 +121,7 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidRequiredModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerGlobal), out invalidMods); + => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerGlobal), out invalidMods, true); /// /// Checks that all s in a combination are valid as "free mods" in a multiplayer match session. @@ -130,13 +130,20 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidFreeModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerLocal), out invalidMods); + => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerLocal), out invalidMods, false); - private static bool checkValid(IEnumerable mods, Predicate valid, [NotNullWhen(false)] out List? invalidMods) + private static bool checkValid(IEnumerable mods, Predicate valid, [NotNullWhen(false)] out List? invalidMods, bool checkCompatibility) { mods = mods.ToArray(); invalidMods = null; + if (checkCompatibility) + { + // exclude multi mods from compatibility checks. + // the loop below automatically marks all multi mods as not valid for gameplay anyway. + CheckCompatibleSet(mods.Where(m => !(m is MultiMod)), out invalidMods); + } + foreach (var mod in mods) { if (!valid(mod)) From 8488a29e9e4db0dd26ab6ec2ecacd24681b764b2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 May 2022 17:22:11 +0300 Subject: [PATCH 0756/1276] Renew obsoletion date --- osu.Game/Rulesets/Mods/IMod.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index d37ef53a66..c0d4aa5c9f 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Mods /// Whether this mod is playable by an end user. /// Should be false for cases where the user is not interacting with the game (so it can be excluded from multiplayer selection, for example). /// - [Obsolete("Override IsPlayable instead.")] // Can be removed 20220918 + [Obsolete("Override IsPlayable instead.")] // Can be removed 20221104 bool UserPlayable { get; } /// diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index debb1a1e48..b2f1b7e24f 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Mods #pragma warning restore 618 [JsonIgnore] - [Obsolete("Override IsPlayable instead.")] // Can be removed 20220918 + [Obsolete("Override IsPlayable instead.")] // Can be removed 20221104 public virtual bool UserPlayable => true; [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override IsPlayable to false.")] // Can be removed 20211009 From d0df9e8051f7580b94e888079557d6cf62018b40 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 May 2022 18:56:27 +0300 Subject: [PATCH 0757/1276] Inline `CheckCompatibleSet` method to avoid ugly boolean flag --- osu.Game/Utils/ModUtils.cs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index c18125054a..40db981ef0 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -112,7 +112,16 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidForGameplay(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && !(m is MultiMod), out invalidMods, true); + { + mods = mods.ToArray(); + + // exclude multi mods from compatibility checks. + // the loop below automatically marks all multi mods as not valid for gameplay anyway. + if (!CheckCompatibleSet(mods.Where(m => !(m is MultiMod)), out invalidMods)) + return false; + + return checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && !(m is MultiMod), out invalidMods); + } /// /// Checks that all s in a combination are valid as "required mods" in a multiplayer match session. @@ -121,7 +130,14 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidRequiredModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerGlobal), out invalidMods, true); + { + mods = mods.ToArray(); + + if (!CheckCompatibleSet(mods, out invalidMods)) + return false; + + return checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerGlobal), out invalidMods); + } /// /// Checks that all s in a combination are valid as "free mods" in a multiplayer match session. @@ -130,20 +146,13 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidFreeModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerLocal), out invalidMods, false); + => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerLocal), out invalidMods); - private static bool checkValid(IEnumerable mods, Predicate valid, [NotNullWhen(false)] out List? invalidMods, bool checkCompatibility) + private static bool checkValid(IEnumerable mods, Predicate valid, [NotNullWhen(false)] out List? invalidMods) { mods = mods.ToArray(); invalidMods = null; - if (checkCompatibility) - { - // exclude multi mods from compatibility checks. - // the loop below automatically marks all multi mods as not valid for gameplay anyway. - CheckCompatibleSet(mods.Where(m => !(m is MultiMod)), out invalidMods); - } - foreach (var mod in mods) { if (!valid(mod)) From a27fcda9f1cee33104a9e6bb1aee1e5ac2bca1ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 01:52:39 +0900 Subject: [PATCH 0758/1276] Fix failing tests due to `CurrentDialog` being unexpectedly not set after `Push` --- .../UserInterface/TestSceneDialogOverlay.cs | 2 -- osu.Game/Overlays/DialogOverlay.cs | 27 +++++++++---------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index aecb3f6c71..54cdeaf956 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -105,8 +105,6 @@ namespace osu.Game.Tests.Visual.UserInterface }); }); - AddAssert("dialog not displayed", () => overlay.CurrentDialog != dialog); - AddStep("complete load", () => ((SlowLoadingDialogOverlay)overlay).LoadEvent.Set()); AddUntilStep("wait for load", () => overlay.IsLoaded); diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 0699c69691..a8c3c392e0 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -47,23 +47,23 @@ namespace osu.Game.Overlays public void Push(PopupDialog dialog) { - if (!IsLoaded) - { - Schedule(() => Push(dialog)); - return; - } - if (dialog == CurrentDialog || dialog.State.Value != Visibility.Visible) return; - // if any existing dialog is being displayed, dismiss it before showing a new one. - CurrentDialog?.Hide(); - + var lastDialog = CurrentDialog; CurrentDialog = dialog; - CurrentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); - dialogContainer.Add(CurrentDialog); + Scheduler.Add(() => + { + // if any existing dialog is being displayed, dismiss it before showing a new one. + lastDialog?.Hide(); - Show(); + CurrentDialog = dialog; + CurrentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); + + dialogContainer.Add(CurrentDialog); + + Show(); + }, false); } public override bool IsPresent => Scheduler.HasPendingTasks || dialogContainer.Children.Count > 0; @@ -96,9 +96,6 @@ namespace osu.Game.Overlays base.PopOut(); this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); - // PopOut is called as part of VisibilityContainer's initialisation logic, but we don't want it to interact with a potentially waiting dialog. - if (!IsLoaded) return; - lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 100, Easing.InCubic); if (CurrentDialog?.State.Value == Visibility.Visible) CurrentDialog.Hide(); From 81b462262096d38d864be7e1b1bf22cc13e75eb7 Mon Sep 17 00:00:00 2001 From: Supersonicboss1 Date: Wed, 4 May 2022 22:25:34 +0100 Subject: [PATCH 0759/1276] fixed autoplay not showing compat, + relax compat --- osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs index dfa28a537a..0832cfb545 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Acronym => @"AL"; public override string Description => @"Don't use the same key twice in a row!"; public override double ScoreMultiplier => 1.0; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay) }; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax) }; public override ModType Type => ModType.Conversion; public override IconUsage? Icon => FontAwesome.Solid.Keyboard; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs index b31ef5d2fd..507b3588bd 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModAutoplay : ModAutoplay { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate) }).ToArray(); public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" }); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs b/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs index 5b42772358..99d7535957 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModCinema : ModCinema { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate) }).ToArray(); public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" }); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 6b81efdca6..5f37c6a0ae 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset, IApplicableToPlayer { public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised), typeof(OsuModAlternate) }).ToArray(); /// /// How early before a hitobject's start time to trigger a hit. From 464b3af5f37b23204c3c440d4d0359b670527384 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 05:42:20 +0300 Subject: [PATCH 0760/1276] Rename local correctly --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs index 18cf454be3..d1b83c3749 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs @@ -220,18 +220,18 @@ namespace osu.Game.Screens.OnlinePlay if (SelectedItem.Disabled) return; - var visibleRooms = ListContainer.AsEnumerable().Where(r => r.IsPresent); + var visibleItems = ListContainer.AsEnumerable().Where(r => r.IsPresent); PlaylistItem item; if (SelectedItem.Value == null) - item = visibleRooms.FirstOrDefault()?.Model; + item = visibleItems.FirstOrDefault()?.Model; else { if (direction < 0) - visibleRooms = visibleRooms.Reverse(); + visibleItems = visibleItems.Reverse(); - item = visibleRooms.SkipWhile(r => r.Model != SelectedItem.Value).Skip(1).FirstOrDefault()?.Model; + item = visibleItems.SkipWhile(r => r.Model != SelectedItem.Value).Skip(1).FirstOrDefault()?.Model; } // we already have a valid selection only change selection if we still have a room to switch to. From f28978b85661ea1c878c27864788e0d65e68de57 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 05:42:49 +0300 Subject: [PATCH 0761/1276] Handle against playlists which disallow selection `SelectedItem.Disabled` is also not checked against in the select-via-click flow inside `DrawableRoomPlaylistItem` (only `AllowSelection` is checked). --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 9 +++++++++ osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 566a8db608..d520384218 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -56,6 +56,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("click", () => InputManager.Click(MouseButton.Left)); AddAssert("no item selected", () => playlist.SelectedItem.Value == null); + + AddStep("press down", () => InputManager.Key(Key.Down)); + AddAssert("no item selected", () => playlist.SelectedItem.Value == null); } [Test] @@ -73,6 +76,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("click", () => InputManager.Click(MouseButton.Left)); AddAssert("no item selected", () => playlist.SelectedItem.Value == null); + + AddStep("press down", () => InputManager.Key(Key.Down)); + AddAssert("no item selected", () => playlist.SelectedItem.Value == null); } [Test] @@ -91,6 +97,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("click", () => InputManager.Click(MouseButton.Left)); AddAssert("no item selected", () => playlist.SelectedItem.Value == null); + + AddStep("press down", () => InputManager.Key(Key.Down)); + AddAssert("no item selected", () => playlist.SelectedItem.Value == null); } [Test] diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs index d1b83c3749..840fc48613 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs @@ -217,7 +217,7 @@ namespace osu.Game.Screens.OnlinePlay private void selectNext(int direction) { - if (SelectedItem.Disabled) + if (!AllowSelection) return; var visibleItems = ListContainer.AsEnumerable().Where(r => r.IsPresent); From a26793cd654a29ada9d9d0234e796f531cd404c8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 06:12:24 +0300 Subject: [PATCH 0762/1276] Add missing `Test` prefix --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index d520384218..e2b4b2870f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void KeyboardSelection() + public void TestKeyboardSelection() { createPlaylist(p => p.AllowSelection = true); From e3c7c5d0b9ceca3ea19683280321c66e08155c42 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 08:12:54 +0300 Subject: [PATCH 0763/1276] Improve validity methods to include system, non-implemented, and multi mods --- osu.Game/Utils/ModUtils.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 40db981ef0..f11a06e3be 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -115,12 +115,15 @@ namespace osu.Game.Utils { mods = mods.ToArray(); - // exclude multi mods from compatibility checks. - // the loop below automatically marks all multi mods as not valid for gameplay anyway. - if (!CheckCompatibleSet(mods.Where(m => !(m is MultiMod)), out invalidMods)) + // checking compatibility of multi mods would try to flatten them and return incompatible mods. + // in gameplay context, we never want MultiMod selected in the first place, therefore check against it first. + if (!checkValid(mods, m => !(m is MultiMod), out invalidMods)) return false; - return checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && !(m is MultiMod), out invalidMods); + if (!CheckCompatibleSet(mods, out invalidMods)) + return false; + + return checkValid(mods, m => m.Type != ModType.System && m.HasImplementation, out invalidMods); } /// @@ -133,10 +136,15 @@ namespace osu.Game.Utils { mods = mods.ToArray(); + // checking compatibility of multi mods would try to flatten them and return incompatible mods. + // in gameplay context, we never want MultiMod selected in the first place, therefore check against it first. + if (!checkValid(mods, m => !(m is MultiMod), out invalidMods)) + return false; + if (!CheckCompatibleSet(mods, out invalidMods)) return false; - return checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerGlobal), out invalidMods); + return checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && m.IsPlayable(ModUsage.MultiplayerGlobal), out invalidMods); } /// @@ -146,7 +154,7 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidFreeModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.IsPlayable(ModUsage.MultiplayerLocal), out invalidMods); + => checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && m.IsPlayable(ModUsage.MultiplayerLocal) && !(m is MultiMod), out invalidMods); private static bool checkValid(IEnumerable mods, Predicate valid, [NotNullWhen(false)] out List? invalidMods) { From 86aa2125fe9878eda71857a0e1d0ca0823ab2899 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 08:13:29 +0300 Subject: [PATCH 0764/1276] Add test coverage for multiplayer mod validity methods --- osu.Game.Tests/Mods/ModUtilsTest.cs | 161 ++++++++++++++++++++++++++-- 1 file changed, 153 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index 4c126f0a3b..2447233d3c 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -137,33 +137,137 @@ namespace osu.Game.Tests.Mods // incompatible pair. new object[] { - new Mod[] { new OsuModDoubleTime(), new OsuModHalfTime() }, - new[] { typeof(OsuModDoubleTime), typeof(OsuModHalfTime) } + new Mod[] { new OsuModHidden(), new OsuModApproachDifferent() }, + new[] { typeof(OsuModHidden), typeof(OsuModApproachDifferent) } }, // incompatible pair with derived class. new object[] { - new Mod[] { new OsuModNightcore(), new OsuModHalfTime() }, - new[] { typeof(OsuModNightcore), typeof(OsuModHalfTime) } + new Mod[] { new OsuModDeflate(), new OsuModApproachDifferent() }, + new[] { typeof(OsuModDeflate), typeof(OsuModApproachDifferent) } }, // system mod. new object[] { - new Mod[] { new OsuModDoubleTime(), new OsuModTouchDevice() }, + new Mod[] { new OsuModHidden(), new OsuModTouchDevice() }, new[] { typeof(OsuModTouchDevice) } }, // multi mod. new object[] { - new Mod[] { new MultiMod(new OsuModHalfTime()), new OsuModDaycore() }, + new Mod[] { new MultiMod(new OsuModSuddenDeath(), new OsuModPerfect()) }, new[] { typeof(MultiMod) } }, + // invalid multiplayer mod is valid for local. + new object[] + { + new Mod[] { new OsuModHidden(), new InvalidMultiplayerMod() }, + null + }, + // invalid free mod is valid for local. + new object[] + { + new Mod[] { new OsuModHidden(), new InvalidMultiplayerFreeMod() }, + null + }, // valid pair. new object[] { - new Mod[] { new OsuModDoubleTime(), new OsuModHardRock() }, + new Mod[] { new OsuModHidden(), new OsuModHardRock() }, null - } + }, + }; + + private static readonly object[] invalid_multiplayer_mod_test_scenarios = + { + // incompatible pair. + new object[] + { + new Mod[] { new OsuModHidden(), new OsuModApproachDifferent() }, + new[] { typeof(OsuModHidden), typeof(OsuModApproachDifferent) } + }, + // incompatible pair with derived class. + new object[] + { + new Mod[] { new OsuModDeflate(), new OsuModApproachDifferent() }, + new[] { typeof(OsuModDeflate), typeof(OsuModApproachDifferent) } + }, + // system mod. + new object[] + { + new Mod[] { new OsuModHidden(), new OsuModTouchDevice() }, + new[] { typeof(OsuModTouchDevice) } + }, + // multi mod. + new object[] + { + new Mod[] { new MultiMod(new OsuModSuddenDeath(), new OsuModPerfect()) }, + new[] { typeof(MultiMod) } + }, + // invalid multiplayer mod. + new object[] + { + new Mod[] { new OsuModHidden(), new InvalidMultiplayerMod() }, + new[] { typeof(InvalidMultiplayerMod) } + }, + // invalid free mod is valid for multiplayer global. + new object[] + { + new Mod[] { new OsuModHidden(), new InvalidMultiplayerFreeMod() }, + null + }, + // valid pair. + new object[] + { + new Mod[] { new OsuModHidden(), new OsuModHardRock() }, + null + }, + }; + + private static readonly object[] invalid_free_mod_test_scenarios = + { + // system mod. + new object[] + { + new Mod[] { new OsuModHidden(), new OsuModTouchDevice() }, + new[] { typeof(OsuModTouchDevice) } + }, + // multi mod. + new object[] + { + new Mod[] { new MultiMod(new OsuModSuddenDeath(), new OsuModPerfect()) }, + new[] { typeof(MultiMod) } + }, + // invalid multiplayer mod. + new object[] + { + new Mod[] { new OsuModHidden(), new InvalidMultiplayerMod() }, + new[] { typeof(InvalidMultiplayerMod) } + }, + // invalid free mod. + new object[] + { + new Mod[] { new OsuModHidden(), new InvalidMultiplayerFreeMod() }, + new[] { typeof(InvalidMultiplayerFreeMod) } + }, + // incompatible pair is valid for free mods. + new object[] + { + new Mod[] { new OsuModHidden(), new OsuModApproachDifferent() }, + null, + }, + // incompatible pair with derived class is valid for free mods. + new object[] + { + new Mod[] { new OsuModDeflate(), new OsuModSpinIn() }, + null, + }, + // valid pair. + new object[] + { + new Mod[] { new OsuModHidden(), new OsuModHardRock() }, + null + }, }; [TestCaseSource(nameof(invalid_mod_test_scenarios))] @@ -179,6 +283,32 @@ namespace osu.Game.Tests.Mods Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid)); } + [TestCaseSource(nameof(invalid_multiplayer_mod_test_scenarios))] + public void TestInvalidMultiplayerModScenarios(Mod[] inputMods, Type[] expectedInvalid) + { + bool isValid = ModUtils.CheckValidRequiredModsForMultiplayer(inputMods, out var invalid); + + Assert.That(isValid, Is.EqualTo(expectedInvalid == null)); + + if (isValid) + Assert.IsNull(invalid); + else + Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid)); + } + + [TestCaseSource(nameof(invalid_free_mod_test_scenarios))] + public void TestInvalidFreeModScenarios(Mod[] inputMods, Type[] expectedInvalid) + { + bool isValid = ModUtils.CheckValidFreeModsForMultiplayer(inputMods, out var invalid); + + Assert.That(isValid, Is.EqualTo(expectedInvalid == null)); + + if (isValid) + Assert.IsNull(invalid); + else + Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid)); + } + public abstract class CustomMod1 : Mod, IModCompatibilitySpecification { } @@ -187,6 +317,21 @@ namespace osu.Game.Tests.Mods { } + public class InvalidMultiplayerMod : Mod + { + public override string Name => string.Empty; + public override string Description => string.Empty; + public override string Acronym => string.Empty; + public override double ScoreMultiplier => 1; + public override bool HasImplementation => true; + public override bool IsPlayable(ModUsage usage) => usage == ModUsage.SoloLocal; + } + + private class InvalidMultiplayerFreeMod : InvalidMultiplayerMod + { + public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerLocal; + } + public interface IModCompatibilitySpecification { } From 326ff3b6e9998d1e67cc41f2fa26c70eca456e4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 14:55:05 +0900 Subject: [PATCH 0765/1276] Fix `CurrentDialog` being updated twice, unsafe variable access --- osu.Game/Overlays/DialogOverlay.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index a8c3c392e0..c46f36b4df 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -50,17 +50,17 @@ namespace osu.Game.Overlays if (dialog == CurrentDialog || dialog.State.Value != Visibility.Visible) return; var lastDialog = CurrentDialog; + + // Immediately update the externally accessible property as this may be used for checks even before + // a DialogOverlay instance has finished loading. CurrentDialog = dialog; Scheduler.Add(() => { // if any existing dialog is being displayed, dismiss it before showing a new one. lastDialog?.Hide(); - - CurrentDialog = dialog; - CurrentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); - - dialogContainer.Add(CurrentDialog); + dialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); + dialogContainer.Add(dialog); Show(); }, false); From f6a6a11391e934973b3449cec07700c5421d11b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 15:02:23 +0900 Subject: [PATCH 0766/1276] Remove `DialogOverlay` local fades (is already managed by `PopupDialog`s themselves) --- osu.Game/Overlays/DialogOverlay.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index c46f36b4df..5a69562e82 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -87,18 +87,16 @@ namespace osu.Game.Overlays protected override void PopIn() { base.PopIn(); - this.FadeIn(PopupDialog.ENTER_DURATION, Easing.OutQuint); lowPassFilter.CutoffTo(300, 100, Easing.OutCubic); } protected override void PopOut() { base.PopOut(); - this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); - lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 100, Easing.InCubic); - if (CurrentDialog?.State.Value == Visibility.Visible) CurrentDialog.Hide(); + if (CurrentDialog?.State.Value == Visibility.Visible) + CurrentDialog.Hide(); } public override bool OnPressed(KeyBindingPressEvent e) From cbc58c67bfd32b0631089afd0b7ee89c0ca01ea3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 15:16:01 +0900 Subject: [PATCH 0767/1276] Remove weird strict tracking icon and reword description to explain what the mod does --- osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs index ab45e5192d..22ace52c2a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using System.Threading; -using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; @@ -23,9 +22,8 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => @"Strict Tracking"; public override string Acronym => @"ST"; - public override IconUsage? Icon => FontAwesome.Solid.PenFancy; public override ModType Type => ModType.DifficultyIncrease; - public override string Description => @"Follow circles just got serious..."; + public override string Description => @"Once you start a slider, follow precisely or get a miss."; public override double ScoreMultiplier => 1.0; public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTarget) }; From f6fc926f1ade6fb5d1366edd4dd2f6632ec83913 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 15:58:21 +0900 Subject: [PATCH 0768/1276] Add xmldoc and rename methods in `IPositionSnapProvider` for legibility --- osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs | 4 ++-- .../Editor/TestSceneManiaBeatSnapGrid.cs | 4 ++-- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 6 +++--- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 4 ++-- .../Sliders/Components/PathControlPointVisualiser.cs | 2 +- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 8 ++++---- .../Visual/Editing/TestSceneDistanceSnapGrid.cs | 4 ++-- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 6 +++--- osu.Game/Rulesets/Edit/IPositionSnapProvider.cs | 10 +++++++--- .../Edit/Compose/Components/BlueprintContainer.cs | 4 ++-- .../Compose/Components/ComposeBlueprintContainer.cs | 2 +- .../Edit/Compose/Components/Timeline/Timeline.cs | 4 ++-- .../Components/Timeline/TimelineHitObjectBlueprint.cs | 2 +- 13 files changed, 32 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 9ff6d10a49..630a2cf645 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -89,9 +89,9 @@ namespace osu.Game.Rulesets.Catch.Edit new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }) }); - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) { - var result = base.SnapScreenSpacePositionToValidTime(screenSpacePosition); + var result = base.FindSnappedPositionAndTime(screenSpacePosition); result.ScreenSpacePosition.X = screenSpacePosition.X; if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(result.ScreenSpacePosition) is SnapResult snapResult && diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs index 2e55f86bb6..4bb049b1a4 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs @@ -97,12 +97,12 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor set => InternalChild = value; } - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) { throw new System.NotImplementedException(); } - public override SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPosition(Vector2 screenSpacePosition) { throw new System.NotImplementedException(); } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 2baec95c94..fef315e2ef 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -56,9 +56,9 @@ namespace osu.Game.Rulesets.Mania.Edit protected override Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => Playfield.GetColumnByPosition(screenSpacePosition); - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) { - var result = base.SnapScreenSpacePositionToValidTime(screenSpacePosition); + var result = base.FindSnappedPositionAndTime(screenSpacePosition); switch (ScrollingInfo.Direction.Value) { @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Mania.Edit } else { - var result = SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position); + var result = FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position); if (result.Time is double time) beatSnapGrid.SelectionTimeRange = (time, time); else diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 2ba30c5f74..27381d9d55 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -182,10 +182,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private class SnapProvider : IDistanceSnapProvider { - public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => + public SnapResult FindSnappedPosition(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, null); - public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); + public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); public IBindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 5de614722f..e71bde7357 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -255,7 +255,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { // Special handling for selections containing head control point - the position of the slider changes which means the snapped position and time have to be taken into account Vector2 newHeadPosition = Parent.ToScreenSpace(e.MousePosition + (dragStartPositions[0] - dragStartPositions[draggedControlPointIndex])); - var result = snapProvider?.SnapScreenSpacePositionToValidTime(newHeadPosition); + var result = snapProvider?.FindSnappedPositionAndTime(newHeadPosition); Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? newHeadPosition) - slider.Position; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index ba7c6e9d33..02beb0f2a4 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -123,7 +123,7 @@ namespace osu.Game.Rulesets.Osu.Edit } } - public override SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPosition(Vector2 screenSpacePosition) { if (snapToVisibleBlueprints(screenSpacePosition, out var snapResult)) return snapResult; @@ -131,9 +131,9 @@ namespace osu.Game.Rulesets.Osu.Edit return new SnapResult(screenSpacePosition, null); } - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) { - var positionSnap = SnapScreenSpacePositionToValidPosition(screenSpacePosition); + var positionSnap = FindSnappedPosition(screenSpacePosition); if (positionSnap.ScreenSpacePosition != screenSpacePosition) return positionSnap; @@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Edit return new SnapResult(rectangularPositionSnapGrid.ToScreenSpace(pos), null, PlayfieldAtScreenSpacePosition(screenSpacePosition)); } - return base.SnapScreenSpacePositionToValidTime(screenSpacePosition); + return base.FindSnappedPositionAndTime(screenSpacePosition); } private bool snapToVisibleBlueprints(Vector2 screenSpacePosition, out SnapResult snapResult) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index b9cfa84a5d..1be4ac2eab 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -162,10 +162,10 @@ namespace osu.Game.Tests.Visual.Editing private class SnapProvider : IDistanceSnapProvider { - public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => + public SnapResult FindSnappedPosition(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, null); - public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); + public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); public IBindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1c388fe68f..e0cfd5c74a 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -362,7 +362,7 @@ namespace osu.Game.Rulesets.Edit /// The most relevant . protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield; - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) { var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition); double? targetTime = null; @@ -416,9 +416,9 @@ namespace osu.Game.Rulesets.Edit #region IPositionSnapProvider - public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); + public abstract SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition); - public virtual SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => + public virtual SnapResult FindSnappedPosition(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, null); #endregion diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index 8a179ed424..8b6dbb6af3 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -5,23 +5,27 @@ using osuTK; namespace osu.Game.Rulesets.Edit { + /// + /// A snap provider which given the position of a hit object, offers a more correct position and time value inferred from the context of the beatmap. + /// Provided values are done so in an isolated context, without consideration of other nearby hit objects. + /// public interface IPositionSnapProvider { /// /// Given a position, find a valid time and position snap. /// /// - /// This call should be equivalent to running with any additional logic that can be performed without the time immutability restriction. + /// This call should be equivalent to running with any additional logic that can be performed without the time immutability restriction. /// /// The screen-space position to be snapped. /// The time and position post-snapping. - SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); + SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition); /// /// Given a position, find a value position snap, restricting time to its input value. /// /// The screen-space position to be snapped. /// The position post-snapping. Time will always be null. - SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition); + SnapResult FindSnappedPosition(Vector2 screenSpacePosition); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 6dc6f20cfe..186c66e0af 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -486,7 +486,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Vector2 originalPosition = movementBlueprintOriginalPositions[i]; var testPosition = originalPosition + distanceTravelled; - var positionalResult = snapProvider.SnapScreenSpacePositionToValidPosition(testPosition); + var positionalResult = snapProvider.FindSnappedPosition(testPosition); if (positionalResult.ScreenSpacePosition == testPosition) continue; @@ -505,7 +505,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Vector2 movePosition = movementBlueprintOriginalPositions.First() + distanceTravelled; // Retrieve a snapped position. - var result = snapProvider?.SnapScreenSpacePositionToValidTime(movePosition); + var result = snapProvider?.FindSnappedPositionAndTime(movePosition); if (result == null) { diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 79b38861ee..68be20720d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -214,7 +214,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updatePlacementPosition() { - var snapResult = Composer.SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position); + var snapResult = Composer.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position); // if no time was found from positional snapping, we should still quantize to the beat. snapResult.Time ??= Beatmap.SnapTime(EditorClock.CurrentTime, null); diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index c2b2bdb861..56ff972801 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -307,10 +307,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// public double VisibleRange => track.Length / Zoom; - public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => + public SnapResult FindSnappedPosition(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, null); - public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => + public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition)))); private double getTimeFromPosition(Vector2 localPosition) => diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 6426d33e99..33ea137d51 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -382,7 +382,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { OnDragHandled?.Invoke(e); - if (timeline.SnapScreenSpacePositionToValidTime(e.ScreenSpaceMousePosition).Time is double time) + if (timeline.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition).Time is double time) { switch (hitObject) { From 1fce0da33189907d8c14588af5b35dd8b5efb13a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 16:04:34 +0900 Subject: [PATCH 0769/1276] Reword slightly, to allow better conformity with `IDistanceSnapProvider` --- osu.Game/Rulesets/Edit/IPositionSnapProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index 8b6dbb6af3..c6a365938c 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -6,8 +6,8 @@ using osuTK; namespace osu.Game.Rulesets.Edit { /// - /// A snap provider which given the position of a hit object, offers a more correct position and time value inferred from the context of the beatmap. - /// Provided values are done so in an isolated context, without consideration of other nearby hit objects. + /// A snap provider which given a proposed position for a hit object, potentially offers a more correct position and time value inferred from the context of the beatmap. + /// Provided values are inferred in an isolated context, without consideration of other nearby hit objects. /// public interface IPositionSnapProvider { From ac6342ff8d22213ac41a2df547ab69c816641a21 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 10:16:16 +0300 Subject: [PATCH 0770/1276] Add workaround for item scrolling issue --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs index 840fc48613..2a72fc6eb1 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs @@ -190,7 +190,12 @@ namespace osu.Game.Screens.OnlinePlay if (SelectedItem.Value == null || !ItemMap.TryGetValue(SelectedItem.Value, out var drawableItem)) return; - ScrollContainer.ScrollIntoView(drawableItem); + // ScrollIntoView does not handle non-loaded items appropriately, delay scroll until the item finishes loading. + // see: https://github.com/ppy/osu-framework/issues/5158 + if (!drawableItem.IsLoaded) + drawableItem.OnLoadComplete += _ => ScrollContainer.ScrollIntoView(drawableItem); + else + ScrollContainer.ScrollIntoView(drawableItem); } #region Key selection logic (shared with BeatmapCarousel and RoomsContainer) From 6227e3f876bfd9d6fceebb5e2f7a8a276fdcccf6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 16:17:37 +0900 Subject: [PATCH 0771/1276] Add comprehensive documentation of `BeatmapInfo.DistanceSpacing` --- osu.Game/Beatmaps/BeatmapInfo.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 1a9703f478..abc9020dc6 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -1,21 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Linq; using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.Testing; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Database; using osu.Game.Models; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Rulesets; +using osu.Game.Rulesets.Edit; using osu.Game.Scoring; using Realms; -#nullable enable - namespace osu.Game.Beatmaps { /// @@ -109,6 +111,16 @@ namespace osu.Game.Beatmaps public bool SamplesMatchPlaybackRate { get; set; } = true; + /// + /// The ratio of distance travelled per time unit. + /// Generally used to decouple the spacing between hit objects from the enforced "velocity" of the beatmap (see ). + /// + /// + /// The most common method of understanding is that at a default value of 1.0, the time-to-distance ratio will match the slider velocity of the beatmap + /// at the current point in time. Increasing this value will make hit objects more spaced apart when compared to the cursor movement required to track a slider. + /// + /// This is only a hint property, used by the editor in implementations. It does not directly affect the beatmap or gameplay. + /// public double DistanceSpacing { get; set; } = 1.0; public int BeatDivisor { get; set; } From 977e6d8a8081fff93503156670ce034ec54387f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 16:25:05 +0900 Subject: [PATCH 0772/1276] Add xmldoc for `IDistanceSnapProvider` and related properties --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 4 ++-- .../Sliders/SliderPlacementBlueprint.cs | 2 +- ...tSceneHitObjectComposerDistanceSnapping.cs | 4 ++-- .../Editing/TestSceneDistanceSnapGrid.cs | 4 ++-- .../Edit/DistancedHitObjectComposer.cs | 4 ++-- .../Rulesets/Edit/IDistanceSnapProvider.cs | 23 +++++++++++-------- .../Rulesets/Objects/SliderPathExtensions.cs | 2 +- .../Components/CircularDistanceSnapGrid.cs | 2 +- 8 files changed, 25 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 2ba30c5f74..2ea39e47f4 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -195,9 +195,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public double DistanceToDuration(HitObject referenceObject, float distance) => distance; - public double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) => 0; + public double FindSnappedDuration(HitObject referenceObject, float distance) => 0; - public float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) => 0; + public float FindSnappedDistance(HitObject referenceObject, float distance) => 0; } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 73a4ea5434..501589987d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -220,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSlider() { - HitObject.Path.ExpectedDistance.Value = snapProvider?.GetSnappedDistanceFromDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; + HitObject.Path.ExpectedDistance.Value = snapProvider?.FindSnappedDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; bodyPiece.UpdateFrom(HitObject); headCirclePiece.UpdateFrom(HitObject.HeadCircle); diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index a2ee97210a..82fc7a208b 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -213,10 +213,10 @@ namespace osu.Game.Tests.Editing => AddAssert($"distance = {distance} -> duration = {expectedDuration}", () => composer.DistanceToDuration(new HitObject(), distance) == expectedDuration); private void assertSnappedDuration(float distance, double expectedDuration) - => AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.GetSnappedDurationFromDistance(new HitObject(), distance) == expectedDuration); + => AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.FindSnappedDuration(new HitObject(), distance) == expectedDuration); private void assertSnappedDistance(float distance, float expectedDistance) - => AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.GetSnappedDistanceFromDistance(new HitObject(), distance) == expectedDistance); + => AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.FindSnappedDistance(new HitObject(), distance) == expectedDistance); private class TestHitObjectComposer : OsuHitObjectComposer { diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index b9cfa84a5d..7cb1e8f36a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -175,9 +175,9 @@ namespace osu.Game.Tests.Visual.Editing public double DistanceToDuration(HitObject referenceObject, float distance) => distance; - public double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) => 0; + public double FindSnappedDuration(HitObject referenceObject, float distance) => 0; - public float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) => 0; + public float FindSnappedDistance(HitObject referenceObject, float distance) => 0; } } } diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 680fc9aaa8..2280211421 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -146,10 +146,10 @@ namespace osu.Game.Rulesets.Edit return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength; } - public virtual double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) + public virtual double FindSnappedDuration(HitObject referenceObject, float distance) => BeatSnapProvider.SnapTime(referenceObject.StartTime + DistanceToDuration(referenceObject, distance), referenceObject.StartTime) - referenceObject.StartTime; - public virtual float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) + public virtual float FindSnappedDistance(HitObject referenceObject, float distance) { double startTime = referenceObject.StartTime; diff --git a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs index c6e866561e..8e4e9afb1c 100644 --- a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs @@ -7,10 +7,13 @@ using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Edit { + /// + /// A snap provider which given a reference hit object and proposed distance from it, offers a more correct duration or distance value. + /// public interface IDistanceSnapProvider : IPositionSnapProvider { /// - /// The spacing multiplier applied to beat snap distances. + /// A multiplier which changes the ratio of distance travelled per time unit. /// /// IBindable DistanceSpacingMultiplier { get; } @@ -23,7 +26,7 @@ namespace osu.Game.Rulesets.Edit float GetBeatSnapDistanceAt(HitObject referenceObject); /// - /// Converts a duration to a distance. + /// Converts a duration to a distance without applying any snapping. /// /// An object to be used as a reference point for this operation. /// The duration to convert. @@ -31,7 +34,7 @@ namespace osu.Game.Rulesets.Edit float DurationToDistance(HitObject referenceObject, double duration); /// - /// Converts a distance to a duration. + /// Converts a distance to a duration without applying any snapping. /// /// An object to be used as a reference point for this operation. /// The distance to convert. @@ -39,20 +42,22 @@ namespace osu.Game.Rulesets.Edit double DistanceToDuration(HitObject referenceObject, float distance); /// - /// Converts a distance to a snapped duration. + /// Given a distance from the provided hit object, find the valid snapped duration. /// /// An object to be used as a reference point for this operation. /// The distance to convert. /// A value that represents as a duration snapped to the closest beat of the timing point. - double GetSnappedDurationFromDistance(HitObject referenceObject, float distance); + double FindSnappedDuration(HitObject referenceObject, float distance); /// - /// Converts an unsnapped distance to a snapped distance. - /// The returned distance will always be floored (as to never exceed the provided . + /// Given a distance from the provided hit object, find the valid snapped distance. /// /// An object to be used as a reference point for this operation. /// The distance to convert. - /// A value that represents snapped to the closest beat of the timing point. - float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance); + /// + /// A value that represents snapped to the closest beat of the timing point. + /// The distance will always be less than or equal to the provided . + /// + float FindSnappedDistance(HitObject referenceObject, float distance); } } diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index 3100d26a55..dd418a1b7b 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Objects public static void SnapTo(this THitObject hitObject, IDistanceSnapProvider? snapProvider) where THitObject : HitObject, IHasPath { - hitObject.Path.ExpectedDistance.Value = snapProvider?.GetSnappedDistanceFromDistance(hitObject, (float)hitObject.Path.CalculatedDistance) ?? hitObject.Path.CalculatedDistance; + hitObject.Path.ExpectedDistance.Value = snapProvider?.FindSnappedDistance(hitObject, (float)hitObject.Path.CalculatedDistance) ?? hitObject.Path.CalculatedDistance; } /// diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 6b32ff96c4..50d5f0389a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Vector2 normalisedDirection = direction * new Vector2(1f / distance); Vector2 snappedPosition = StartPosition + normalisedDirection * radialCount * radius; - return (snappedPosition, StartTime + SnapProvider.GetSnappedDurationFromDistance(ReferenceObject, (snappedPosition - StartPosition).Length)); + return (snappedPosition, StartTime + SnapProvider.FindSnappedDuration(ReferenceObject, (snappedPosition - StartPosition).Length)); } } } From b411b59006da5cfbb5d39cd0e0754db943b3f298 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 16:32:14 +0900 Subject: [PATCH 0773/1276] Move `IPlacementHandler` caching to interface --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 1 - osu.Game/Screens/Edit/Compose/IPlacementHandler.cs | 2 ++ osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1c388fe68f..fdc12bf549 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -35,7 +35,6 @@ namespace osu.Game.Rulesets.Edit /// Responsible for providing snapping and generally gluing components together. /// /// The base type of supported objects. - [Cached(Type = typeof(IPlacementHandler))] public abstract class HitObjectComposer : HitObjectComposer, IPlacementHandler where TObject : HitObject { diff --git a/osu.Game/Screens/Edit/Compose/IPlacementHandler.cs b/osu.Game/Screens/Edit/Compose/IPlacementHandler.cs index aefcbc6542..59eb13cae5 100644 --- a/osu.Game/Screens/Edit/Compose/IPlacementHandler.cs +++ b/osu.Game/Screens/Edit/Compose/IPlacementHandler.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Edit.Compose { + [Cached] public interface IPlacementHandler { /// diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index b2f5b1754f..b981a31bd1 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -14,7 +14,6 @@ using osu.Game.Screens.Edit.Compose; namespace osu.Game.Tests.Visual { - [Cached(Type = typeof(IPlacementHandler))] public abstract class PlacementBlueprintTestScene : OsuManualInputManagerTestScene, IPlacementHandler { protected readonly Container HitObjectContainer; From 1c6a233cc0daba2519a3ba54609ebabea6c7c637 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 16:35:52 +0900 Subject: [PATCH 0774/1276] Move snap provider caching to interfaces --- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 1 - osu.Game/Rulesets/Edit/HitObjectComposer.cs | 1 - osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs | 2 ++ osu.Game/Rulesets/Edit/IPositionSnapProvider.cs | 2 ++ osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs | 1 - 5 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 680fc9aaa8..7a997bb423 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -23,7 +23,6 @@ namespace osu.Game.Rulesets.Edit /// Represents a for rulesets with the concept of distances between objects. /// /// The base type of supported objects. - [Cached(typeof(IDistanceSnapProvider))] public abstract class DistancedHitObjectComposer : HitObjectComposer, IDistanceSnapProvider, IScrollBindingHandler where TObject : HitObject { diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index fdc12bf549..c384eb4ea9 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -388,7 +388,6 @@ namespace osu.Game.Rulesets.Edit /// Generally used to access certain methods without requiring a generic type for . /// [Cached(typeof(HitObjectComposer))] - [Cached(typeof(IPositionSnapProvider))] public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider { protected HitObjectComposer() diff --git a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs index c6e866561e..7850edf3f1 100644 --- a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs @@ -1,12 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Edit { + [Cached] public interface IDistanceSnapProvider : IPositionSnapProvider { /// diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index 8a179ed424..d7379b26ce 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osuTK; namespace osu.Game.Rulesets.Edit { + [Cached] public interface IPositionSnapProvider { /// diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index c2b2bdb861..f91b321835 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -19,7 +19,6 @@ using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - [Cached(typeof(IPositionSnapProvider))] [Cached] public class Timeline : ZoomableScrollContainer, IPositionSnapProvider { From 5a1ac71d90fdb698d6e8e2a68ef7779faf651acd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 16:42:57 +0900 Subject: [PATCH 0775/1276] Remove unnecessary type specification in `HitObjectComposer`'s caching --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index c384eb4ea9..b97690c9e0 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -387,7 +387,7 @@ namespace osu.Game.Rulesets.Edit /// A non-generic definition of a HitObject composer class. /// Generally used to access certain methods without requiring a generic type for . /// - [Cached(typeof(HitObjectComposer))] + [Cached] public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider { protected HitObjectComposer() From c3d2648f85bd87289cb58a5622038e2f4d44e7f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 17:07:05 +0900 Subject: [PATCH 0776/1276] Reword weird xmldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Rulesets/Edit/IPositionSnapProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index c6a365938c..200005cb11 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Edit SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition); /// - /// Given a position, find a value position snap, restricting time to its input value. + /// Given a position, find a valid position snap, without changing the time value. /// /// The screen-space position to be snapped. /// The position post-snapping. Time will always be null. From de9b3d33ebfba7beefe36f659e3a8aa77ab6a7eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 17:00:36 +0900 Subject: [PATCH 0777/1276] Rename misleading `DistanceSpacing` variable --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 16 +++------------- .../Visual/Editing/TestSceneDistanceSnapGrid.cs | 10 +++++----- .../Components/CircularDistanceSnapGrid.cs | 10 +++++----- .../Edit/Compose/Components/DistanceSnapGrid.cs | 8 ++++---- 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 368166157d..015a922719 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [Cached(typeof(IDistanceSnapProvider))] private readonly SnapProvider snapProvider = new SnapProvider(); - private TestOsuDistanceSnapGrid grid; + private OsuDistanceSnapGrid grid; public TestSceneOsuDistanceSnapGrid() { @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor RelativeSizeAxes = Axes.Both, Colour = Color4.SlateGray }, - grid = new TestOsuDistanceSnapGrid(new HitCircle { Position = grid_position }), + grid = new OsuDistanceSnapGrid(new HitCircle { Position = grid_position }), new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position } }; }); @@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor RelativeSizeAxes = Axes.Both, Colour = Color4.SlateGray }, - grid = new TestOsuDistanceSnapGrid(new HitCircle { Position = grid_position }, new HitCircle { StartTime = 200 }), + grid = new OsuDistanceSnapGrid(new HitCircle { Position = grid_position }, new HitCircle { StartTime = 200 }), new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position } }; }); @@ -170,16 +170,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor } } - private class TestOsuDistanceSnapGrid : OsuDistanceSnapGrid - { - public new float DistanceSpacing => base.DistanceSpacing; - - public TestOsuDistanceSnapGrid(OsuHitObject hitObject, OsuHitObject nextHitObject = null) - : base(hitObject, nextHitObject) - { - } - } - private class SnapProvider : IDistanceSnapProvider { public SnapResult FindSnappedPosition(Vector2 screenSpacePosition) => diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 38c0808a71..cdaa3739b7 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Editing private class TestDistanceSnapGrid : DistanceSnapGrid { - public new float DistanceSpacing => base.DistanceSpacing; + public new float DistanceBetweenTick => base.DistanceBetweenTick; public TestDistanceSnapGrid(double? endTime = null) : base(new HitObject(), grid_position, 0, endTime) @@ -105,7 +105,7 @@ namespace osu.Game.Tests.Visual.Editing int indexFromPlacement = 0; - for (float s = StartPosition.X + DistanceSpacing; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++) + for (float s = StartPosition.X + DistanceBetweenTick; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceBetweenTick, indexFromPlacement++) { AddInternal(new Circle { @@ -118,7 +118,7 @@ namespace osu.Game.Tests.Visual.Editing indexFromPlacement = 0; - for (float s = StartPosition.X - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++) + for (float s = StartPosition.X - DistanceBetweenTick; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceBetweenTick, indexFromPlacement++) { AddInternal(new Circle { @@ -131,7 +131,7 @@ namespace osu.Game.Tests.Visual.Editing indexFromPlacement = 0; - for (float s = StartPosition.Y + DistanceSpacing; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++) + for (float s = StartPosition.Y + DistanceBetweenTick; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceBetweenTick, indexFromPlacement++) { AddInternal(new Circle { @@ -144,7 +144,7 @@ namespace osu.Game.Tests.Visual.Editing indexFromPlacement = 0; - for (float s = StartPosition.Y - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++) + for (float s = StartPosition.Y - DistanceBetweenTick; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceBetweenTick, indexFromPlacement++) { AddInternal(new Circle { diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 50d5f0389a..76e429dbeb 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -30,14 +30,14 @@ namespace osu.Game.Screens.Edit.Compose.Components Position = StartPosition, Width = crosshair_thickness, EdgeSmoothness = new Vector2(1), - Height = Math.Min(crosshair_max_size, DistanceSpacing * 2), + Height = Math.Min(crosshair_max_size, DistanceBetweenTick * 2), }, new Box { Origin = Anchor.Centre, Position = StartPosition, EdgeSmoothness = new Vector2(1), - Width = Math.Min(crosshair_max_size, DistanceSpacing * 2), + Width = Math.Min(crosshair_max_size, DistanceBetweenTick * 2), Height = crosshair_thickness, } }); @@ -45,11 +45,11 @@ namespace osu.Game.Screens.Edit.Compose.Components float dx = Math.Max(StartPosition.X, DrawWidth - StartPosition.X); float dy = Math.Max(StartPosition.Y, DrawHeight - StartPosition.Y); float maxDistance = new Vector2(dx, dy).Length; - int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceSpacing)); + int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceBetweenTick)); for (int i = 0; i < requiredCircles; i++) { - float radius = (i + 1) * DistanceSpacing * 2; + float radius = (i + 1) * DistanceBetweenTick * 2; AddInternal(new CircularProgress { @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Edit.Compose.Components float distance = direction.Length; - float radius = DistanceSpacing; + float radius = DistanceBetweenTick; int radialCount = Math.Clamp((int)MathF.Round(distance / radius), 1, MaxIntervals); Vector2 normalisedDirection = direction * new Vector2(1f / distance); diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 5568c15514..42bb8a813d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// The spacing between each tick of the beat snapping grid. /// - protected float DistanceSpacing { get; private set; } + protected float DistanceBetweenTick { get; private set; } /// /// The maximum number of distance snapping intervals allowed. @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// The position which the grid should start. - /// The first beat snapping tick is located at + away from this point. + /// The first beat snapping tick is located at + away from this point. /// protected readonly Vector2 StartPosition; @@ -92,7 +92,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updateSpacing() { - DistanceSpacing = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * distanceSpacingMultiplier.Value); + DistanceBetweenTick = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * distanceSpacingMultiplier.Value); if (endTime == null) MaxIntervals = int.MaxValue; @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { // +1 is added since a snapped hitobject may have its start time slightly less than the snapped time due to floating point errors double maxDuration = endTime.Value - StartTime + 1; - MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(ReferenceObject, DistanceSpacing)); + MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(ReferenceObject, DistanceBetweenTick)); } gridCache.Invalidate(); From 786c7f14d3d0a2ea1db09dea4528e04a4b53e217 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 17:08:53 +0900 Subject: [PATCH 0778/1276] Expose `DistanceSpacingMultiplier` to distance --- .../Edit/Compose/Components/DistanceSnapGrid.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 42bb8a813d..a907608cbc 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -25,6 +25,8 @@ namespace osu.Game.Screens.Edit.Compose.Components /// protected float DistanceBetweenTick { get; private set; } + protected IBindable DistanceSpacingMultiplier { get; private set; } + /// /// The maximum number of distance snapping intervals allowed. /// @@ -53,8 +55,6 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved] private BindableBeatDivisor beatDivisor { get; set; } - private IBindable distanceSpacingMultiplier; - private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit); private readonly double? endTime; @@ -86,13 +86,13 @@ namespace osu.Game.Screens.Edit.Compose.Components beatDivisor.BindValueChanged(_ => updateSpacing()); - distanceSpacingMultiplier = SnapProvider.DistanceSpacingMultiplier.GetBoundCopy(); - distanceSpacingMultiplier.BindValueChanged(_ => updateSpacing(), true); + DistanceSpacingMultiplier = SnapProvider.DistanceSpacingMultiplier.GetBoundCopy(); + DistanceSpacingMultiplier.BindValueChanged(_ => updateSpacing(), true); } private void updateSpacing() { - DistanceBetweenTick = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * distanceSpacingMultiplier.Value); + DistanceBetweenTick = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * DistanceSpacingMultiplier.Value); if (endTime == null) MaxIntervals = int.MaxValue; From 4c884aea5d8592b810b7549ab8d6966759edf60c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 17:09:14 +0900 Subject: [PATCH 0779/1276] Fix `CircularDistanceSnapGrid` returning an incorrect time value when distance spacing is not 1.0 --- .../Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 76e429dbeb..1c6eb98521 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Vector2 normalisedDirection = direction * new Vector2(1f / distance); Vector2 snappedPosition = StartPosition + normalisedDirection * radialCount * radius; - return (snappedPosition, StartTime + SnapProvider.FindSnappedDuration(ReferenceObject, (snappedPosition - StartPosition).Length)); + return (snappedPosition, StartTime + SnapProvider.FindSnappedDuration(ReferenceObject, (float)((snappedPosition - StartPosition).Length / DistanceSpacingMultiplier.Value))); } } } From 947a68006a6afb4f166053401bc6b01f21e78f14 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 17:50:17 +0900 Subject: [PATCH 0780/1276] Add note about `IDistanceSnapProvider` not multiplying `DistanceSpacing` itself --- osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs index 8c599f8596..b12e1437dc 100644 --- a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs @@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Edit { /// /// A multiplier which changes the ratio of distance travelled per time unit. + /// Importantly, this is provided for manual usage, and not multiplied into any of the methods exposed by this interface. /// /// IBindable DistanceSpacingMultiplier { get; } From b2e9be70a5e3ae76a315fa346356a9348f0a590e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 17:50:33 +0900 Subject: [PATCH 0781/1276] Rewrite `CircularDistanceSnapGrid` snapping implementation to use snap provider --- .../Components/CircularDistanceSnapGrid.cs | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 1c6eb98521..f34fa7328a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -68,19 +68,29 @@ namespace osu.Game.Screens.Edit.Compose.Components if (MaxIntervals == 0) return (StartPosition, StartTime); - Vector2 direction = position - StartPosition; - if (direction == Vector2.Zero) - direction = new Vector2(0.001f, 0.001f); + // This grid implementation factors in the user's distance spacing specification, + // which is usually not considered by an `IDistanceSnapProvider`. + float distanceSpacing = (float)DistanceSpacingMultiplier.Value; - float distance = direction.Length; + Vector2 travelVector = (position - StartPosition); - float radius = DistanceBetweenTick; - int radialCount = Math.Clamp((int)MathF.Round(distance / radius), 1, MaxIntervals); + if (travelVector == Vector2.Zero) + return (StartPosition, StartTime); - Vector2 normalisedDirection = direction * new Vector2(1f / distance); - Vector2 snappedPosition = StartPosition + normalisedDirection * radialCount * radius; + float travelLength = travelVector.Length; - return (snappedPosition, StartTime + SnapProvider.FindSnappedDuration(ReferenceObject, (float)((snappedPosition - StartPosition).Length / DistanceSpacingMultiplier.Value))); + // FindSnappedDistance will always round down, but we want to potentially round upwards. + travelLength += DistanceBetweenTick / 2; + + // When interacting with the resolved snap provider, the distance spacing multiplier should first be removed + // to allow for snapping at a non-multiplied ratio. + float snappedDistance = SnapProvider.FindSnappedDistance(ReferenceObject, travelLength / distanceSpacing); + double snappedTime = StartTime + SnapProvider.DistanceToDuration(ReferenceObject, snappedDistance); + + // The multiplier can then be reapplied to the final position. + Vector2 snappedPosition = StartPosition + travelVector.Normalized() * snappedDistance * distanceSpacing; + + return (snappedPosition, snappedTime); } } } From 67341db0e742e302802a006d21e9c8baa2bba68a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 12:40:02 +0300 Subject: [PATCH 0782/1276] Wrap `BeatmapOnlineLookupQueue` cache request in a task --- osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs index a24b6b315a..46f5b418bd 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs @@ -153,7 +153,7 @@ namespace osu.Game.Beatmaps } }; - cacheDownloadRequest.PerformAsync(); + Task.Run(() => cacheDownloadRequest.PerformAsync()); } private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmapInfo) From 269e15c167dc53a20afd3162e936b578d8fbae4a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 11:42:50 +0300 Subject: [PATCH 0783/1276] Add test coverage of distance spacing multiplier working with distance snap grid --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 12 +++++- .../Editing/TestSceneDistanceSnapGrid.cs | 37 ++++++++++++++++--- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 015a922719..1feb80414a 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -82,6 +82,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep($"set beat divisor = {divisor}", () => beatDivisor.Value = divisor); } + [TestCase(1.0f)] + [TestCase(2.0f)] + [TestCase(0.5f)] + public void TestDistanceSpacing(float multiplier) + { + AddStep($"set beat divisor = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier); + } + [Test] public void TestCursorInCentre() { @@ -177,7 +185,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); - public IBindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); + public Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); + + IBindable IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier; public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index cdaa3739b7..7d8089b435 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -21,8 +21,12 @@ namespace osu.Game.Tests.Visual.Editing public class TestSceneDistanceSnapGrid : EditorClockTestScene { private const double beat_length = 100; + private const int beat_snap_distance = 10; + private static readonly Vector2 grid_position = new Vector2(512, 384); + private TestDistanceSnapGrid grid; + [Cached(typeof(EditorBeatmap))] private readonly EditorBeatmap editorBeatmap; @@ -39,6 +43,7 @@ namespace osu.Game.Tests.Visual.Editing } }); editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length }); + editorBeatmap.Difficulty.SliderMultiplier = 1; } [SetUp] @@ -51,7 +56,7 @@ namespace osu.Game.Tests.Visual.Editing RelativeSizeAxes = Axes.Both, Colour = Color4.SlateGray }, - new TestDistanceSnapGrid() + grid = new TestDistanceSnapGrid() }; }); @@ -68,9 +73,22 @@ namespace osu.Game.Tests.Visual.Editing AddStep($"set beat divisor = {divisor}", () => BeatDivisor.Value = divisor); } - [Test] - public void TestLimitedDistance() + [TestCase(1.0)] + [TestCase(2.0)] + [TestCase(0.5)] + public void TestDistanceSpacing(double multiplier) { + AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier); + AddAssert("distance spacing matches multiplier", () => grid.DistanceBetweenTick == beat_snap_distance * multiplier); + } + + [TestCase(1.0)] + [TestCase(2.0)] + [TestCase(0.5)] + public void TestLimitedDistance(double multiplier) + { + const int end_time = 100; + AddStep("create limited grid", () => { Children = new Drawable[] @@ -80,15 +98,20 @@ namespace osu.Game.Tests.Visual.Editing RelativeSizeAxes = Axes.Both, Colour = Color4.SlateGray }, - new TestDistanceSnapGrid(100) + grid = new TestDistanceSnapGrid(end_time) }; }); + + AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier); + AddAssert("check correct interval count", () => grid.MaxIntervals == (end_time / grid.DistanceBetweenTick)); } private class TestDistanceSnapGrid : DistanceSnapGrid { public new float DistanceBetweenTick => base.DistanceBetweenTick; + public new int MaxIntervals => base.MaxIntervals; + public TestDistanceSnapGrid(double? endTime = null) : base(new HitObject(), grid_position, 0, endTime) { @@ -167,9 +190,11 @@ namespace osu.Game.Tests.Visual.Editing public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); - public IBindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); + public Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); - public float GetBeatSnapDistanceAt(HitObject referenceObject) => 10; + IBindable IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier; + + public float GetBeatSnapDistanceAt(HitObject referenceObject) => beat_snap_distance; public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration; From 7b71fb860b39574c71cad2def3055750cec4bdad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 18:25:32 +0900 Subject: [PATCH 0784/1276] Expose `DistanceSpacingMultiplier` for test usage --- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index bbcb702bd8..5e6d9dbe34 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Edit public abstract class DistancedHitObjectComposer : HitObjectComposer, IDistanceSnapProvider, IScrollBindingHandler where TObject : HitObject { - protected Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1.0) + public Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1.0) { MinValue = 0.1, MaxValue = 6.0, From b9d8b7e413462c45871419977b67a927ace19c41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 18:25:46 +0900 Subject: [PATCH 0785/1276] Fix end time extent not being accounted for in new snap implementation --- .../Edit/Compose/Components/CircularDistanceSnapGrid.cs | 5 +++++ .../Screens/Edit/Compose/Components/DistanceSnapGrid.cs | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index f34fa7328a..2873d74f75 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -87,6 +87,11 @@ namespace osu.Game.Screens.Edit.Compose.Components float snappedDistance = SnapProvider.FindSnappedDistance(ReferenceObject, travelLength / distanceSpacing); double snappedTime = StartTime + SnapProvider.DistanceToDuration(ReferenceObject, snappedDistance); + if (snappedTime > LatestEndTime) + { + snappedDistance = SnapProvider.DurationToDistance(ReferenceObject, LatestEndTime.Value - ReferenceObject.StartTime); + } + // The multiplier can then be reapplied to the final position. Vector2 snappedPosition = StartPosition + travelVector.Normalized() * snappedDistance * distanceSpacing; diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index a907608cbc..b9e0cfef19 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -43,6 +43,8 @@ namespace osu.Game.Screens.Edit.Compose.Components /// protected readonly double StartTime; + protected readonly double? LatestEndTime; + [Resolved] protected OsuColour Colours { get; private set; } @@ -56,7 +58,6 @@ namespace osu.Game.Screens.Edit.Compose.Components private BindableBeatDivisor beatDivisor { get; set; } private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit); - private readonly double? endTime; protected readonly HitObject ReferenceObject; @@ -70,7 +71,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected DistanceSnapGrid(HitObject referenceObject, Vector2 startPosition, double startTime, double? endTime = null) { ReferenceObject = referenceObject; - this.endTime = endTime; + LatestEndTime = endTime; StartPosition = startPosition; StartTime = startTime; @@ -94,12 +95,12 @@ namespace osu.Game.Screens.Edit.Compose.Components { DistanceBetweenTick = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * DistanceSpacingMultiplier.Value); - if (endTime == null) + if (LatestEndTime == null) MaxIntervals = int.MaxValue; else { // +1 is added since a snapped hitobject may have its start time slightly less than the snapped time due to floating point errors - double maxDuration = endTime.Value - StartTime + 1; + double maxDuration = LatestEndTime.Value - StartTime + 1; MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(ReferenceObject, DistanceBetweenTick)); } From 4fe23bced2da57d8931991b3df12aab667c7c6d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 18:32:37 +0900 Subject: [PATCH 0786/1276] Update tests with new assumptions and a better snap implementation --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 1feb80414a..201d6b5d8f 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -4,7 +4,6 @@ using System; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -12,7 +11,6 @@ using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Objects; @@ -26,16 +24,26 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene { private const double beat_length = 100; + private static readonly Vector2 grid_position = new Vector2(512, 384); [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] private readonly EditorBeatmap editorBeatmap; + [Cached] + private readonly EditorClock editorClock; + [Cached] private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); [Cached(typeof(IDistanceSnapProvider))] - private readonly SnapProvider snapProvider = new SnapProvider(); + private readonly OsuHitObjectComposer snapProvider = new OsuHitObjectComposer(new OsuRuleset()) + { + // Just used for the snap implementation, so let's hide from vision. + AlwaysPresent = true, + Alpha = 0, + }; private OsuDistanceSnapGrid grid; @@ -48,14 +56,25 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Ruleset = new OsuRuleset().RulesetInfo } }); + + editorClock = new EditorClock(editorBeatmap); + + base.Content.Children = new Drawable[] + { + snapProvider, + Content + }; } + protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; + [SetUp] public void Setup() => Schedule(() => { editorBeatmap.Difficulty.SliderMultiplier = 1; editorBeatmap.ControlPointInfo.Clear(); editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length }); + snapProvider.DistanceSpacingMultiplier.Value = 1; Children = new Drawable[] { @@ -94,20 +113,20 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public void TestCursorInCentre() { AddStep("move mouse to centre", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position))); - assertSnappedDistance((float)beat_length); + assertSnappedDistance(0); } [Test] public void TestCursorBeforeMovementPoint() { - AddStep("move mouse to just before movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 1.49f))); + AddStep("move mouse to just before movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 1.45f))); assertSnappedDistance((float)beat_length); } [Test] public void TestCursorAfterMovementPoint() { - AddStep("move mouse to just after movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 1.51f))); + AddStep("move mouse to just after movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 1.55f))); assertSnappedDistance((float)beat_length * 2); } @@ -177,27 +196,5 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor cursor.Position = GetSnapPosition.Invoke(screenSpacePosition); } } - - private class SnapProvider : IDistanceSnapProvider - { - public SnapResult FindSnappedPosition(Vector2 screenSpacePosition) => - new SnapResult(screenSpacePosition, null); - - public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); - - public Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); - - IBindable IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier; - - public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length; - - public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration; - - public double DistanceToDuration(HitObject referenceObject, float distance) => distance; - - public double FindSnappedDuration(HitObject referenceObject, float distance) => 0; - - public float FindSnappedDistance(HitObject referenceObject, float distance) => 0; - } } } From 9fd98b80608e42182df12ced2ebec96b19d2e3db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 18:41:25 +0900 Subject: [PATCH 0787/1276] Also add test coverage of adjsuting the distance spacing multiplier --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 201d6b5d8f..6d0ed15e45 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -7,7 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; +using osu.Framework.Input; using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene { - private const double beat_length = 100; + private const float beat_length = 100; private static readonly Vector2 grid_position = new Vector2(512, 384); @@ -119,15 +119,27 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [Test] public void TestCursorBeforeMovementPoint() { - AddStep("move mouse to just before movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 1.45f))); - assertSnappedDistance((float)beat_length); + AddStep("move mouse to just before movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 1.45f))); + assertSnappedDistance(beat_length); } [Test] public void TestCursorAfterMovementPoint() { - AddStep("move mouse to just after movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 1.55f))); - assertSnappedDistance((float)beat_length * 2); + AddStep("move mouse to just after movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 1.55f))); + assertSnappedDistance(beat_length * 2); + } + + [TestCase(0.5f, beat_length * 2)] + [TestCase(1, beat_length * 2)] + [TestCase(1.5f, beat_length * 1.5f)] + [TestCase(2f, beat_length * 2)] + public void TestDistanceSpacingAdjust(float multiplier, float expectedDistance) + { + AddStep($"Set distance spacing to {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier); + AddStep("move mouse to point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 2))); + + assertSnappedDistance(expectedDistance); } [Test] @@ -147,8 +159,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor }; }); - AddStep("move mouse outside grid", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 3f))); - assertSnappedDistance((float)beat_length * 2); + AddStep("move mouse outside grid", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 3f))); + assertSnappedDistance(beat_length * 2); } private void assertSnappedDistance(float expectedDistance) => AddAssert($"snap distance = {expectedDistance}", () => @@ -164,6 +176,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private readonly Drawable cursor; + private InputManager inputManager; + + public override bool HandlePositionalInput => true; + public SnappingCursorContainer() { RelativeSizeAxes = Axes.Both; @@ -180,20 +196,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { base.LoadComplete(); - updatePosition(GetContainingInputManager().CurrentState.Mouse.Position); + inputManager = GetContainingInputManager(); } - protected override bool OnMouseMove(MouseMoveEvent e) + protected override void Update() { - base.OnMouseMove(e); - - updatePosition(e.ScreenSpaceMousePosition); - return true; - } - - private void updatePosition(Vector2 screenSpacePosition) - { - cursor.Position = GetSnapPosition.Invoke(screenSpacePosition); + base.Update(); + cursor.Position = GetSnapPosition.Invoke(inputManager.CurrentState.Mouse.Position); } } } From e7205d8593ec1ab27528137eed48b2002d61cd85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 19:09:56 +0900 Subject: [PATCH 0788/1276] Reset all test data before each test method to avoid channels stuck in joined state --- .../Visual/Online/TestSceneChatOverlayV2.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 9d4de11c5a..b15a7afbf9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -31,22 +31,20 @@ namespace osu.Game.Tests.Visual.Online private ChatOverlayV2 chatOverlay; private ChannelManager channelManager; - private readonly APIUser testUser; - private readonly Channel testPMChannel; - private readonly Channel[] testChannels; + private APIUser testUser; + private Channel testPMChannel; + private Channel[] testChannels; + private Channel testChannel1 => testChannels[0]; private Channel testChannel2 => testChannels[1]; - public TestSceneChatOverlayV2() - { - testUser = new APIUser { Username = "test user", Id = 5071479 }; - testPMChannel = new Channel(testUser); - testChannels = Enumerable.Range(1, 10).Select(createPublicChannel).ToArray(); - } - [SetUp] public void SetUp() => Schedule(() => { + testUser = new APIUser { Username = "test user", Id = 5071479 }; + testPMChannel = new Channel(testUser); + testChannels = Enumerable.Range(1, 10).Select(createPublicChannel).ToArray(); + Child = new DependencyProvidingContainer { RelativeSizeAxes = Axes.Both, From 25ea660b0b7f449bb74280a09b3f31db5b471d67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 19:13:48 +0900 Subject: [PATCH 0789/1276] Replace `HandlePositionalInput` override with simple hover effect --- osu.Game/Overlays/Chat/ChatOverlayTopBar.cs | 24 +++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs index 9ba7608d89..3a8cd1fb91 100644 --- a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs +++ b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs @@ -4,31 +4,35 @@ #nullable enable using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.Chat { public class ChatOverlayTopBar : Container { - // IsHovered is used by overlay - public override bool HandlePositionalInput => true; + private Box background = null!; + + private Color4 backgroundColour; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, TextureStore textures) { Children = new Drawable[] { - new Box + background = new Box { RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background3, + Colour = backgroundColour = colourProvider.Background3, }, new GridContainer { @@ -63,5 +67,17 @@ namespace osu.Game.Overlays.Chat }, }; } + + protected override bool OnHover(HoverEvent e) + { + background.FadeColour(backgroundColour.Lighten(0.1f), 300, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + background.FadeColour(backgroundColour, 300, Easing.OutQuint); + base.OnHoverLost(e); + } } } From 74505ba1666e821c905d27325bb86f7e0236aa2a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 19:16:19 +0900 Subject: [PATCH 0790/1276] Remove `!` usage (also seems to fix height saving/loading) --- osu.Game/Overlays/ChatOverlayV2.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 658a28bfdb..ae4e160de5 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -39,7 +39,8 @@ namespace osu.Game.Overlays private ChatTextBar textBar = null!; private Container currentChannelContainer = null!; - private Bindable? chatHeight; + private readonly Bindable chatHeight = new Bindable(); + private bool isDraggingTopBar; private float dragStartChatHeight; @@ -137,7 +138,8 @@ namespace osu.Game.Overlays loading.Show(); - chatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight).GetBoundCopy(); + config.BindWith(OsuSetting.ChatDisplayHeight, chatHeight); + chatHeight.BindValueChanged(height => { Height = height.NewValue; }, true); currentChannel.BindTo(channelManager.CurrentChannel); @@ -193,7 +195,7 @@ namespace osu.Game.Overlays if (!isDraggingTopBar) return base.OnDragStart(e); - dragStartChatHeight = chatHeight!.Value; + dragStartChatHeight = chatHeight.Value; return true; } @@ -203,7 +205,7 @@ namespace osu.Game.Overlays return; float targetChatHeight = dragStartChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; - chatHeight!.Value = targetChatHeight; + chatHeight.Value = targetChatHeight; } protected override void OnDragEnd(DragEndEvent e) From e54f5e2d9206f49b387beda75f70e9fcfddc5a4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 19:17:32 +0900 Subject: [PATCH 0791/1276] Adjust value change variables to avoid `e` usage --- osu.Game/Overlays/ChatOverlayV2.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index ae4e160de5..8a73eabed2 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -238,9 +238,9 @@ namespace osu.Game.Overlays base.OnFocus(e); } - private void currentChannelChanged(ValueChangedEvent e) + private void currentChannelChanged(ValueChangedEvent channel) { - Channel? newChannel = e.NewValue; + Channel? newChannel = channel.NewValue; loading.Show(); @@ -248,7 +248,7 @@ namespace osu.Game.Overlays if (newChannel == null) { // Find another channel to switch to - newChannel = channelManager.JoinedChannels.FirstOrDefault(chan => chan != e.OldValue); + newChannel = channelManager.JoinedChannels.FirstOrDefault(c => c != channel.OldValue); if (newChannel == null) selectorActive.Value = true; From 97221d2ef1ee5b88464fc2bdbbe7dc372d05c882 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 19:22:30 +0900 Subject: [PATCH 0792/1276] Tidy up initialisation --- osu.Game/Overlays/ChatOverlayV2.cs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 8a73eabed2..16da1f7c10 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -64,20 +64,27 @@ namespace osu.Game.Overlays [Cached] private readonly Bindable currentChannel = new Bindable(); + public ChatOverlayV2() + { + Height = default_chat_height; + + Masking = true; + + const float corner_radius = 7f; + + CornerRadius = corner_radius; + + // Hack to hide the bottom edge corner radius off-screen. + Margin = new MarginPadding { Bottom = -corner_radius }; + Padding = new MarginPadding { Bottom = corner_radius }; + + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + } + [BackgroundDependencyLoader] private void load() { - // Width = 0.85f; // Matches OnlineOverlay - Height = default_chat_height; - RelativeSizeAxes = Axes.Both; - RelativePositionAxes = Axes.Both; - Anchor = Anchor.BottomCentre; - Origin = Anchor.BottomCentre; - Masking = true; - CornerRadius = 7f; - Margin = new MarginPadding { Bottom = -10 }; - Padding = new MarginPadding { Bottom = 10 }; - Children = new Drawable[] { topBar = new ChatOverlayTopBar From 684db27bb8d962c6ada325dcb903b71c95106dbb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 19:40:44 +0900 Subject: [PATCH 0793/1276] Reword binding text to read better --- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index cfe0fd55ce..b2f25de7f2 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -130,14 +130,14 @@ namespace osu.Game.Localisation public static LocalisableString SelectNext => new TranslatableString(getKey(@"select_next"), @"Next selection"); /// - /// "Previous group selection" + /// "Select previous group" /// - public static LocalisableString SelectPreviousGroup => new TranslatableString(getKey(@"select_previous_group"), @"Previous group selection"); + public static LocalisableString SelectPreviousGroup => new TranslatableString(getKey(@"select_previous_group"), @"Select previous group"); /// - /// "Next group selection" + /// "Select next group" /// - public static LocalisableString SelectNextGroup => new TranslatableString(getKey(@"select_next_group"), @"Next group selection"); + public static LocalisableString SelectNextGroup => new TranslatableString(getKey(@"select_next_group"), @"Select next group"); /// /// "Home" From 8501a4161958514bfae52c5e78aee2ee26a870e0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 14:37:38 +0300 Subject: [PATCH 0794/1276] Bring back separate bool properties as non-cascading --- osu.Game.Tests/Mods/ModUtilsTest.cs | 12 ++++++--- .../TestSceneFreeModSelectScreen.cs | 3 +-- .../BeatmapSet/LeaderboardModSelector.cs | 2 +- osu.Game/Rulesets/Mods/IMod.cs | 26 +++++++++--------- osu.Game/Rulesets/Mods/Mod.cs | 11 ++++---- osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | 3 ++- osu.Game/Rulesets/Mods/ModAutoplay.cs | 4 ++- osu.Game/Rulesets/Mods/ModRateAdjust.cs | 2 +- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- osu.Game/Rulesets/Mods/ModUsage.cs | 27 ------------------- osu.Game/Rulesets/Mods/UnknownMod.cs | 4 ++- .../OnlinePlay/FreeModSelectOverlay.cs | 2 +- .../Screens/OnlinePlay/FreeModSelectScreen.cs | 2 +- .../Multiplayer/MultiplayerMatchSongSelect.cs | 4 +-- .../OnlinePlay/OnlinePlaySongSelect.cs | 2 +- .../PlayerSettings/BeatmapOffsetControl.cs | 3 +-- osu.Game/Screens/Play/SubmittingPlayer.cs | 3 +-- osu.Game/Screens/Ranking/ResultsScreen.cs | 3 +-- osu.Game/Utils/ModUtils.cs | 4 +-- 19 files changed, 49 insertions(+), 70 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/ModUsage.cs diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index 2447233d3c..6c9dddf51f 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -324,12 +324,18 @@ namespace osu.Game.Tests.Mods public override string Acronym => string.Empty; public override double ScoreMultiplier => 1; public override bool HasImplementation => true; - public override bool IsPlayable(ModUsage usage) => usage == ModUsage.SoloLocal; + public override bool ValidForMultiplayer => false; + public override bool ValidForMultiplayerAsFreeMod => false; } - private class InvalidMultiplayerFreeMod : InvalidMultiplayerMod + private class InvalidMultiplayerFreeMod : Mod { - public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerLocal; + public override string Name => string.Empty; + public override string Description => string.Empty; + public override string Acronym => string.Empty; + public override double ScoreMultiplier => 1; + public override bool HasImplementation => true; + public override bool ValidForMultiplayerAsFreeMod => false; } public interface IModCompatibilitySpecification diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs index fad784a09e..b5f901e51d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -6,7 +6,6 @@ using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Overlays.Mods; -using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay; namespace osu.Game.Tests.Visual.Multiplayer @@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("all visible mods are playable", () => this.ChildrenOfType() .Where(panel => panel.IsPresent) - .All(panel => panel.Mod.HasImplementation && panel.Mod.IsPlayable(ModUsage.SoloLocal))); + .All(panel => panel.Mod.HasImplementation && panel.Mod.UserPlayable)); AddToggleStep("toggle visibility", visible => { diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 17ea117018..25aed4c980 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet return; modsContainer.Add(new ModButton(new ModNoMod())); - modsContainer.AddRange(rulesetInstance.AllMods.Where(m => m.IsPlayable(ModUsage.SoloLocal)).Select(m => new ModButton(m))); + modsContainer.AddRange(rulesetInstance.AllMods.Where(m => m.UserPlayable).Select(m => new ModButton(m))); modsContainer.ForEach(button => { diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index c0d4aa5c9f..30fa1ea8cb 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -33,26 +33,24 @@ namespace osu.Game.Rulesets.Mods /// IconUsage? Icon { get; } - /// - /// Whether this mod is playable for the given usage. - /// - /// - /// - /// Should be always false for cases where the user is not interacting with the game. - /// Should be false in for mods that make gameplay duration dependent on user input (e.g. ). - /// Should be false in for mods that affect the gameplay duration (e.g. and ). - /// - /// - /// The mod usage. - bool IsPlayable(ModUsage usage); - /// /// Whether this mod is playable by an end user. /// Should be false for cases where the user is not interacting with the game (so it can be excluded from multiplayer selection, for example). /// - [Obsolete("Override IsPlayable instead.")] // Can be removed 20221104 bool UserPlayable { get; } + /// + /// Whether this mod is valid for multiplayer matches. + /// Should be false for mods that make gameplay duration dependent on user input (e.g. ). + /// + bool ValidForMultiplayer { get; } + + /// + /// Whether this mod is valid as a free mod in multiplayer matches. + /// Should be false for mods that affect the gameplay duration (e.g. and ). + /// + bool ValidForMultiplayerAsFreeMod { get; } + /// /// Create a fresh instance based on this mod. /// diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b2f1b7e24f..93990d36e1 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -91,13 +91,14 @@ namespace osu.Game.Rulesets.Mods [JsonIgnore] public virtual bool HasImplementation => this is IApplicableMod; -#pragma warning disable 618 - public virtual bool IsPlayable(ModUsage usage) => UserPlayable; -#pragma warning restore 618 + [JsonIgnore] + public virtual bool UserPlayable => true; [JsonIgnore] - [Obsolete("Override IsPlayable instead.")] // Can be removed 20221104 - public virtual bool UserPlayable => true; + public virtual bool ValidForMultiplayer => true; + + [JsonIgnore] + public virtual bool ValidForMultiplayerAsFreeMod => true; [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override IsPlayable to false.")] // Can be removed 20211009 public virtual bool Ranked => false; diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs index 700ad30c08..93251f7b2d 100644 --- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs +++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs @@ -31,7 +31,8 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 1; - public override bool IsPlayable(ModUsage usage) => usage == ModUsage.SoloLocal; + public override bool ValidForMultiplayer => false; + public override bool ValidForMultiplayerAsFreeMod => false; public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModTimeRamp) }; diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index 9df25b006d..0ebe11b393 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -24,7 +24,9 @@ namespace osu.Game.Rulesets.Mods public bool RestartOnFail => false; - public override bool IsPlayable(ModUsage usage) => false; + public override bool UserPlayable => false; + public override bool ValidForMultiplayer => false; + public override bool ValidForMultiplayerAsFreeMod => false; public override Type[] IncompatibleMods => new[] { typeof(ModCinema), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail) }; diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index acb39bfbe6..05953f903f 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModRateAdjust : Mod, IApplicableToRate { - public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerLocal; + public override bool ValidForMultiplayerAsFreeMod => false; public abstract BindableNumber SpeedChange { get; } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index c554c39010..fe6d54332c 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] public abstract BindableBool AdjustPitch { get; } - public override bool IsPlayable(ModUsage usage) => usage != ModUsage.MultiplayerLocal; + public override bool ValidForMultiplayerAsFreeMod => false; public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModAdaptiveSpeed) }; diff --git a/osu.Game/Rulesets/Mods/ModUsage.cs b/osu.Game/Rulesets/Mods/ModUsage.cs deleted file mode 100644 index 3f25154d73..0000000000 --- a/osu.Game/Rulesets/Mods/ModUsage.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Rulesets.Mods -{ - /// - /// The context in which a is playable. - /// - public enum ModUsage - { - /// - /// This mod can be used for a per-user gameplay session. - /// - SoloLocal, - - /// - /// This mod can be used in multiplayer but must be applied to all users. - /// This is generally the case for mods which affect the length of gameplay. - /// - MultiplayerGlobal, - - /// - /// This mod can be used in multiplayer either at a room or per-player level (i.e. "free mod"). - /// - MultiplayerLocal, - } -} diff --git a/osu.Game/Rulesets/Mods/UnknownMod.cs b/osu.Game/Rulesets/Mods/UnknownMod.cs index 75d86e67bc..72de0ad653 100644 --- a/osu.Game/Rulesets/Mods/UnknownMod.cs +++ b/osu.Game/Rulesets/Mods/UnknownMod.cs @@ -15,7 +15,9 @@ namespace osu.Game.Rulesets.Mods public override string Description => "This mod could not be resolved by the game."; public override double ScoreMultiplier => 0; - public override bool IsPlayable(ModUsage usage) => false; + public override bool UserPlayable => false; + public override bool ValidForMultiplayer => false; + public override bool ValidForMultiplayerAsFreeMod => false; public override ModType Type => ModType.System; diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 13d5dd8927..d5abaaab4e 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay public new Func IsValidMod { get => base.IsValidMod; - set => base.IsValidMod = m => m.HasImplementation && m.IsPlayable(ModUsage.SoloLocal) && value(m); + set => base.IsValidMod = m => m.HasImplementation && m.UserPlayable && value(m); } public FreeModSelectOverlay() diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs index 415ae60706..5a7a60b479 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.OnlinePlay public new Func IsValidMod { get => base.IsValidMod; - set => base.IsValidMod = m => m.IsPlayable(ModUsage.SoloLocal) && value.Invoke(m); + set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m); } public FreeModSelectScreen() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index d7e752623d..929c3ee321 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -95,8 +95,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && mod.IsPlayable(ModUsage.MultiplayerGlobal); + protected override bool IsValidMod(Mod mod) => base.IsValidMod(mod) && mod.ValidForMultiplayer; - protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && mod.IsPlayable(ModUsage.MultiplayerLocal); + protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && mod.ValidForMultiplayerAsFreeMod; } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index b8b0604c79..6a559dbb2c 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -170,7 +170,7 @@ namespace osu.Game.Screens.OnlinePlay /// /// The to check. /// Whether is a valid mod for online play. - protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && ModUtils.FlattenMod(mod).All(m => m.IsPlayable(ModUsage.SoloLocal)); + protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && ModUtils.FlattenMod(mod).All(m => m.UserPlayable); /// /// Checks whether a given is valid for per-player free-mod selection. diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index af2cecfba5..1662ca399f 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -20,7 +20,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Localisation; using osu.Game.Overlays.Settings; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; @@ -188,7 +187,7 @@ namespace osu.Game.Screens.Play.PlayerSettings if (score.NewValue == null) return; - if (score.NewValue.Mods.Any(m => !m.IsPlayable(ModUsage.SoloLocal))) + if (score.NewValue.Mods.Any(m => !m.UserPlayable)) return; var hitEvents = score.NewValue.HitEvents; diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index b33ff87d55..b62dc1e5a6 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -11,7 +11,6 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.API; using osu.Game.Online.Rooms; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -48,7 +47,7 @@ namespace osu.Game.Screens.Play // Token request construction should happen post-load to allow derived classes to potentially prepare DI backings that are used to create the request. var tcs = new TaskCompletionSource(); - if (Mods.Value.Any(m => !m.IsPlayable(ModUsage.SoloLocal))) + if (Mods.Value.Any(m => !m.UserPlayable)) { handleTokenFailure(new InvalidOperationException("Non-user playable mod selected.")); return false; diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index e0ae0309ce..98514cd846 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -18,7 +18,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Online.API; -using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking.Statistics; @@ -146,7 +145,7 @@ namespace osu.Game.Screens.Ranking if (Score != null) { // only show flair / animation when arriving after watching a play that isn't autoplay. - bool shouldFlair = player != null && Score.Mods.All(m => m.IsPlayable(ModUsage.SoloLocal)); + bool shouldFlair = player != null && Score.Mods.All(m => m.UserPlayable); ScorePanelList.AddScore(Score, shouldFlair); } diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index f11a06e3be..a252a9b416 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -144,7 +144,7 @@ namespace osu.Game.Utils if (!CheckCompatibleSet(mods, out invalidMods)) return false; - return checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && m.IsPlayable(ModUsage.MultiplayerGlobal), out invalidMods); + return checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && m.ValidForMultiplayer, out invalidMods); } /// @@ -154,7 +154,7 @@ namespace osu.Game.Utils /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidFreeModsForMultiplayer(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) - => checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && m.IsPlayable(ModUsage.MultiplayerLocal) && !(m is MultiMod), out invalidMods); + => checkValid(mods, m => m.Type != ModType.System && m.HasImplementation && m.ValidForMultiplayerAsFreeMod && !(m is MultiMod), out invalidMods); private static bool checkValid(IEnumerable mods, Predicate valid, [NotNullWhen(false)] out List? invalidMods) { From 43c9058d09dbff83898f8b7f841c524d8a72d454 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 14:39:05 +0300 Subject: [PATCH 0795/1276] Fix wrong obsolete message --- osu.Game/Rulesets/Mods/Mod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 93990d36e1..af1550f8a9 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Mods [JsonIgnore] public virtual bool ValidForMultiplayerAsFreeMod => true; - [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override IsPlayable to false.")] // Can be removed 20211009 + [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override and set UserPlayable to false.")] // Can be removed 20211009 public virtual bool Ranked => false; /// From 2039d3db6a76031a7f23d9c84140f0f1f8f94d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 14:37:57 +0200 Subject: [PATCH 0796/1276] Use standard slider ticks in strict tracking mod --- osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs index a1e6946157..8bbfa68fdf 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs @@ -70,11 +70,6 @@ namespace osu.Game.Rulesets.Osu.Mods drawableRuleset.Playfield.RegisterPool(10, 100); } - private class StrictTrackingSliderTick : SliderTick - { - public override Judgement CreateJudgement() => new OsuIgnoreJudgement(); - } - private class StrictTrackingSliderTailCircle : SliderTailCircle { public StrictTrackingSliderTailCircle(Slider slider) @@ -115,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Mods switch (e.Type) { case SliderEventType.Tick: - AddNested(new StrictTrackingSliderTick + AddNested(new SliderTick { SpanIndex = e.SpanIndex, SpanStartTime = e.SpanStartTime, From 74c0cb2f6eb9ddf40563e70da00f9454e29b22ca Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 16:16:55 +0300 Subject: [PATCH 0797/1276] Add note about not checking compatibility in free mods validity method --- osu.Game/Utils/ModUtils.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index a252a9b416..ea092a8ca3 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -150,6 +150,11 @@ namespace osu.Game.Utils /// /// Checks that all s in a combination are valid as "free mods" in a multiplayer match session. /// + /// + /// Note that this does not check compatibility between mods, + /// given that the passed mods are expected to be the ones to be allowed for the multiplayer match, + /// not to be confused with the list of mods the user currently has selected for the multiplayer match. + /// /// The mods to check. /// Invalid mods, if any were found. Will be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. From 5ea6f62951b93a9e558c59142e0275ce886d2459 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Thu, 5 May 2022 14:20:33 +0100 Subject: [PATCH 0798/1276] Ensure `RelativePositionAxes` is set in BDL for animations to work --- osu.Game/Overlays/ChatOverlayV2.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 16da1f7c10..cab88136fc 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -85,6 +85,9 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { + // Required for the pop in/out animation + RelativePositionAxes = Axes.Both; + Children = new Drawable[] { topBar = new ChatOverlayTopBar From 9cb52f8879f839eb7ba64cd1b38a35d4bc0c69cb Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Thu, 5 May 2022 14:21:26 +0100 Subject: [PATCH 0799/1276] Add tests for chat height saving/loading --- .../Visual/Online/TestSceneChatOverlayV2.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index b15a7afbf9..31ced4e8f7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -6,12 +6,14 @@ using System.Linq; using System.Collections.Generic; using System.Net; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Framework.Testing; using osu.Framework.Utils; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -21,6 +23,7 @@ using osu.Game.Overlays; using osu.Game.Overlays.Chat; using osu.Game.Overlays.Chat.Listing; using osu.Game.Overlays.Chat.ChannelList; +using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.Online @@ -38,6 +41,9 @@ namespace osu.Game.Tests.Visual.Online private Channel testChannel1 => testChannels[0]; private Channel testChannel2 => testChannels[1]; + [Resolved] + private OsuConfigManager config { get; set; } = null!; + [SetUp] public void SetUp() => Schedule(() => { @@ -124,6 +130,28 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Overlay is hidden", () => chatOverlay.State.Value == Visibility.Hidden); } + [Test] + public void TestChatHeight() + { + Bindable configChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); + float newHeight = 0; + + AddStep("Set config chat height", () => configChatHeight.Value = 0.4f); + AddStep("Show overlay", () => chatOverlay.Show()); + AddAssert("Overlay uses config height", () => chatOverlay.Height == 0.4f); + AddStep("Drag overlay to new height", () => { + InputManager.MoveMouseTo(chatOverlayTopBar); + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(chatOverlayTopBar, new Vector2(0, -300)); + InputManager.ReleaseButton(MouseButton.Left); + }); + AddStep("Store new height", () => newHeight = chatOverlay.Height); + AddAssert("Config height changed", () => configChatHeight.Value != 0.4f && configChatHeight.Value == newHeight); + AddStep("Hide overlay", () => chatOverlay.Hide()); + AddStep("Show overlay", () => chatOverlay.Show()); + AddAssert("Overlay uses new height", () => chatOverlay.Height == newHeight); + } + [Test] public void TestChannelSelection() { @@ -360,6 +388,9 @@ namespace osu.Game.Tests.Visual.Online private ChatTextBox chatOverlayTextBox => chatOverlay.ChildrenOfType().Single(); + private ChatOverlayTopBar chatOverlayTopBar => + chatOverlay.ChildrenOfType().Single(); + private void clickDrawable(Drawable d) { InputManager.MoveMouseTo(d); From 5657e7f11ec5d0e8551380efeb3e4998e6e55f3c Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Thu, 5 May 2022 14:52:03 +0100 Subject: [PATCH 0800/1276] Fix chat height saving/loading test --- osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 31ced4e8f7..98574e5d53 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -133,13 +133,15 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestChatHeight() { - Bindable configChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); + Bindable configChatHeight = null; float newHeight = 0; + AddStep("Bind config chat height", () => configChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight).GetBoundCopy()); AddStep("Set config chat height", () => configChatHeight.Value = 0.4f); AddStep("Show overlay", () => chatOverlay.Show()); AddAssert("Overlay uses config height", () => chatOverlay.Height == 0.4f); - AddStep("Drag overlay to new height", () => { + AddStep("Drag overlay to new height", () => + { InputManager.MoveMouseTo(chatOverlayTopBar); InputManager.PressButton(MouseButton.Left); InputManager.MoveMouseTo(chatOverlayTopBar, new Vector2(0, -300)); From 2cc56a4b19348b2b80ca4e4521a37e56a4bc0011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 19:07:44 +0200 Subject: [PATCH 0801/1276] Fix hidden issue with automatic customisation panel show logic Doesn't cause tests to fail headless, but they do fail in the test browser. --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 8060bca65f..1779d59e33 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -217,7 +217,7 @@ namespace osu.Game.Overlays.Mods foreach (var mod in SelectedMods.Value) { anyCustomisableMod |= mod.GetSettingsSourceProperties().Any(); - anyModWithRequiredCustomisationAdded |= !valueChangedEvent.OldValue.Contains(mod) && mod.RequiresConfiguration; + anyModWithRequiredCustomisationAdded |= valueChangedEvent.OldValue.All(m => m.GetType() != mod.GetType()) && mod.RequiresConfiguration; } if (anyCustomisableMod) From 8002726c9c1a1e1bf23befa68bb45eaec1cb57fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 19:08:38 +0200 Subject: [PATCH 0802/1276] Add test coverage for dismissing customisation area with Escape --- .../Visual/UserInterface/TestSceneModSelectScreen.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index bdb423a43c..a3ce4b734b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -164,11 +164,19 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("select mod requiring configuration", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); assertCustomisationToggleState(disabled: false, active: true); - AddStep("dismiss mod customisation", () => + AddStep("dismiss mod customisation via mouse", () => { InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); + assertCustomisationToggleState(disabled: false, active: false); + + AddStep("reset mods", () => SelectedMods.SetDefault()); + AddStep("select mod requiring configuration", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); + assertCustomisationToggleState(disabled: false, active: true); + + AddStep("dismiss mod customisation via keyboard", () => InputManager.Key(Key.Escape)); + assertCustomisationToggleState(disabled: false, active: false); AddStep("append another mod not requiring config", () => SelectedMods.Value = SelectedMods.Value.Append(new OsuModFlashlight()).ToArray()); assertCustomisationToggleState(disabled: false, active: false); From 79e1b93b303195ef57cb9d2fc839aca0670fefd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 17:08:02 +0200 Subject: [PATCH 0803/1276] Close customisation area on escape press before exiting --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 1779d59e33..a7f8a167f9 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -18,6 +18,7 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Input; @@ -315,6 +316,17 @@ namespace osu.Game.Overlays.Mods } } + public override bool OnPressed(KeyBindingPressEvent e) + { + if (e.Action == GlobalAction.Back && customisationVisible.Value) + { + customisationVisible.Value = false; + return true; + } + + return base.OnPressed(e); + } + internal class ColumnScrollContainer : OsuScrollContainer { public ColumnScrollContainer() From aafb363a3487e547ea349d0717f489557eb82252 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 6 May 2022 01:24:19 +0800 Subject: [PATCH 0804/1276] Only enable EnforceCodeStyleInBuild in CI --- .github/workflows/ci.yml | 7 +++++-- Directory.Build.props | 1 - osu.Android.props | 1 - osu.iOS.props | 1 - 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4d7f894d4..57069a0ae2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,9 @@ jobs: path: ${{ github.workspace }}/inspectcode key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }} + - name: Dotnet code style + run: dotnet build -c Debug -warnaserror osu.Desktop.slnf -p:EnforceCodeStyleInBuild=true + - name: CodeFileSanity run: | # TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround. @@ -128,7 +131,7 @@ jobs: # cannot accept .sln(f) files as arguments. # Build just the main game for now. - name: Build - run: msbuild osu.Android/osu.Android.csproj /restore /p:Configuration=Debug /p:EnforceCodeStyleInBuild=false + run: msbuild osu.Android/osu.Android.csproj /restore /p:Configuration=Debug build-only-ios: name: Build only (iOS) @@ -147,4 +150,4 @@ jobs: # cannot accept .sln(f) files as arguments. # Build just the main game for now. - name: Build - run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug /p:EnforceCodeStyleInBuild=false + run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug diff --git a/Directory.Build.props b/Directory.Build.props index 65ffe02584..73a150d3e3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,7 +20,6 @@ - true $(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset diff --git a/osu.Android.props b/osu.Android.props index 299f236220..2866ec24a6 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -19,7 +19,6 @@ cjk,mideast,other,rare,west SdkOnly prompt - false True diff --git a/osu.iOS.props b/osu.iOS.props index d16c6803f4..f987ae9bf8 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -11,7 +11,6 @@ iPhone Developer true - false From 30ffc7b23f0d354cad739df4ca2e4c62ba42a294 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 6 May 2022 01:28:30 +0800 Subject: [PATCH 0805/1276] Remove dotnet-format --- .config/dotnet-tools.json | 6 ------ .github/workflows/ci.yml | 4 ---- 2 files changed, 10 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 5a3eadf607..1132396608 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -2,12 +2,6 @@ "version": 1, "isRoot": true, "tools": { - "dotnet-format": { - "version": "3.1.37601", - "commands": [ - "dotnet-format" - ] - }, "jetbrains.resharper.globaltools": { "version": "2022.1.0-eap10", "commands": [ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57069a0ae2..514acef525 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,10 +49,6 @@ jobs: done <<< $(dotnet codefilesanity) exit $exit_code - # Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded. - # - name: .NET Format (Dry Run) - # run: dotnet format --dry-run --check - - name: InspectCode run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN From 701ddade47739c20143e56e4da271663b24bce7d Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 6 May 2022 01:32:37 +0800 Subject: [PATCH 0806/1276] Revert editorconfig changes from framework --- .editorconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.editorconfig b/.editorconfig index 35ac84fca0..c0ea55f4c8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -176,7 +176,7 @@ csharp_style_unused_value_assignment_preference = discard_variable:warning #Style - variable declaration csharp_style_inlined_variable_declaration = true:warning -csharp_style_deconstructed_variable_declaration = true:warning +csharp_style_deconstructed_variable_declaration = false:silent #Style - other C# 7.x features dotnet_style_prefer_inferred_tuple_names = true:warning @@ -187,8 +187,8 @@ dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent #Style - C# 8 features csharp_prefer_static_local_function = true:warning csharp_prefer_simple_using_statement = true:silent -csharp_style_prefer_index_operator = true:warning -csharp_style_prefer_range_operator = true:warning +csharp_style_prefer_index_operator = false:silent +csharp_style_prefer_range_operator = false:silent csharp_style_prefer_switch_expression = false:none [*.{yaml,yml}] From 407db7ff9d288d9f1a81d05027f732eb33019c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 21:49:02 +0200 Subject: [PATCH 0807/1276] Replace old mod select overlay with new design --- .../Navigation/TestSceneScreenNavigation.cs | 2 +- .../OnlinePlay/OnlinePlaySongSelect.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 36 ++++++------------- 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index a1f41d4caf..8ba074a7d1 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -551,7 +551,7 @@ namespace osu.Game.Tests.Visual.Navigation public class TestPlaySongSelect : PlaySongSelect { - public ModSelectOverlay ModSelectOverlay => ModSelect; + public ModSelectScreen ModSelectOverlay => ModSelect; public BeatmapOptionsOverlay BeatmapOptionsOverlay => BeatmapOptions; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 6a559dbb2c..0eeb32230d 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -153,7 +153,7 @@ namespace osu.Game.Screens.OnlinePlay return base.OnExiting(e); } - protected override ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay + protected override ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen { IsValidMod = IsValidMod }; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 2a1ed2a7a8..3c46433b45 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -101,7 +101,7 @@ namespace osu.Game.Screens.Select [Resolved(CanBeNull = true)] private LegacyImportManager legacyImportManager { get; set; } - protected ModSelectOverlay ModSelect { get; private set; } + protected ModSelectScreen ModSelect { get; private set; } protected Sample SampleConfirm { get; private set; } @@ -252,35 +252,19 @@ namespace osu.Game.Screens.Select { AddRangeInternal(new Drawable[] { - new GridContainer // used for max height implementation + FooterPanels = new Container { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + Padding = new MarginPadding { Bottom = Footer.HEIGHT }, + Children = new Drawable[] { - new Dimension(), - new Dimension(GridSizeMode.Relative, 1f, maxSize: ModSelectOverlay.HEIGHT + Footer.HEIGHT), - }, - Content = new[] - { - null, - new Drawable[] - { - FooterPanels = new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Bottom = Footer.HEIGHT }, - Children = new Drawable[] - { - BeatmapOptions = new BeatmapOptionsOverlay(), - ModSelect = CreateModSelectOverlay() - } - } - } + BeatmapOptions = new BeatmapOptionsOverlay(), } }, - Footer = new Footer() + Footer = new Footer(), + ModSelect = CreateModSelectOverlay() }); } @@ -332,7 +316,7 @@ namespace osu.Game.Screens.Select (new FooterButtonOptions(), BeatmapOptions) }; - protected virtual ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay(); + protected virtual ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(); protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { From 1744d7e4f0cd7130b0dad22da73bb708d9295c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 21:53:04 +0200 Subject: [PATCH 0808/1276] Fix new mod select overlay dimming itself --- .../Containers/OsuFocusedOverlayContainer.cs | 6 +-- osu.Game/OsuGame.cs | 45 ++++++++++++++++++- osu.Game/Screens/Select/SongSelect.cs | 21 ++++++++- 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 68351acd7e..0e8e481e9d 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -127,14 +127,14 @@ namespace osu.Game.Graphics.Containers if (didChange) samplePopIn?.Play(); - if (BlockScreenWideMouse && DimMainContent) game?.AddBlockingOverlay(this); + if (BlockScreenWideMouse && DimMainContent) game?.ShowBlockingOverlay(this); break; case Visibility.Hidden: if (didChange) samplePopOut?.Play(); - if (BlockScreenWideMouse) game?.RemoveBlockingOverlay(this); + if (BlockScreenWideMouse) game?.HideBlockingOverlay(this); break; } @@ -150,7 +150,7 @@ namespace osu.Game.Graphics.Containers protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - game?.RemoveBlockingOverlay(this); + game?.HideBlockingOverlay(this); } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e9fe8c43de..da8cdde944 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -171,6 +171,7 @@ namespace osu.Game private readonly string[] args; private readonly List focusedOverlays = new List(); + private readonly List externalOverlays = new List(); private readonly List visibleBlockingOverlays = new List(); @@ -186,19 +187,59 @@ namespace osu.Game private void updateBlockingOverlayFade() => ScreenContainer.FadeColour(visibleBlockingOverlays.Any() ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint); - public void AddBlockingOverlay(OverlayContainer overlay) + /// + /// Registers a blocking that was not created by itself for later use. + /// + /// + /// The goal of this method is to allow child screens, like to register their own full-screen blocking overlays + /// with background dim. + /// In those cases, for the dim to work correctly, the overlays need to be added at the `OsuGame` level directly, rather as children of the screens. + /// + /// + /// An that should be disposed of when the should be unregistered. + /// Disposing of this will automatically expire the . + /// + internal IDisposable RegisterBlockingOverlay(OverlayContainer overlayContainer) + { + if (overlayContainer.Parent != null) + throw new ArgumentException($@"Overlays registered via {nameof(RegisterBlockingOverlay)} should not be added to the scene graph."); + + if (externalOverlays.Contains(overlayContainer)) + throw new ArgumentException($@"{overlayContainer} has already been registered via {nameof(RegisterBlockingOverlay)} once."); + + externalOverlays.Add(overlayContainer); + overlayContent.Add(overlayContainer); + return new InvokeOnDisposal(() => unregisterBlockingOverlay(overlayContainer)); + } + + /// + /// Should be called when has been shown and should begin blocking background input. + /// + internal void ShowBlockingOverlay(OverlayContainer overlay) { if (!visibleBlockingOverlays.Contains(overlay)) visibleBlockingOverlays.Add(overlay); updateBlockingOverlayFade(); } - public void RemoveBlockingOverlay(OverlayContainer overlay) => Schedule(() => + /// + /// Should be called when a blocking has been hidden and should stop blocking background input. + /// + internal void HideBlockingOverlay(OverlayContainer overlay) => Schedule(() => { visibleBlockingOverlays.Remove(overlay); updateBlockingOverlayFade(); }); + /// + /// Unregisters a blocking that was not created by itself. + /// + private void unregisterBlockingOverlay(OverlayContainer overlayContainer) + { + externalOverlays.Remove(overlayContainer); + overlayContainer.Expire(); + } + /// /// Close all game-wide overlays. /// diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 3c46433b45..448e702b96 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -35,6 +35,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Collections; using osu.Game.Graphics.UserInterface; using System.Diagnostics; +using JetBrains.Annotations; using osu.Game.Screens.Play; using osu.Game.Database; using osu.Game.Skinning; @@ -116,9 +117,15 @@ namespace osu.Game.Screens.Select private double audioFeedbackLastPlaybackTime; + [CanBeNull] + private IDisposable modSelectOverlayRegistration; + [Resolved] private MusicController music { get; set; } + [Resolved(CanBeNull = true)] + private OsuGame game { get; set; } + [BackgroundDependencyLoader(true)] private void load(AudioManager audio, IDialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) { @@ -264,10 +271,13 @@ namespace osu.Game.Screens.Select } }, Footer = new Footer(), - ModSelect = CreateModSelectOverlay() }); } + // preload the mod select overlay for later use in `LoadComplete()`. + // therein it will be registered at the `OsuGame` level to properly function as a blocking overlay. + LoadComponent(ModSelect = CreateModSelectOverlay()); + if (Footer != null) { foreach (var (button, overlay) in CreateFooterButtons()) @@ -301,6 +311,13 @@ namespace osu.Game.Screens.Select } } + protected override void LoadComplete() + { + base.LoadComplete(); + + modSelectOverlayRegistration = game?.RegisterBlockingOverlay(ModSelect); + } + /// /// Creates the buttons to be displayed in the footer. /// @@ -700,6 +717,8 @@ namespace osu.Game.Screens.Select if (music != null) music.TrackChanged -= ensureTrackLooping; + + modSelectOverlayRegistration?.Dispose(); } /// From ac08498f35f8db294d26dfd0dba91df914cf492d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 22:17:40 +0200 Subject: [PATCH 0809/1276] Use plum colour scheme in multiplayer to match rest of multiplayer screens --- .../UserInterface/TestSceneShearedOverlayContainer.cs | 5 ++++- osu.Game/Overlays/FirstRunSetupOverlay.cs | 7 +++++-- osu.Game/Overlays/Mods/ModSelectScreen.cs | 7 +++++-- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 6 ++---- osu.Game/Overlays/Mods/UserModSelectScreen.cs | 5 +++++ osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs | 2 ++ osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 3 ++- 7 files changed, 25 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs index 5a9cafde27..4dd64c6536 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs @@ -66,7 +66,10 @@ namespace osu.Game.Tests.Visual.UserInterface public class TestShearedOverlayContainer : ShearedOverlayContainer { - protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green; + public TestShearedOverlayContainer() + : base(OverlayColourScheme.Green) + { + } [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 75778e6c4d..607bef76dd 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -31,8 +31,6 @@ namespace osu.Game.Overlays [Cached] public class FirstRunSetupOverlay : ShearedOverlayContainer { - protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Purple; - [Resolved] private IPerformFromScreenRunner performer { get; set; } = null!; @@ -70,6 +68,11 @@ namespace osu.Game.Overlays private Container content = null!; + public FirstRunSetupOverlay() + : base(OverlayColourScheme.Purple) + { + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index a7f8a167f9..bf47e4351a 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -27,8 +27,6 @@ namespace osu.Game.Overlays.Mods { public abstract class ModSelectScreen : ShearedOverlayContainer { - protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green; - [Cached] public Bindable> SelectedMods { get; private set; } = new Bindable>(Array.Empty()); @@ -65,6 +63,11 @@ namespace osu.Game.Overlays.Mods private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; + protected ModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green) + : base(colourScheme) + { + } + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index eca192c8e5..92e88bfaaf 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -51,17 +51,15 @@ namespace osu.Game.Overlays.Mods /// protected Container FooterContent { get; private set; } - protected abstract OverlayColourScheme ColourScheme { get; } - protected override bool StartHidden => true; protected override bool BlockNonPositionalInput => true; - protected ShearedOverlayContainer() + protected ShearedOverlayContainer(OverlayColourScheme colourScheme) { RelativeSizeAxes = Axes.Both; - ColourProvider = new OverlayColourProvider(ColourScheme); + ColourProvider = new OverlayColourProvider(colourScheme); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Mods/UserModSelectScreen.cs b/osu.Game/Overlays/Mods/UserModSelectScreen.cs index ca33d35605..a018797cba 100644 --- a/osu.Game/Overlays/Mods/UserModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/UserModSelectScreen.cs @@ -12,6 +12,11 @@ namespace osu.Game.Overlays.Mods { public class UserModSelectScreen : ModSelectScreen { + public UserModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green) + : base(colourScheme) + { + } + protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys); protected override IReadOnlyList ComputeNewModsFromSelection(IReadOnlyList oldSelection, IReadOnlyList newSelection) diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs index 5a7a60b479..63b7d1dbb5 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; using osuTK.Input; @@ -20,6 +21,7 @@ namespace osu.Game.Screens.OnlinePlay } public FreeModSelectScreen() + : base(OverlayColourScheme.Plum) { IsValidMod = _ => true; } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 0eeb32230d..d8474ef86a 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -14,6 +14,7 @@ using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.Rooms; +using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -153,7 +154,7 @@ namespace osu.Game.Screens.OnlinePlay return base.OnExiting(e); } - protected override ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen + protected override ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(OverlayColourScheme.Plum) { IsValidMod = IsValidMod }; From 4eefbd5bc21af4b34fd374fd255b29dacca3d195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 22:25:13 +0200 Subject: [PATCH 0810/1276] Use new free mod select design in room creation flow --- .../Screens/OnlinePlay/OnlinePlaySongSelect.cs | 17 ++++++++++++++--- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index d8474ef86a..659084addf 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -46,7 +46,6 @@ namespace osu.Game.Screens.OnlinePlay protected readonly Bindable> FreeMods = new Bindable>(Array.Empty()); - private readonly FreeModSelectOverlay freeModSelectOverlay; private readonly Room room; private WorkingBeatmap initialBeatmap; @@ -54,13 +53,16 @@ namespace osu.Game.Screens.OnlinePlay private IReadOnlyList initialMods; private bool itemSelected; + private FreeModSelectScreen freeModSelectOverlay; + private IDisposable freeModSelectOverlayRegistration; + protected OnlinePlaySongSelect(Room room) { this.room = room; Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; - freeModSelectOverlay = new FreeModSelectOverlay + freeModSelectOverlay = new FreeModSelectScreen { SelectedMods = { BindTarget = FreeMods }, IsValidMod = IsValidFreeMod, @@ -76,7 +78,7 @@ namespace osu.Game.Screens.OnlinePlay initialRuleset = Ruleset.Value; initialMods = Mods.Value.ToList(); - FooterPanels.Add(freeModSelectOverlay); + LoadComponent(freeModSelectOverlay); } protected override void LoadComplete() @@ -95,6 +97,8 @@ namespace osu.Game.Screens.OnlinePlay Mods.BindValueChanged(onModsChanged); Ruleset.BindValueChanged(onRulesetChanged); + + freeModSelectOverlayRegistration = Game?.RegisterBlockingOverlay(freeModSelectOverlay); } private void onModsChanged(ValueChangedEvent> mods) @@ -183,5 +187,12 @@ namespace osu.Game.Screens.OnlinePlay private bool checkCompatibleFreeMod(Mod mod) => Mods.Value.All(m => m.Acronym != mod.Acronym) // Mod must not be contained in the required mods. && ModUtils.CheckCompatibleSet(Mods.Value.Append(mod).ToArray()); // Mod must be compatible with all the required mods. + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + freeModSelectOverlayRegistration?.Dispose(); + } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 448e702b96..55d28ec8a4 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Select private MusicController music { get; set; } [Resolved(CanBeNull = true)] - private OsuGame game { get; set; } + protected new OsuGame Game { get; private set; } [BackgroundDependencyLoader(true)] private void load(AudioManager audio, IDialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) @@ -315,7 +315,7 @@ namespace osu.Game.Screens.Select { base.LoadComplete(); - modSelectOverlayRegistration = game?.RegisterBlockingOverlay(ModSelect); + modSelectOverlayRegistration = Game?.RegisterBlockingOverlay(ModSelect); } /// From 66473972daafbcf0b1ec7358a21dbae001ec149f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 May 2022 22:30:20 +0200 Subject: [PATCH 0811/1276] Use new user mod select design in multiplayer lobby --- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index cc1f842f8c..5b892e1925 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -57,6 +58,9 @@ namespace osu.Game.Screens.OnlinePlay.Match protected readonly IBindable RoomId = new Bindable(); + [Resolved] + private OsuGame game { get; set; } + [Resolved] private MusicController music { get; set; } @@ -77,7 +81,11 @@ namespace osu.Game.Screens.OnlinePlay.Match public readonly Room Room; private readonly bool allowEdit; - private ModSelectOverlay userModsSelectOverlay; + private ModSelectScreen userModsSelectOverlay; + + [CanBeNull] + private IDisposable userModsSelectOverlayRegistration; + private RoomSettingsOverlay settingsOverlay; private Drawable mainContent; @@ -180,11 +188,6 @@ namespace osu.Game.Screens.OnlinePlay.Match Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = userModsSelectOverlay = new UserModSelectOverlay - { - SelectedMods = { BindTarget = UserMods }, - IsValidMod = _ => false - } }, } } @@ -227,6 +230,12 @@ namespace osu.Game.Screens.OnlinePlay.Match } } }; + + LoadComponent(userModsSelectOverlay = new UserModSelectScreen(OverlayColourScheme.Plum) + { + SelectedMods = { BindTarget = UserMods }, + IsValidMod = _ => false + }); } protected override void LoadComplete() @@ -254,6 +263,8 @@ namespace osu.Game.Screens.OnlinePlay.Match beatmapAvailabilityTracker.SelectedItem.BindTo(SelectedItem); beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateWorkingBeatmap()); + + userModsSelectOverlayRegistration = game?.RegisterBlockingOverlay(userModsSelectOverlay); } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -459,5 +470,12 @@ namespace osu.Game.Screens.OnlinePlay.Match public class UserModSelectButton : PurpleTriangleButton { } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + userModsSelectOverlayRegistration?.Dispose(); + } } } From a56eab2c4757dfb3955e3eefb507c051c723f5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 15:47:10 +0200 Subject: [PATCH 0812/1276] Extract interface for overlay management --- .../Containers/OsuFocusedOverlayContainer.cs | 12 ++--- osu.Game/OsuGame.cs | 36 +++++---------- osu.Game/Overlays/IOverlayManager.cs | 44 +++++++++++++++++++ .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 6 +-- .../OnlinePlay/OnlinePlaySongSelect.cs | 4 +- osu.Game/Screens/Select/SongSelect.cs | 4 +- 6 files changed, 69 insertions(+), 37 deletions(-) create mode 100644 osu.Game/Overlays/IOverlayManager.cs diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 0e8e481e9d..512602d120 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Graphics.Containers protected virtual bool DimMainContent => true; [Resolved(CanBeNull = true)] - private OsuGame game { get; set; } + private IOverlayManager overlayManager { get; set; } [Resolved] private PreviewTrackManager previewTrackManager { get; set; } @@ -50,8 +50,8 @@ namespace osu.Game.Graphics.Containers protected override void LoadComplete() { - if (game != null) - OverlayActivationMode.BindTo(game.OverlayActivationMode); + if (overlayManager != null) + OverlayActivationMode.BindTo(overlayManager.OverlayActivationMode); OverlayActivationMode.BindValueChanged(mode => { @@ -127,14 +127,14 @@ namespace osu.Game.Graphics.Containers if (didChange) samplePopIn?.Play(); - if (BlockScreenWideMouse && DimMainContent) game?.ShowBlockingOverlay(this); + if (BlockScreenWideMouse && DimMainContent) overlayManager?.ShowBlockingOverlay(this); break; case Visibility.Hidden: if (didChange) samplePopOut?.Play(); - if (BlockScreenWideMouse) game?.HideBlockingOverlay(this); + if (BlockScreenWideMouse) overlayManager?.HideBlockingOverlay(this); break; } @@ -150,7 +150,7 @@ namespace osu.Game.Graphics.Containers protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - game?.HideBlockingOverlay(this); + overlayManager?.HideBlockingOverlay(this); } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index da8cdde944..fcf5889a7e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -63,7 +63,7 @@ namespace osu.Game /// The full osu! experience. Builds on top of to add menus and binding logic /// for initial components that are generally retrieved via DI. /// - public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo, IPerformFromScreenRunner + public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo, IPerformFromScreenRunner, IOverlayManager { /// /// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications). @@ -184,48 +184,34 @@ namespace osu.Game SentryLogger = new SentryLogger(this); } + #region IOverlayManager + + IBindable IOverlayManager.OverlayActivationMode => OverlayActivationMode; + private void updateBlockingOverlayFade() => ScreenContainer.FadeColour(visibleBlockingOverlays.Any() ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint); - /// - /// Registers a blocking that was not created by itself for later use. - /// - /// - /// The goal of this method is to allow child screens, like to register their own full-screen blocking overlays - /// with background dim. - /// In those cases, for the dim to work correctly, the overlays need to be added at the `OsuGame` level directly, rather as children of the screens. - /// - /// - /// An that should be disposed of when the should be unregistered. - /// Disposing of this will automatically expire the . - /// - internal IDisposable RegisterBlockingOverlay(OverlayContainer overlayContainer) + IDisposable IOverlayManager.RegisterBlockingOverlay(OverlayContainer overlayContainer) { if (overlayContainer.Parent != null) - throw new ArgumentException($@"Overlays registered via {nameof(RegisterBlockingOverlay)} should not be added to the scene graph."); + throw new ArgumentException($@"Overlays registered via {nameof(IOverlayManager.RegisterBlockingOverlay)} should not be added to the scene graph."); if (externalOverlays.Contains(overlayContainer)) - throw new ArgumentException($@"{overlayContainer} has already been registered via {nameof(RegisterBlockingOverlay)} once."); + throw new ArgumentException($@"{overlayContainer} has already been registered via {nameof(IOverlayManager.RegisterBlockingOverlay)} once."); externalOverlays.Add(overlayContainer); overlayContent.Add(overlayContainer); return new InvokeOnDisposal(() => unregisterBlockingOverlay(overlayContainer)); } - /// - /// Should be called when has been shown and should begin blocking background input. - /// - internal void ShowBlockingOverlay(OverlayContainer overlay) + void IOverlayManager.ShowBlockingOverlay(OverlayContainer overlay) { if (!visibleBlockingOverlays.Contains(overlay)) visibleBlockingOverlays.Add(overlay); updateBlockingOverlayFade(); } - /// - /// Should be called when a blocking has been hidden and should stop blocking background input. - /// - internal void HideBlockingOverlay(OverlayContainer overlay) => Schedule(() => + void IOverlayManager.HideBlockingOverlay(OverlayContainer overlay) => Schedule(() => { visibleBlockingOverlays.Remove(overlay); updateBlockingOverlayFade(); @@ -240,6 +226,8 @@ namespace osu.Game overlayContainer.Expire(); } + #endregion + /// /// Close all game-wide overlays. /// diff --git a/osu.Game/Overlays/IOverlayManager.cs b/osu.Game/Overlays/IOverlayManager.cs new file mode 100644 index 0000000000..940ee2d8db --- /dev/null +++ b/osu.Game/Overlays/IOverlayManager.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Game.Screens.Select; + +namespace osu.Game.Overlays +{ + [Cached] + internal interface IOverlayManager + { + /// + /// Whether overlays should be able to be opened game-wide. Value is sourced from the current active screen. + /// + IBindable OverlayActivationMode { get; } + + /// + /// Registers a blocking that was not created by itself for later use. + /// + /// + /// The goal of this method is to allow child screens, like to register their own full-screen blocking overlays + /// with background dim. + /// In those cases, for the dim to work correctly, the overlays need to be added at a game level directly, rather as children of the screens. + /// + /// + /// An that should be disposed of when the should be unregistered. + /// Disposing of this will automatically expire the . + /// + IDisposable RegisterBlockingOverlay(OverlayContainer overlayContainer); + + /// + /// Should be called when has been shown and should begin blocking background input. + /// + void ShowBlockingOverlay(OverlayContainer overlay); + + /// + /// Should be called when a blocking has been hidden and should stop blocking background input. + /// + void HideBlockingOverlay(OverlayContainer overlay); + } +} diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 5b892e1925..b7a9b45bb8 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -58,8 +58,8 @@ namespace osu.Game.Screens.OnlinePlay.Match protected readonly IBindable RoomId = new Bindable(); - [Resolved] - private OsuGame game { get; set; } + [Resolved(CanBeNull = true)] + private IOverlayManager overlayManager { get; set; } [Resolved] private MusicController music { get; set; } @@ -264,7 +264,7 @@ namespace osu.Game.Screens.OnlinePlay.Match beatmapAvailabilityTracker.SelectedItem.BindTo(SelectedItem); beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateWorkingBeatmap()); - userModsSelectOverlayRegistration = game?.RegisterBlockingOverlay(userModsSelectOverlay); + userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(userModsSelectOverlay); } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 659084addf..c262544d6c 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.OnlinePlay private IReadOnlyList initialMods; private bool itemSelected; - private FreeModSelectScreen freeModSelectOverlay; + private readonly FreeModSelectScreen freeModSelectOverlay; private IDisposable freeModSelectOverlayRegistration; protected OnlinePlaySongSelect(Room room) @@ -98,7 +98,7 @@ namespace osu.Game.Screens.OnlinePlay Mods.BindValueChanged(onModsChanged); Ruleset.BindValueChanged(onRulesetChanged); - freeModSelectOverlayRegistration = Game?.RegisterBlockingOverlay(freeModSelectOverlay); + freeModSelectOverlayRegistration = OverlayManager?.RegisterBlockingOverlay(freeModSelectOverlay); } private void onModsChanged(ValueChangedEvent> mods) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 55d28ec8a4..bb2746ee95 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Select private MusicController music { get; set; } [Resolved(CanBeNull = true)] - protected new OsuGame Game { get; private set; } + internal IOverlayManager OverlayManager { get; private set; } [BackgroundDependencyLoader(true)] private void load(AudioManager audio, IDialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) @@ -315,7 +315,7 @@ namespace osu.Game.Screens.Select { base.LoadComplete(); - modSelectOverlayRegistration = Game?.RegisterBlockingOverlay(ModSelect); + modSelectOverlayRegistration = OverlayManager?.RegisterBlockingOverlay(ModSelect); } /// From fdb21fedaba887647523f0c25fdad6c3bc0775d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 16:31:02 +0200 Subject: [PATCH 0813/1276] Implement `IOverlayManager` in `ScreenTestScene` --- osu.Game/Tests/Visual/ScreenTestScene.cs | 33 ++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/ScreenTestScene.cs b/osu.Game/Tests/Visual/ScreenTestScene.cs index e9069d8c9c..0fa2f3e786 100644 --- a/osu.Game/Tests/Visual/ScreenTestScene.cs +++ b/osu.Game/Tests/Visual/ScreenTestScene.cs @@ -1,12 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Development; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Framework.Testing; +using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Screens; @@ -15,11 +18,12 @@ namespace osu.Game.Tests.Visual /// /// A test case which can be used to test a screen (that relies on OnEntering being called to execute startup instructions). /// - public abstract class ScreenTestScene : OsuManualInputManagerTestScene + public abstract class ScreenTestScene : OsuManualInputManagerTestScene, IOverlayManager { protected readonly OsuScreenStack Stack; private readonly Container content; + private readonly Container overlayContent; protected override Container Content => content; @@ -36,7 +40,11 @@ namespace osu.Game.Tests.Visual RelativeSizeAxes = Axes.Both }, content = new Container { RelativeSizeAxes = Axes.Both }, - DialogOverlay = new DialogOverlay() + overlayContent = new Container + { + RelativeSizeAxes = Axes.Both, + Child = DialogOverlay = new DialogOverlay() + } }); Stack.ScreenPushed += (lastScreen, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed → {newScreen}"); @@ -65,5 +73,26 @@ namespace osu.Game.Tests.Visual return false; }); } + + #region IOverlayManager + + IBindable IOverlayManager.OverlayActivationMode { get; } = new Bindable(OverlayActivation.All); + + // in the blocking methods below it is important to be careful about threading (e.g. use `Expire()` rather than `Remove()`, and schedule transforms), + // because in the worst case the clean-up methods could be called from async disposal. + + IDisposable IOverlayManager.RegisterBlockingOverlay(OverlayContainer overlayContainer) + { + overlayContent.Add(overlayContainer); + return new InvokeOnDisposal(() => overlayContainer.Expire()); + } + + void IOverlayManager.ShowBlockingOverlay(OverlayContainer overlay) + => Schedule(() => Stack.FadeColour(OsuColour.Gray(0.5f), 500, Easing.OutQuint)); + + void IOverlayManager.HideBlockingOverlay(OverlayContainer overlay) + => Schedule(() => Stack.FadeColour(Colour4.White, 500, Easing.OutQuint)); + + #endregion } } From 0caea7717641ad85849219257ea563e2ad8e53f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 17:27:21 +0200 Subject: [PATCH 0814/1276] Fix mod selects not hiding when their owner screens exit --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 10 ++++++++-- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 2 ++ osu.Game/Screens/Select/SongSelect.cs | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index b7a9b45bb8..ec4e329f7a 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -309,7 +309,7 @@ namespace osu.Game.Screens.OnlinePlay.Match public override void OnSuspending(ScreenTransitionEvent e) { - endHandlingTrack(); + onLeaving(); base.OnSuspending(e); } @@ -327,7 +327,7 @@ namespace osu.Game.Screens.OnlinePlay.Match RoomManager?.PartRoom(); Mods.Value = Array.Empty(); - endHandlingTrack(); + onLeaving(); return base.OnExiting(e); } @@ -423,6 +423,12 @@ namespace osu.Game.Screens.OnlinePlay.Match Beatmap.BindValueChanged(applyLoopingToTrack, true); } + private void onLeaving() + { + userModsSelectOverlay.Hide(); + endHandlingTrack(); + } + private void endHandlingTrack() { Beatmap.ValueChanged -= applyLoopingToTrack; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index c262544d6c..c4503773ad 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -155,6 +155,8 @@ namespace osu.Game.Screens.OnlinePlay Mods.Value = initialMods; } + freeModSelectOverlay.Hide(); + return base.OnExiting(e); } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index bb2746ee95..eb5e996972 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -659,6 +659,7 @@ namespace osu.Game.Screens.Select return true; beatmapInfoWedge.Hide(); + ModSelect.Hide(); this.FadeOut(100); From 8f65e0e60f4931f736e6b342e18b6a67e1ecbb4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 21:08:19 +0200 Subject: [PATCH 0815/1276] Add failing test coverage for toggling mod overlay with hotkey --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index d27f16a624..083e24be7b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -18,6 +19,7 @@ using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; +using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -918,6 +920,19 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("check ruleset is correct for score", () => Ruleset.Value.OnlineID == 0); } + [Test] + public void TestModOverlayToggling() + { + changeRuleset(0); + createSongSelect(); + + AddStep("toggle mod overlay on", () => InputManager.Key(Key.F1)); + AddUntilStep("mod overlay shown", () => songSelect.ModSelect.State.Value == Visibility.Visible); + + AddStep("toggle mod overlay off", () => InputManager.Key(Key.F1)); + AddUntilStep("mod overlay hidden", () => songSelect.ModSelect.State.Value == Visibility.Hidden); + } + private void waitForInitialSelection() { AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); @@ -993,6 +1008,7 @@ namespace osu.Game.Tests.Visual.SongSelect public WorkingBeatmap CurrentBeatmap => Beatmap.Value; public IWorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public new BeatmapCarousel Carousel => base.Carousel; + public new ModSelectScreen ModSelect => base.ModSelect; public new void PresentScore(ScoreInfo score) => base.PresentScore(score); From 34cf4c6a3895557d6dc5f1d07b702857b512ccca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 21:22:07 +0200 Subject: [PATCH 0816/1276] Fix mod overlay not closing on toggle hotkey --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 21 +++++++++++++++++++ .../OnlinePlay/OnlinePlaySongSelect.cs | 2 ++ osu.Game/Screens/Select/SongSelect.cs | 5 ++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index bf47e4351a..7c0ed941c6 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Select; using osuTK; using osuTK.Input; @@ -44,6 +45,15 @@ namespace osu.Game.Overlays.Mods } } + /// + /// Hotkey that should be used to hide the mod select screen. + /// + /// + /// This is handled locally here rather than via , because this overlay is being registered at the game level + /// and therefore takes away keyboard focus from the screen stack. + /// + internal GlobalAction? Hotkey { get; set; } + /// /// Whether configurable s can be configured by the local user. /// @@ -321,12 +331,23 @@ namespace osu.Game.Overlays.Mods public override bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (e.Action == GlobalAction.Back && customisationVisible.Value) { customisationVisible.Value = false; return true; } + if (e.Action == Hotkey) + { + if (customisationVisible.Value) + customisationVisible.Value = false; + Hide(); + return true; + } + return base.OnPressed(e); } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index c4503773ad..4b6c06481b 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Overlays; @@ -162,6 +163,7 @@ namespace osu.Game.Screens.OnlinePlay protected override ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(OverlayColourScheme.Plum) { + Hotkey = GlobalAction.ToggleModSelection, IsValidMod = IsValidMod }; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index eb5e996972..7317b20f88 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -333,7 +333,10 @@ namespace osu.Game.Screens.Select (new FooterButtonOptions(), BeatmapOptions) }; - protected virtual ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(); + protected virtual ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen + { + Hotkey = GlobalAction.ToggleModSelection + }; protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { From 17c19063f7945ad3d40287f471875c0a109835f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 21:24:11 +0200 Subject: [PATCH 0817/1276] Fix user dim test scene accumulating no fail instances in `SelectedMods` The old design was apparently somehow tolerant of multiple instances of the same mod in `SelectedMods`, but the new one is not. Fix the test, because appending to a list of mods that was getting modified by previous tests is generally not how tests should be written. --- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index f7140537ee..ef115ab66b 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using System.Threading; using NUnit.Framework; using osu.Framework.Allocation; @@ -283,7 +282,7 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmapInfo != null); AddStep("Set default user settings", () => { - SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray(); + SelectedMods.Value = new[] { new OsuModNoFail() }; songSelect.DimLevel.Value = 0.7f; songSelect.BlurLevel.Value = 0.4f; }); From 199bdb8e7ded820c6a0b1663ef307d8564f9a831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 21:43:39 +0200 Subject: [PATCH 0818/1276] Replace reference to old mod select in multiplayer test --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 6a69917fb4..8e26d1c562 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -622,7 +622,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("invoke on back button", () => multiplayerComponents.OnBackButton()); - AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); + AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden); From ecc28050992e69ec8a1068ede63d8cd313623680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 21:47:03 +0200 Subject: [PATCH 0819/1276] Replace references to old mod select classes in match song select test Also note the change from `songSelect.ChildrenOfType<>()...` to `this.ChildrenOfType<>()...` - because the new design is registered at game-level, the mod select is not a child of the song select screen anymore. --- .../Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 714951cc42..d170b12dc3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void assertHasFreeModButton(Type type, bool hasButton = true) { AddAssert($"{type.ReadableName()} {(hasButton ? "displayed" : "not displayed")} in freemod overlay", - () => songSelect.ChildrenOfType().Single().ChildrenOfType().All(b => b.Mod.GetType() != type)); + () => this.ChildrenOfType().Single().ChildrenOfType().All(b => b.Mod.GetType() != type)); } private class TestMultiplayerMatchSongSelect : MultiplayerMatchSongSelect From d296f78dfe097fdf70c759a416538ef7a1fa3fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 21:59:44 +0200 Subject: [PATCH 0820/1276] Replace references to old mod select classes in multiplayer match screen test --- .../Multiplayer/TestSceneMultiplayerMatchSubScreen.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 057032c413..2abde82e92 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -168,8 +168,13 @@ namespace osu.Game.Tests.Visual.Multiplayer ClickButtonWhenEnabled(); + AddUntilStep("mod select contents loaded", + () => this.ChildrenOfType().Any() && this.ChildrenOfType().All(col => col.IsLoaded && col.ItemsLoaded)); AddUntilStep("mod select contains only double time mod", - () => this.ChildrenOfType().SingleOrDefault()?.ChildrenOfType().SingleOrDefault()?.Mod is OsuModDoubleTime); + () => this.ChildrenOfType() + .SingleOrDefault()? + .ChildrenOfType() + .SingleOrDefault(panel => !panel.Filtered.Value)?.Mod is OsuModDoubleTime); } } } From a2ab79620a2a7dd63260a23951b9b2d572046d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 5 May 2022 22:15:49 +0200 Subject: [PATCH 0821/1276] Update song select exit via click test to match new expectations --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 8ba074a7d1..f8eee7be56 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -253,12 +253,12 @@ namespace osu.Game.Tests.Visual.Navigation PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show()); AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); - AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition)); - // BackButton handles hover using its child button, so this checks whether or not any of BackButton's children are hovered. - AddUntilStep("Back button is hovered", () => Game.ChildrenOfType().First().Children.Any(c => c.IsHovered)); + AddStep("Move mouse to dimmed area", () => InputManager.MoveMouseTo(new Vector2( + songSelect.ScreenSpaceDrawQuad.TopLeft.X + 1, + songSelect.ScreenSpaceDrawQuad.TopLeft.Y + songSelect.ScreenSpaceDrawQuad.Height / 2))); + AddStep("Click left mouse button", () => InputManager.Click(MouseButton.Left)); - AddStep("Click back button", () => InputManager.Click(MouseButton.Left)); AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden); exitViaBackButtonAndConfirm(); } From 16a6c11bc87a906b36e699fcd2101eac371dcb25 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Thu, 5 May 2022 21:39:31 +0100 Subject: [PATCH 0822/1276] Clamp maximum and minimum height the chat overlay can be resized to --- osu.Game/Overlays/ChatOverlayV2.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index cab88136fc..8b6fe70f3c 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -3,6 +3,7 @@ #nullable enable +using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; @@ -44,8 +45,11 @@ namespace osu.Game.Overlays private bool isDraggingTopBar; private float dragStartChatHeight; - private const int transition_length = 500; + private const float min_chat_height = 0.2f; + private const float max_chat_height = 1f; private const float default_chat_height = 0.4f; + + private const int transition_length = 500; private const float top_bar_height = 40; private const float side_bar_width = 190; private const float chat_bar_height = 60; @@ -215,7 +219,7 @@ namespace osu.Game.Overlays return; float targetChatHeight = dragStartChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; - chatHeight.Value = targetChatHeight; + chatHeight.Value = Math.Clamp(targetChatHeight, min_chat_height, max_chat_height); } protected override void OnDragEnd(DragEndEvent e) From 0974de8fb007afdca51520ac44204c408a6d3523 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Thu, 5 May 2022 23:13:32 +0100 Subject: [PATCH 0823/1276] Use `BindableFloat` for chat height --- osu.Game/Overlays/ChatOverlayV2.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 8b6fe70f3c..4c7fa0f802 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -3,7 +3,6 @@ #nullable enable -using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; @@ -40,16 +39,13 @@ namespace osu.Game.Overlays private ChatTextBar textBar = null!; private Container currentChannelContainer = null!; - private readonly Bindable chatHeight = new Bindable(); + private readonly BindableFloat chatHeight = new BindableFloat(); private bool isDraggingTopBar; private float dragStartChatHeight; - private const float min_chat_height = 0.2f; - private const float max_chat_height = 1f; - private const float default_chat_height = 0.4f; - private const int transition_length = 500; + private const float default_chat_height = 0.4f; private const float top_bar_height = 40; private const float side_bar_width = 190; private const float chat_bar_height = 60; @@ -219,7 +215,7 @@ namespace osu.Game.Overlays return; float targetChatHeight = dragStartChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; - chatHeight.Value = Math.Clamp(targetChatHeight, min_chat_height, max_chat_height); + chatHeight.Value = targetChatHeight; } protected override void OnDragEnd(DragEndEvent e) From 7ffe3b132fbe12ff2f8689fbc330aecf90bd1249 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Thu, 5 May 2022 23:13:43 +0100 Subject: [PATCH 0824/1276] Use `BindableFloat` in chat height tests --- .../Visual/Online/TestSceneChatOverlayV2.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 98574e5d53..7c77ac925e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -133,22 +133,22 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestChatHeight() { - Bindable configChatHeight = null; + BindableFloat configChatHeight = new BindableFloat(); + config.BindWith(OsuSetting.ChatDisplayHeight, configChatHeight); float newHeight = 0; - AddStep("Bind config chat height", () => configChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight).GetBoundCopy()); - AddStep("Set config chat height", () => configChatHeight.Value = 0.4f); + AddStep("Reset config chat height", () => configChatHeight.SetDefault()); AddStep("Show overlay", () => chatOverlay.Show()); - AddAssert("Overlay uses config height", () => chatOverlay.Height == 0.4f); - AddStep("Drag overlay to new height", () => + AddAssert("Overlay uses config height", () => chatOverlay.Height == configChatHeight.Default); + AddStep("Click top bar", () => { InputManager.MoveMouseTo(chatOverlayTopBar); InputManager.PressButton(MouseButton.Left); - InputManager.MoveMouseTo(chatOverlayTopBar, new Vector2(0, -300)); - InputManager.ReleaseButton(MouseButton.Left); }); + AddStep("Drag overlay to new height", () => InputManager.MoveMouseTo(chatOverlayTopBar, new Vector2(0, -300))); + AddStep("Stop dragging", () => InputManager.ReleaseButton(MouseButton.Left)); AddStep("Store new height", () => newHeight = chatOverlay.Height); - AddAssert("Config height changed", () => configChatHeight.Value != 0.4f && configChatHeight.Value == newHeight); + AddAssert("Config height changed", () => !configChatHeight.IsDefault && configChatHeight.Value == newHeight); AddStep("Hide overlay", () => chatOverlay.Hide()); AddStep("Show overlay", () => chatOverlay.Show()); AddAssert("Overlay uses new height", () => chatOverlay.Height == newHeight); From be960eb092aacf2f8651d5099b1eb2d1eb09a8a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 14:34:31 +0900 Subject: [PATCH 0825/1276] Move dangerous hold specification to base class --- .../Containers/HoldToConfirmContainer.cs | 32 ++++++++++---- .../Dialog/PopupDialogDangerousButton.cs | 7 ++-- .../Screens/Play/HUD/HoldForMenuButton.cs | 42 +++++++------------ 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index c74245461d..078721ec77 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -12,6 +12,13 @@ namespace osu.Game.Graphics.Containers { public abstract class HoldToConfirmContainer : Container { + public const double DANGEROUS_HOLD_ACTIVATION_DELAY = 500; + + /// + /// Whether the associated action is considered dangerous, warranting a longer hold. + /// + public bool IsDangerousAction { get; } + public Action Action; private const int fadeout_delay = 200; @@ -29,12 +36,9 @@ namespace osu.Game.Graphics.Containers protected virtual bool AllowMultipleFires => false; /// - /// Specify a custom activation delay, overriding the game-wide user setting. + /// The current activation delay for this control. /// - /// - /// This should be used in special cases where we want to be extra sure the user knows what they are doing. An example is when changes would be lost. - /// - protected virtual double? HoldActivationDelay => null; + protected IBindable HoldActivationDelay => holdActivationDelay; public Bindable Progress = new BindableDouble(); @@ -43,13 +47,25 @@ namespace osu.Game.Graphics.Containers [Resolved] private OsuConfigManager config { get; set; } + protected HoldToConfirmContainer(bool isDangerousAction = false) + { + IsDangerousAction = isDangerousAction; + } + protected override void LoadComplete() { base.LoadComplete(); - holdActivationDelay = HoldActivationDelay != null - ? new Bindable(HoldActivationDelay.Value) - : config.GetBindable(OsuSetting.UIHoldActivationDelay); + if (IsDangerousAction) + { + holdActivationDelay.Value = DANGEROUS_HOLD_ACTIVATION_DELAY; + } + else + { + holdActivationDelay = HoldActivationDelay != null + ? new Bindable(HoldActivationDelay.Value) + : config.GetBindable(OsuSetting.UIHoldActivationDelay); + } } protected void BeginConfirm() diff --git a/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs b/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs index 6c775f44f8..6239c5e409 100644 --- a/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs +++ b/osu.Game/Overlays/Dialog/PopupDialogDangerousButton.cs @@ -12,8 +12,6 @@ namespace osu.Game.Overlays.Dialog { public class PopupDialogDangerousButton : PopupDialogButton { - public const double DANGEROUS_HOLD_ACTIVATION_DELAY = 500; - private Box progressBox; private DangerousConfirmContainer confirmContainer; @@ -44,7 +42,10 @@ namespace osu.Game.Overlays.Dialog private class DangerousConfirmContainer : HoldToConfirmContainer { - protected override double? HoldActivationDelay => DANGEROUS_HOLD_ACTIVATION_DELAY; + public DangerousConfirmContainer() + : base(isDangerousAction: true) + { + } protected override bool OnMouseDown(MouseDownEvent e) { diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index c6aa3fbe08..9d280a1737 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -13,12 +13,10 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Utils; -using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; -using osu.Game.Overlays.Dialog; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -29,20 +27,11 @@ namespace osu.Game.Screens.Play.HUD public readonly Bindable IsPaused = new Bindable(); - private readonly Button button; + private Button button; - public Action Action - { - set => button.Action = value; - } + public Action Action { get; set; } - private readonly OsuSpriteText text; - - [Resolved] - private OsuConfigManager config { get; set; } - - [Resolved(canBeNull: true)] - private Player player { get; set; } + private OsuSpriteText text; private readonly Bindable activationDelay = new Bindable(); @@ -51,6 +40,11 @@ namespace osu.Game.Screens.Play.HUD Direction = FillDirection.Horizontal; Spacing = new Vector2(20, 0); Margin = new MarginPadding(10); + } + + [BackgroundDependencyLoader(true)] + private void load(Player player) + { Children = new Drawable[] { text = new OsuSpriteText @@ -59,11 +53,12 @@ namespace osu.Game.Screens.Play.HUD Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft }, - button = new Button + button = new Button(player?.Configuration.AllowRestart == false) { HoverGained = () => text.FadeIn(500, Easing.OutQuint), HoverLost = () => text.FadeOut(500, Easing.OutQuint), - IsPaused = { BindTarget = IsPaused } + IsPaused = { BindTarget = IsPaused }, + Action = () => Action(), } }; AutoSizeAxes = Axes.Both; @@ -71,13 +66,6 @@ namespace osu.Game.Screens.Play.HUD protected override void LoadComplete() { - if (player?.Configuration.AllowRestart == false) - { - activationDelay.Value = PopupDialogDangerousButton.DANGEROUS_HOLD_ACTIVATION_DELAY; - } - else - config.BindWith(OsuSetting.UIHoldActivationDelay, activationDelay); - activationDelay.BindValueChanged(v => { text.Text = v.NewValue > 0 @@ -125,10 +113,10 @@ namespace osu.Game.Screens.Play.HUD public Action HoverGained; public Action HoverLost; - [Resolved(canBeNull: true)] - private Player player { get; set; } - - protected override double? HoldActivationDelay => player?.Configuration.AllowRestart == false ? PopupDialogDangerousButton.DANGEROUS_HOLD_ACTIVATION_DELAY : (double?)null; + public Button(bool isDangerousAction) + : base(isDangerousAction) + { + } [BackgroundDependencyLoader] private void load(OsuColour colours) From a11771c11b82992c2b5dfdda3acfcc09c59121e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 14:37:50 +0900 Subject: [PATCH 0826/1276] Better encapsulate exposed bindables of `HoldToConfirmContainer` --- .../Containers/HoldToConfirmContainer.cs | 40 ++++++++++--------- .../Screens/Play/HUD/HoldForMenuButton.cs | 2 +- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index 078721ec77..540f9b97a7 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -14,15 +14,18 @@ namespace osu.Game.Graphics.Containers { public const double DANGEROUS_HOLD_ACTIVATION_DELAY = 500; + private const int fadeout_delay = 200; + /// /// Whether the associated action is considered dangerous, warranting a longer hold. /// public bool IsDangerousAction { get; } + /// + /// The action to perform when a hold successfully completes. + /// public Action Action; - private const int fadeout_delay = 200; - /// /// Whether currently in a fired state (and the confirm has been sent). /// @@ -30,19 +33,24 @@ namespace osu.Game.Graphics.Containers private bool confirming; + /// + /// The current activation delay for this control. + /// + public IBindable HoldActivationDelay => holdActivationDelay; + + /// + /// The progress of any ongoing hold operation. 0 means no hold has started; 1 means a hold has been completed. + /// + public IBindable Progress => progress; + /// /// Whether the overlay should be allowed to return from a fired state. /// protected virtual bool AllowMultipleFires => false; - /// - /// The current activation delay for this control. - /// - protected IBindable HoldActivationDelay => holdActivationDelay; + private readonly Bindable progress = new BindableDouble(); - public Bindable Progress = new BindableDouble(); - - private Bindable holdActivationDelay; + private readonly Bindable holdActivationDelay = new Bindable(); [Resolved] private OsuConfigManager config { get; set; } @@ -57,15 +65,9 @@ namespace osu.Game.Graphics.Containers base.LoadComplete(); if (IsDangerousAction) - { holdActivationDelay.Value = DANGEROUS_HOLD_ACTIVATION_DELAY; - } else - { - holdActivationDelay = HoldActivationDelay != null - ? new Bindable(HoldActivationDelay.Value) - : config.GetBindable(OsuSetting.UIHoldActivationDelay); - } + config.BindWith(OsuSetting.UIHoldActivationDelay, holdActivationDelay); } protected void BeginConfirm() @@ -74,7 +76,7 @@ namespace osu.Game.Graphics.Containers confirming = true; - this.TransformBindableTo(Progress, 1, holdActivationDelay.Value * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm()); + this.TransformBindableTo(progress, 1, holdActivationDelay.Value * (1 - progress.Value), Easing.Out).OnComplete(_ => Confirm()); } protected virtual void Confirm() @@ -91,9 +93,9 @@ namespace osu.Game.Graphics.Containers Fired = false; this - .TransformBindableTo(Progress, Progress.Value) + .TransformBindableTo(progress, progress.Value) .Delay(200) - .TransformBindableTo(Progress, 0, fadeout_delay, Easing.InSine); + .TransformBindableTo(progress, 0, fadeout_delay, Easing.InSine); } } } diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 9d280a1737..678c4256b4 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -164,7 +164,7 @@ namespace osu.Game.Screens.Play.HUD private void bind() { - circularProgress.Current.BindTo(Progress); + ((IBindable)circularProgress.Current).BindTo(Progress); Progress.ValueChanged += progress => icon.Scale = new Vector2(1 + (float)progress.NewValue * 0.2f); } From c38e97c9ecb390496c576ab815911a87d17c1a04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 14:43:24 +0900 Subject: [PATCH 0827/1276] Add full xmldoc to `HoldToConfirmContainer` --- .../Containers/HoldToConfirmContainer.cs | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index 540f9b97a7..28afd082c3 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -10,6 +10,16 @@ using osu.Game.Configuration; namespace osu.Game.Graphics.Containers { + /// + /// A container which adds a common "hold-to-perform" pattern to a container. + /// + /// + /// This container does not handle triggering the hold/abort operations. + /// To use this class, please call and when necessary. + /// + /// The is exposed as a transforming bindable which smoothly tracks the progress of a hold operation. + /// It can be used for animating and displaying progress directly. + /// public abstract class HoldToConfirmContainer : Container { public const double DANGEROUS_HOLD_ACTIVATION_DELAY = 500; @@ -70,6 +80,12 @@ namespace osu.Game.Graphics.Containers config.BindWith(OsuSetting.UIHoldActivationDelay, holdActivationDelay); } + /// + /// Begin a new confirmation. Should be called when the container is interacted with (ie. the user presses a key). + /// + /// + /// Calling this method when already in the process of confirming has no effect. + /// protected void BeginConfirm() { if (confirming || (!AllowMultipleFires && Fired)) return; @@ -79,12 +95,9 @@ namespace osu.Game.Graphics.Containers this.TransformBindableTo(progress, 1, holdActivationDelay.Value * (1 - progress.Value), Easing.Out).OnComplete(_ => Confirm()); } - protected virtual void Confirm() - { - Action?.Invoke(); - Fired = true; - } - + /// + /// Abort any ongoing confirmation. Should be called when the container's interaction is no longer valid (ie. the user releases a key). + /// protected void AbortConfirm() { if (!AllowMultipleFires && Fired) return; @@ -97,5 +110,15 @@ namespace osu.Game.Graphics.Containers .Delay(200) .TransformBindableTo(progress, 0, fadeout_delay, Easing.InSine); } + + /// + /// A method which is invoked when the confirmation sequence completes successfully. + /// By default, will fire the associated . + /// + protected virtual void Confirm() + { + Action?.Invoke(); + Fired = true; + } } } From 1c4aa125859d2de65858661b38bc207706a65625 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 14:44:10 +0900 Subject: [PATCH 0828/1276] Rename non-descript `Button` nested class --- osu.Game/Screens/Play/HUD/HoldForMenuButton.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 678c4256b4..e3283062ef 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Play.HUD public readonly Bindable IsPaused = new Bindable(); - private Button button; + private HoldButton button; public Action Action { get; set; } @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Play.HUD Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft }, - button = new Button(player?.Configuration.AllowRestart == false) + button = new HoldButton(player?.Configuration.AllowRestart == false) { HoverGained = () => text.FadeIn(500, Easing.OutQuint), HoverLost = () => text.FadeOut(500, Easing.OutQuint), @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Play.HUD } } - private class Button : HoldToConfirmContainer, IKeyBindingHandler + private class HoldButton : HoldToConfirmContainer, IKeyBindingHandler { private SpriteIcon icon; private CircularProgress circularProgress; @@ -113,7 +113,7 @@ namespace osu.Game.Screens.Play.HUD public Action HoverGained; public Action HoverLost; - public Button(bool isDangerousAction) + public HoldButton(bool isDangerousAction) : base(isDangerousAction) { } From 78959a6e05a4cf753dfc59d480b2fdf041296c33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 15:01:24 +0900 Subject: [PATCH 0829/1276] Add animation to denote a dangerous player exit --- .../Screens/Play/HUD/HoldForMenuButton.cs | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index e3283062ef..f892ea1049 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -12,12 +12,14 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { @@ -113,6 +115,11 @@ namespace osu.Game.Screens.Play.HUD public Action HoverGained; public Action HoverLost; + private const double shake_duration = 20; + + private bool pendingAnimation; + private ScheduledDelegate shakeOperation; + public HoldButton(bool isDangerousAction) : base(isDangerousAction) { @@ -165,10 +172,38 @@ namespace osu.Game.Screens.Play.HUD private void bind() { ((IBindable)circularProgress.Current).BindTo(Progress); - Progress.ValueChanged += progress => icon.Scale = new Vector2(1 + (float)progress.NewValue * 0.2f); + Progress.ValueChanged += progress => + { + icon.Scale = new Vector2(1 + (float)progress.NewValue * 0.2f); + + if (IsDangerousAction) + { + //Child.Scale = new Vector2(1 + (float)progress.NewValue); + Colour = Interpolation.ValueAt(progress.NewValue, Color4.White, Color4.Red, 0, 1, Easing.OutQuint); + + if (progress.NewValue > 0 && progress.NewValue < 1) + { + shakeOperation ??= Scheduler.AddDelayed(shake, shake_duration, true); + } + else + { + Child.MoveTo(Vector2.Zero, shake_duration * 2, Easing.OutQuint); + shakeOperation?.Cancel(); + shakeOperation = null; + } + } + }; } - private bool pendingAnimation; + private void shake() + { + const float shake_magnitude = 8; + + Child.MoveTo(new Vector2( + RNG.NextSingle(-1, 1) * (float)Progress.Value * shake_magnitude, + RNG.NextSingle(-1, 1) * (float)Progress.Value * shake_magnitude + ), shake_duration); + } protected override void Confirm() { From 060461a431a3fe99a46786b652cba234933fbc85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 15:22:22 +0900 Subject: [PATCH 0830/1276] Add test coverage of multiplayer gameplay exit flow --- .../Multiplayer/TestSceneMultiplayer.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 6a69917fb4..8e45d99eae 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Containers; @@ -17,6 +18,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; @@ -56,6 +58,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient; private TestMultiplayerRoomManager roomManager => multiplayerComponents.RoomManager; + [Resolved] + private OsuConfigManager config { get; set; } + [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -668,6 +673,43 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for results", () => multiplayerComponents.CurrentScreen is ResultsScreen); } + [Test] + public void TestGameplayExitFlow() + { + Bindable holdDelay = null; + + AddStep("Set hold delay to zero", () => + { + holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); + holdDelay.Value = 0; + }); + + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, + } + } + }); + + enterGameplay(); + + AddUntilStep("wait for playing", () => this.ChildrenOfType().FirstOrDefault()?.LocalUserPlaying.Value == true); + + AddStep("attempt exit without hold", () => InputManager.Key(Key.Escape)); + AddAssert("still in gameplay", () => multiplayerComponents.CurrentScreen is Player); + + AddStep("attempt exit with hold", () => InputManager.PressKey(Key.Escape)); + AddUntilStep("wait for lounge", () => multiplayerComponents.CurrentScreen is Screens.OnlinePlay.Multiplayer.Multiplayer); + + AddStep("stop holding", () => InputManager.ReleaseKey(Key.Escape)); + AddStep("set hold delay to default", () => holdDelay.SetDefault()); + } + [Test] public void TestGameplayDoesntStartWithNonLoadedUser() { From 32b40bdabfaa99f6c01ecf22a07c222b064103cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 15:45:36 +0900 Subject: [PATCH 0831/1276] Rename `DistanceBetweenTick` to be plural --- .../Visual/Editing/TestSceneDistanceSnapGrid.cs | 14 +++++++------- .../Compose/Components/CircularDistanceSnapGrid.cs | 10 +++++----- .../Edit/Compose/Components/DistanceSnapGrid.cs | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 7d8089b435..9148965987 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Editing public void TestDistanceSpacing(double multiplier) { AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier); - AddAssert("distance spacing matches multiplier", () => grid.DistanceBetweenTick == beat_snap_distance * multiplier); + AddAssert("distance spacing matches multiplier", () => grid.DistanceBetweenTicks == beat_snap_distance * multiplier); } [TestCase(1.0)] @@ -103,12 +103,12 @@ namespace osu.Game.Tests.Visual.Editing }); AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier); - AddAssert("check correct interval count", () => grid.MaxIntervals == (end_time / grid.DistanceBetweenTick)); + AddAssert("check correct interval count", () => grid.MaxIntervals == (end_time / grid.DistanceBetweenTicks)); } private class TestDistanceSnapGrid : DistanceSnapGrid { - public new float DistanceBetweenTick => base.DistanceBetweenTick; + public new float DistanceBetweenTicks => base.DistanceBetweenTicks; public new int MaxIntervals => base.MaxIntervals; @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Visual.Editing int indexFromPlacement = 0; - for (float s = StartPosition.X + DistanceBetweenTick; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceBetweenTick, indexFromPlacement++) + for (float s = StartPosition.X + DistanceBetweenTicks; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceBetweenTicks, indexFromPlacement++) { AddInternal(new Circle { @@ -141,7 +141,7 @@ namespace osu.Game.Tests.Visual.Editing indexFromPlacement = 0; - for (float s = StartPosition.X - DistanceBetweenTick; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceBetweenTick, indexFromPlacement++) + for (float s = StartPosition.X - DistanceBetweenTicks; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceBetweenTicks, indexFromPlacement++) { AddInternal(new Circle { @@ -154,7 +154,7 @@ namespace osu.Game.Tests.Visual.Editing indexFromPlacement = 0; - for (float s = StartPosition.Y + DistanceBetweenTick; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceBetweenTick, indexFromPlacement++) + for (float s = StartPosition.Y + DistanceBetweenTicks; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceBetweenTicks, indexFromPlacement++) { AddInternal(new Circle { @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.Editing indexFromPlacement = 0; - for (float s = StartPosition.Y - DistanceBetweenTick; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceBetweenTick, indexFromPlacement++) + for (float s = StartPosition.Y - DistanceBetweenTicks; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceBetweenTicks, indexFromPlacement++) { AddInternal(new Circle { diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 2873d74f75..ee8ca80c01 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -30,14 +30,14 @@ namespace osu.Game.Screens.Edit.Compose.Components Position = StartPosition, Width = crosshair_thickness, EdgeSmoothness = new Vector2(1), - Height = Math.Min(crosshair_max_size, DistanceBetweenTick * 2), + Height = Math.Min(crosshair_max_size, DistanceBetweenTicks * 2), }, new Box { Origin = Anchor.Centre, Position = StartPosition, EdgeSmoothness = new Vector2(1), - Width = Math.Min(crosshair_max_size, DistanceBetweenTick * 2), + Width = Math.Min(crosshair_max_size, DistanceBetweenTicks * 2), Height = crosshair_thickness, } }); @@ -45,11 +45,11 @@ namespace osu.Game.Screens.Edit.Compose.Components float dx = Math.Max(StartPosition.X, DrawWidth - StartPosition.X); float dy = Math.Max(StartPosition.Y, DrawHeight - StartPosition.Y); float maxDistance = new Vector2(dx, dy).Length; - int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceBetweenTick)); + int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceBetweenTicks)); for (int i = 0; i < requiredCircles; i++) { - float radius = (i + 1) * DistanceBetweenTick * 2; + float radius = (i + 1) * DistanceBetweenTicks * 2; AddInternal(new CircularProgress { @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose.Components float travelLength = travelVector.Length; // FindSnappedDistance will always round down, but we want to potentially round upwards. - travelLength += DistanceBetweenTick / 2; + travelLength += DistanceBetweenTicks / 2; // When interacting with the resolved snap provider, the distance spacing multiplier should first be removed // to allow for snapping at a non-multiplied ratio. diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index b9e0cfef19..742fbc99ca 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// The spacing between each tick of the beat snapping grid. /// - protected float DistanceBetweenTick { get; private set; } + protected float DistanceBetweenTicks { get; private set; } protected IBindable DistanceSpacingMultiplier { get; private set; } @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// The position which the grid should start. - /// The first beat snapping tick is located at + away from this point. + /// The first beat snapping tick is located at + away from this point. /// protected readonly Vector2 StartPosition; @@ -93,7 +93,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updateSpacing() { - DistanceBetweenTick = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * DistanceSpacingMultiplier.Value); + DistanceBetweenTicks = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * DistanceSpacingMultiplier.Value); if (LatestEndTime == null) MaxIntervals = int.MaxValue; @@ -101,7 +101,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { // +1 is added since a snapped hitobject may have its start time slightly less than the snapped time due to floating point errors double maxDuration = LatestEndTime.Value - StartTime + 1; - MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(ReferenceObject, DistanceBetweenTick)); + MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(ReferenceObject, DistanceBetweenTicks)); } gridCache.Invalidate(); From 246479bf34c725d886742017099f9d509e58b2f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 15:54:37 +0900 Subject: [PATCH 0832/1276] Fix snap extent not working correctly on sliders (and providing incorrect time values) --- .../Edit/Compose/Components/CircularDistanceSnapGrid.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index ee8ca80c01..ffa4176a48 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -89,7 +89,8 @@ namespace osu.Game.Screens.Edit.Compose.Components if (snappedTime > LatestEndTime) { - snappedDistance = SnapProvider.DurationToDistance(ReferenceObject, LatestEndTime.Value - ReferenceObject.StartTime); + snappedDistance = SnapProvider.DurationToDistance(ReferenceObject, LatestEndTime.Value - ReferenceObject.GetEndTime()); + snappedTime = StartTime + SnapProvider.DistanceToDuration(ReferenceObject, snappedDistance); } // The multiplier can then be reapplied to the final position. From f39fcee41bae044337bca75e5619f88772b50a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 10:04:00 +0200 Subject: [PATCH 0833/1276] Remove commented-out code --- osu.Game/Screens/Play/HUD/HoldForMenuButton.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index f892ea1049..8ed7260aac 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -178,7 +178,6 @@ namespace osu.Game.Screens.Play.HUD if (IsDangerousAction) { - //Child.Scale = new Vector2(1 + (float)progress.NewValue); Colour = Interpolation.ValueAt(progress.NewValue, Color4.White, Color4.Red, 0, 1, Easing.OutQuint); if (progress.NewValue > 0 && progress.NewValue < 1) From f5026bbbeb579dfe72325fbbab92647ceafcafd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 10:04:36 +0200 Subject: [PATCH 0834/1276] Bind to button's activation delay directly --- osu.Game/Screens/Play/HUD/HoldForMenuButton.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 8ed7260aac..2ba76d0896 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -35,8 +35,6 @@ namespace osu.Game.Screens.Play.HUD private OsuSpriteText text; - private readonly Bindable activationDelay = new Bindable(); - public HoldForMenuButton() { Direction = FillDirection.Horizontal; @@ -68,7 +66,7 @@ namespace osu.Game.Screens.Play.HUD protected override void LoadComplete() { - activationDelay.BindValueChanged(v => + button.HoldActivationDelay.BindValueChanged(v => { text.Text = v.NewValue > 0 ? "hold for menu" From c7e7aa5962c12cda3e74b161e0f4b30130b5a026 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 17:05:52 +0900 Subject: [PATCH 0835/1276] Don't draw distance snap grid on the start time of the next object --- osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs index 8a561f962a..b11929c1e8 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Edit public class OsuDistanceSnapGrid : CircularDistanceSnapGrid { public OsuDistanceSnapGrid(OsuHitObject hitObject, [CanBeNull] OsuHitObject nextHitObject = null) - : base(hitObject, hitObject.StackedEndPosition, hitObject.GetEndTime(), nextHitObject?.StartTime) + : base(hitObject, hitObject.StackedEndPosition, hitObject.GetEndTime(), nextHitObject?.StartTime - 1) { Masking = true; } From 684fef7f8cf813f616b96cb0c3abfe009ccf40d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 17:09:02 +0900 Subject: [PATCH 0836/1276] Fix `MaxIntervals` incorrectly factoring distance spacing multipiler into snap calculation --- .../Edit/Compose/Components/DistanceSnapGrid.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 742fbc99ca..2f39db06d4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -52,7 +52,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected IDistanceSnapProvider SnapProvider { get; private set; } [Resolved] - private EditorBeatmap beatmap { get; set; } + protected EditorBeatmap Beatmap { get; private set; } [Resolved] private BindableBeatDivisor beatDivisor { get; set; } @@ -93,16 +93,15 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updateSpacing() { - DistanceBetweenTicks = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * DistanceSpacingMultiplier.Value); + float distanceSpacingMultiplier = (float)DistanceSpacingMultiplier.Value; + float beatSnapDistance = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject); + + DistanceBetweenTicks = beatSnapDistance * distanceSpacingMultiplier; if (LatestEndTime == null) MaxIntervals = int.MaxValue; else - { - // +1 is added since a snapped hitobject may have its start time slightly less than the snapped time due to floating point errors - double maxDuration = LatestEndTime.Value - StartTime + 1; - MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(ReferenceObject, DistanceBetweenTicks)); - } + MaxIntervals = (int)((LatestEndTime.Value - StartTime) / SnapProvider.DistanceToDuration(ReferenceObject, beatSnapDistance)); gridCache.Invalidate(); } @@ -138,7 +137,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The applicable colour. protected ColourInfo GetColourForIndexFromPlacement(int placementIndex) { - var timingPoint = beatmap.ControlPointInfo.TimingPointAt(StartTime); + var timingPoint = Beatmap.ControlPointInfo.TimingPointAt(StartTime); double beatLength = timingPoint.BeatLength / beatDivisor.Value; int beatIndex = (int)Math.Round((StartTime - timingPoint.Time) / beatLength); From 37cbc792834b9128ae2dfdcb8deba7abe6129766 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 17:09:38 +0900 Subject: [PATCH 0837/1276] Fix clamping logic to always clamp to the last displayed tick --- .../Compose/Components/CircularDistanceSnapGrid.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index ffa4176a48..13d838234e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // This grid implementation factors in the user's distance spacing specification, // which is usually not considered by an `IDistanceSnapProvider`. - float distanceSpacing = (float)DistanceSpacingMultiplier.Value; + float distanceSpacingMultiplier = (float)DistanceSpacingMultiplier.Value; Vector2 travelVector = (position - StartPosition); @@ -84,17 +84,19 @@ namespace osu.Game.Screens.Edit.Compose.Components // When interacting with the resolved snap provider, the distance spacing multiplier should first be removed // to allow for snapping at a non-multiplied ratio. - float snappedDistance = SnapProvider.FindSnappedDistance(ReferenceObject, travelLength / distanceSpacing); + float snappedDistance = SnapProvider.FindSnappedDistance(ReferenceObject, travelLength / distanceSpacingMultiplier); double snappedTime = StartTime + SnapProvider.DistanceToDuration(ReferenceObject, snappedDistance); if (snappedTime > LatestEndTime) { - snappedDistance = SnapProvider.DurationToDistance(ReferenceObject, LatestEndTime.Value - ReferenceObject.GetEndTime()); + double tickLength = Beatmap.GetBeatLengthAtTime(StartTime); + + snappedDistance = SnapProvider.DurationToDistance(ReferenceObject, MaxIntervals * tickLength); snappedTime = StartTime + SnapProvider.DistanceToDuration(ReferenceObject, snappedDistance); } // The multiplier can then be reapplied to the final position. - Vector2 snappedPosition = StartPosition + travelVector.Normalized() * snappedDistance * distanceSpacing; + Vector2 snappedPosition = StartPosition + travelVector.Normalized() * snappedDistance * distanceSpacingMultiplier; return (snappedPosition, snappedTime); } From 319867f73c968d9238ac319c05ed03fe38fa3076 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 17:32:55 +0900 Subject: [PATCH 0838/1276] Mark `GuestUser` as system user via `Id` for now Should resolve https://github.com/ppy/osu/issues/18105. Checking through usages, it doesn't immediately look like this will regress any other scenarios. --- osu.Game/Online/API/APIAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 8c9741b98b..62ddd49881 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -458,7 +458,7 @@ namespace osu.Game.Online.API public GuestUser() { Username = @"Guest"; - Id = 1; + Id = SYSTEM_USER_ID; } } From 118e58888b77d38a2b202c97ad7fe5ed11ff3ab9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 17:51:51 +0900 Subject: [PATCH 0839/1276] Rename incorrectly named variable (radius is not diameter) --- .../Edit/Compose/Components/CircularDistanceSnapGrid.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 13d838234e..91fad08aff 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -49,15 +49,15 @@ namespace osu.Game.Screens.Edit.Compose.Components for (int i = 0; i < requiredCircles; i++) { - float radius = (i + 1) * DistanceBetweenTicks * 2; + float diameter = (i + 1) * DistanceBetweenTicks * 2; AddInternal(new CircularProgress { Origin = Anchor.Centre, Position = StartPosition, Current = { Value = 1 }, - Size = new Vector2(radius), - InnerRadius = 4 * 1f / radius, + Size = new Vector2(diameter), + InnerRadius = 4 * 1f / diameter, Colour = GetColourForIndexFromPlacement(i) }); } From 69592722f80f0869a1ddbe42fbe76564c6252c5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 18:25:02 +0900 Subject: [PATCH 0840/1276] Highlight distance snap grid rings that are close to the current time value --- .../Components/CircularDistanceSnapGrid.cs | 45 +++++++++++++++++-- .../Compose/Components/DistanceSnapGrid.cs | 7 +-- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 91fad08aff..5682b5563e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -2,11 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components { @@ -51,14 +55,12 @@ namespace osu.Game.Screens.Edit.Compose.Components { float diameter = (i + 1) * DistanceBetweenTicks * 2; - AddInternal(new CircularProgress + AddInternal(new Ring(ReferenceObject, GetColourForIndexFromPlacement(i)) { - Origin = Anchor.Centre, Position = StartPosition, - Current = { Value = 1 }, + Origin = Anchor.Centre, Size = new Vector2(diameter), InnerRadius = 4 * 1f / diameter, - Colour = GetColourForIndexFromPlacement(i) }); } } @@ -100,5 +102,40 @@ namespace osu.Game.Screens.Edit.Compose.Components return (snappedPosition, snappedTime); } + + public class Ring : CircularProgress + { + [Resolved] + private IDistanceSnapProvider snapProvider { get; set; } + + [Resolved(canBeNull: true)] + private EditorClock editorClock { get; set; } + + private readonly HitObject referenceObject; + + private readonly Color4 baseColour; + + public Ring(HitObject referenceObject, Color4 baseColour) + { + this.referenceObject = referenceObject; + + Colour = this.baseColour = baseColour; + + Current.Value = 1; + } + + protected override void Update() + { + base.Update(); + + if (editorClock == null) + return; + + float distanceForCurrentTime = snapProvider.DurationToDistance(referenceObject, editorClock.CurrentTime - referenceObject.StartTime); + float timeBasedAlpha = Math.Clamp(1 - Math.Abs(distanceForCurrentTime - Size.X / 2) / 30, 0, 1); + + Colour = baseColour.Opacity(Math.Max(baseColour.A, timeBasedAlpha)); + } + } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 2f39db06d4..1f64a50c02 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -4,14 +4,15 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Layout; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components { @@ -135,7 +136,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// The 0-based beat index from the point of placement. /// The applicable colour. - protected ColourInfo GetColourForIndexFromPlacement(int placementIndex) + protected Color4 GetColourForIndexFromPlacement(int placementIndex) { var timingPoint = Beatmap.ControlPointInfo.TimingPointAt(StartTime); double beatLength = timingPoint.BeatLength / beatDivisor.Value; @@ -144,7 +145,7 @@ namespace osu.Game.Screens.Edit.Compose.Components var colour = BindableBeatDivisor.GetColourFor(BindableBeatDivisor.GetDivisorForBeatIndex(beatIndex + placementIndex + 1, beatDivisor.Value), Colours); int repeatIndex = placementIndex / beatDivisor.Value; - return ColourInfo.SingleColour(colour).MultiplyAlpha(0.5f / (repeatIndex + 1)); + return colour.Opacity(0.5f / (repeatIndex + 1)); } } } From 8f217d1e975db8ffdf3099e982e9edabb6bc8fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 11:37:50 +0200 Subject: [PATCH 0841/1276] Add failing test case for broken hover behaviour --- .../UserInterface/TestSceneModSelectScreen.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index a3ce4b734b..c92a738b56 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; +using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface @@ -164,7 +165,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("select mod requiring configuration", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); assertCustomisationToggleState(disabled: false, active: true); - AddStep("dismiss mod customisation via mouse", () => + AddStep("dismiss mod customisation via toggle", () => { InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); @@ -191,6 +192,29 @@ namespace osu.Game.Tests.Visual.UserInterface assertCustomisationToggleState(disabled: true, active: false); // config was dismissed without explicit user action. } + [Test] + public void TestDismissCustomisationViaDimmedArea() + { + createScreen(); + assertCustomisationToggleState(disabled: true, active: false); + + AddStep("select mod requiring configuration", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); + assertCustomisationToggleState(disabled: false, active: true); + + AddStep("move mouse to settings area", () => InputManager.MoveMouseTo(this.ChildrenOfType().Single())); + AddStep("move mouse to dimmed area", () => + { + InputManager.MoveMouseTo(new Vector2( + modSelectScreen.ScreenSpaceDrawQuad.TopLeft.X, + (modSelectScreen.ScreenSpaceDrawQuad.TopLeft.Y + modSelectScreen.ScreenSpaceDrawQuad.BottomLeft.Y) / 2)); + }); + AddStep("click", () => InputManager.Click(MouseButton.Left)); + assertCustomisationToggleState(disabled: false, active: false); + + AddStep("move mouse to first mod panel", () => InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType().First())); + AddAssert("first mod panel is hovered", () => modSelectScreen.ChildrenOfType().First().IsHovered); + } + /// /// Ensure that two mod overlays are not cross polluting via central settings instances. /// From daed42513e6476abe183fd6173ce328765970903 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 18:44:25 +0900 Subject: [PATCH 0842/1276] Fix outdated test asserts --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 2 +- osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 6d0ed15e45..f6b8f4edc3 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor }); AddStep("move mouse outside grid", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 3f))); - assertSnappedDistance(beat_length * 2); + assertSnappedDistance(beat_length); } private void assertSnappedDistance(float expectedDistance) => AddAssert($"snap distance = {expectedDistance}", () => diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 9148965987..3aa3481cbf 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -103,7 +103,7 @@ namespace osu.Game.Tests.Visual.Editing }); AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier); - AddAssert("check correct interval count", () => grid.MaxIntervals == (end_time / grid.DistanceBetweenTicks)); + AddStep("check correct interval count", () => Assert.That((end_time / grid.DistanceBetweenTicks) * multiplier, Is.EqualTo(grid.MaxIntervals))); } private class TestDistanceSnapGrid : DistanceSnapGrid From c6bc6be1280dfff8db52757ab51ab8aa9111b28b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 19:06:40 +0900 Subject: [PATCH 0843/1276] Fix toolbox expand being interrupted by gaps between groups --- osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs b/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs index e807dbd482..c6cc09a16c 100644 --- a/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs +++ b/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; @@ -25,7 +24,7 @@ namespace osu.Game.Rulesets.Edit public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) && anyToolboxHovered(screenSpacePos); - private bool anyToolboxHovered(Vector2 screenSpacePos) => FillFlow.Children.Any(d => d.ScreenSpaceDrawQuad.Contains(screenSpacePos)); + private bool anyToolboxHovered(Vector2 screenSpacePos) => FillFlow.ScreenSpaceDrawQuad.Contains(screenSpacePos); protected override bool OnMouseDown(MouseDownEvent e) => true; From 08fd0ea086d4e6b7ca0d9d07179493120ecdc47e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 12:26:09 +0200 Subject: [PATCH 0844/1276] Fix click-to-return container still handling hover when inactive --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index a7f8a167f9..a1b4d7d8a0 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -464,6 +464,8 @@ namespace osu.Game.Overlays.Mods public Action? OnClicked { get; set; } + public override bool HandlePositionalInput => base.HandlePositionalInput && HandleMouse.Value; + protected override bool Handle(UIEvent e) { if (!HandleMouse.Value) From 949e30c4b4d85ed4b50e04494fe20fd3f6143ab7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 19:36:41 +0900 Subject: [PATCH 0845/1276] Remove auto-expansion of individual toolbox groups when parent expanding container expands --- osu.Game/Overlays/SettingsToolboxGroup.cs | 54 ++++++++--------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index b9e5283a44..ca4864293c 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -135,60 +135,31 @@ namespace osu.Game.Overlays headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint); } - [Resolved(canBeNull: true)] - private IExpandingContainer expandingContainer { get; set; } - - private bool expandedByContainer; - protected override void LoadComplete() { base.LoadComplete(); - expandingContainer?.Expanded.BindValueChanged(containerExpanded => - { - if (containerExpanded.NewValue && !Expanded.Value) - { - Expanded.Value = true; - expandedByContainer = true; - } - else if (!containerExpanded.NewValue && expandedByContainer) - { - Expanded.Value = false; - expandedByContainer = false; - } - - updateActiveState(); - }, true); - Expanded.BindValueChanged(v => { // clearing transforms can break autosizing, see: https://github.com/ppy/osu-framework/issues/5064 if (v.NewValue != v.OldValue) content.ClearTransforms(); - if (v.NewValue) - content.AutoSizeAxes = Axes.Y; - else - { - content.AutoSizeAxes = Axes.None; - content.ResizeHeightTo(0, transition_duration, Easing.OutQuint); - } - - button.FadeColour(Expanded.Value ? expandedColour : Color4.White, 200, Easing.InOutQuint); + Scheduler.AddOnce(updateExpandedState); }, true); - this.Delay(600).Schedule(updateActiveState); + this.Delay(600).Schedule(updateFadeState); } protected override bool OnHover(HoverEvent e) { - updateActiveState(); + updateFadeState(); return false; } protected override void OnHoverLost(HoverLostEvent e) { - updateActiveState(); + updateFadeState(); base.OnHoverLost(e); } @@ -198,9 +169,22 @@ namespace osu.Game.Overlays expandedColour = colours.Yellow; } - private void updateActiveState() + private void updateExpandedState() { - this.FadeTo(IsHovered || expandingContainer?.Expanded.Value == true ? 1 : inactive_alpha, fade_duration, Easing.OutQuint); + if (Expanded.Value) + content.AutoSizeAxes = Axes.Y; + else + { + content.AutoSizeAxes = Axes.None; + content.ResizeHeightTo(0, transition_duration, Easing.OutQuint); + } + + button.FadeColour(Expanded.Value ? expandedColour : Color4.White, 200, Easing.InOutQuint); + } + + private void updateFadeState() + { + this.FadeTo(IsHovered ? 1 : inactive_alpha, fade_duration, Easing.OutQuint); } protected override Container Content => content; From 59add66632fcd15cad5b81f0baf99653bdabb483 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 19:39:00 +0900 Subject: [PATCH 0846/1276] Remove unnecessary autosize workaround (was fixed long ago) --- osu.Game/Overlays/SettingsToolboxGroup.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index ca4864293c..10a09f3965 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -141,10 +141,6 @@ namespace osu.Game.Overlays Expanded.BindValueChanged(v => { - // clearing transforms can break autosizing, see: https://github.com/ppy/osu-framework/issues/5064 - if (v.NewValue != v.OldValue) - content.ClearTransforms(); - Scheduler.AddOnce(updateExpandedState); }, true); From b432885e5f4b20cee67ab44b3321df1ea954ecca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 19:39:09 +0900 Subject: [PATCH 0847/1276] Tidy up ordering of `SettingsToolboxGroup` --- osu.Game/Overlays/SettingsToolboxGroup.cs | 47 +++++++++++------------ 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 10a09f3965..eb8c0248e7 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -117,32 +117,17 @@ namespace osu.Game.Overlays }; } - protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) + [BackgroundDependencyLoader] + private void load(OsuColour colours) { - if (invalidation.HasFlagFast(Invalidation.DrawSize)) - headerTextVisibilityCache.Invalidate(); - - return base.OnInvalidate(invalidation, source); - } - - protected override void Update() - { - base.Update(); - - if (!headerTextVisibilityCache.IsValid) - // These toolbox grouped may be contracted to only show icons. - // For now, let's hide the header to avoid text truncation weirdness in such cases. - headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint); + expandedColour = colours.Yellow; } protected override void LoadComplete() { base.LoadComplete(); - Expanded.BindValueChanged(v => - { - Scheduler.AddOnce(updateExpandedState); - }, true); + Expanded.BindValueChanged(updateExpandedState, true); this.Delay(600).Schedule(updateFadeState); } @@ -159,15 +144,27 @@ namespace osu.Game.Overlays base.OnHoverLost(e); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + protected override void Update() { - expandedColour = colours.Yellow; + base.Update(); + + if (!headerTextVisibilityCache.IsValid) + // These toolbox grouped may be contracted to only show icons. + // For now, let's hide the header to avoid text truncation weirdness in such cases. + headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint); } - private void updateExpandedState() + protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) { - if (Expanded.Value) + if (invalidation.HasFlagFast(Invalidation.DrawSize)) + headerTextVisibilityCache.Invalidate(); + + return base.OnInvalidate(invalidation, source); + } + + private void updateExpandedState(ValueChangedEvent expanded) + { + if (expanded.NewValue) content.AutoSizeAxes = Axes.Y; else { @@ -175,7 +172,7 @@ namespace osu.Game.Overlays content.ResizeHeightTo(0, transition_duration, Easing.OutQuint); } - button.FadeColour(Expanded.Value ? expandedColour : Color4.White, 200, Easing.InOutQuint); + button.FadeColour(expanded.NewValue ? expandedColour : Color4.White, 200, Easing.InOutQuint); } private void updateFadeState() From 88c190f3e359f9cb3bf9449dba97d19af0de7329 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 19:45:14 +0900 Subject: [PATCH 0848/1276] Change colour denoting expanded state to be gray rather than yellow I always found the yellow colour very non-descript in this case. Gray seems to work better? --- osu.Game/Overlays/SettingsToolboxGroup.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index eb8c0248e7..fd81d092c9 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -39,10 +39,10 @@ namespace osu.Game.Overlays public BindableBool Expanded { get; } = new BindableBool(true); - private Color4 expandedColour; - private readonly OsuSpriteText headerText; + private readonly Container headerContent; + /// /// Create a new instance. /// @@ -71,7 +71,7 @@ namespace osu.Game.Overlays AutoSizeAxes = Axes.Y, Children = new Drawable[] { - new Container + headerContent = new Container { Name = @"Header", Origin = Anchor.TopCentre, @@ -117,12 +117,6 @@ namespace osu.Game.Overlays }; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - expandedColour = colours.Yellow; - } - protected override void LoadComplete() { base.LoadComplete(); @@ -172,7 +166,7 @@ namespace osu.Game.Overlays content.ResizeHeightTo(0, transition_duration, Easing.OutQuint); } - button.FadeColour(expanded.NewValue ? expandedColour : Color4.White, 200, Easing.InOutQuint); + headerContent.FadeColour(expanded.NewValue ? Color4.White : OsuColour.Gray(0.5f), 200, Easing.OutQuint); } private void updateFadeState() From a915b7333c8935ce0c643431824806059d494cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 13:08:54 +0200 Subject: [PATCH 0849/1276] Remove unused using directive --- osu.Game/Overlays/SettingsToolboxGroup.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index fd81d092c9..f9cfd8ff2a 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; using osu.Framework.Extensions.EnumExtensions; From e9d52aa9547f0df158310fac9bf7a632232d34fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 13:09:37 +0200 Subject: [PATCH 0850/1276] Remove not-accessed field --- osu.Game/Overlays/SettingsToolboxGroup.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index f9cfd8ff2a..808d4fc422 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -34,7 +34,6 @@ namespace osu.Game.Overlays private readonly Cached headerTextVisibilityCache = new Cached(); private readonly FillFlowContainer content; - private readonly IconButton button; public BindableBool Expanded { get; } = new BindableBool(true); @@ -87,7 +86,7 @@ namespace osu.Game.Overlays Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17), Padding = new MarginPadding { Left = 10, Right = 30 }, }, - button = new IconButton + new IconButton { Origin = Anchor.Centre, Anchor = Anchor.CentreRight, From fad1f727bb26954f75a8eeea25ff333107f3325a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 May 2022 20:34:44 +0900 Subject: [PATCH 0851/1276] Fix editor drag box visuals --- .../Edit/Compose/Components/DragBox.cs | 85 ++++++++++++++++--- 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs index eaee2cd1e2..a256adbe4a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs @@ -9,7 +9,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osuTK.Graphics; +using osu.Framework.Layout; +using osuTK; namespace osu.Game.Screens.Edit.Compose.Components { @@ -41,17 +42,7 @@ namespace osu.Game.Screens.Edit.Compose.Components InternalChild = Box = CreateBox(); } - protected virtual Drawable CreateBox() => new Container - { - Masking = true, - BorderColour = Color4.White, - BorderThickness = SelectionBox.BORDER_RADIUS, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.1f - } - }; + protected virtual Drawable CreateBox() => new BoxWithBorders(); private RectangleF? dragRectangle; @@ -111,5 +102,75 @@ namespace osu.Game.Screens.Edit.Compose.Components public override void Show() => State = Visibility.Visible; public event Action StateChanged; + + public class BoxWithBorders : CompositeDrawable + { + private readonly LayoutValue cache = new LayoutValue(Invalidation.RequiredParentSizeToFit); + + public BoxWithBorders() + { + AddLayout(cache); + } + + protected override void Update() + { + base.Update(); + + if (!cache.IsValid) + { + createContent(); + cache.Validate(); + } + } + + private void createContent() + { + if (DrawSize == Vector2.Zero) + { + ClearInternal(); + return; + } + + // Make lines the same width independent of display resolution. + float lineThickness = DrawWidth > 0 + ? DrawWidth / ScreenSpaceDrawQuad.Width * 2 + : DrawHeight / ScreenSpaceDrawQuad.Height * 2; + + Padding = new MarginPadding(-lineThickness); + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.X, + Height = lineThickness, + }, + new Box + { + RelativeSizeAxes = Axes.X, + Height = lineThickness, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + }, + new Box + { + RelativeSizeAxes = Axes.Y, + Width = lineThickness, + }, + new Box + { + RelativeSizeAxes = Axes.Y, + Width = lineThickness, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.1f + } + }; + } + } } } From 19297375e270839c443bfd3165b79c641f692de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 13:44:04 +0200 Subject: [PATCH 0852/1276] Update tests to reflect new expected behaviour --- .../Visual/UserInterface/TestSceneExpandingContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs index 288c0cb140..2bb6e58448 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs @@ -99,15 +99,15 @@ namespace osu.Game.Tests.Visual.UserInterface } /// - /// Tests expanding a container will expand underlying groups if contracted. + /// Tests expanding a container will not expand underlying groups if they were manually contracted by the user. /// [Test] - public void TestExpandingContainerExpandsContractedGroup() + public void TestExpandingContainerDoesNotExpandContractedGroup() { AddStep("contract group", () => toolboxGroup.Expanded.Value = false); AddStep("expand container", () => container.Expanded.Value = true); - AddAssert("group expanded", () => toolboxGroup.Expanded.Value); + AddAssert("group not expanded", () => !toolboxGroup.Expanded.Value); AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value); AddStep("contract container", () => container.Expanded.Value = false); From b119726b289334df0666c15709a99245b2c0a28d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 May 2022 15:36:52 +0300 Subject: [PATCH 0853/1276] Reword test step --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index f6b8f4edc3..3c3c5cb939 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [TestCase(0.5f)] public void TestDistanceSpacing(float multiplier) { - AddStep($"set beat divisor = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier); + AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier); } [Test] From 29a3ab7e7a018d895ddcc5f14d65d0f864812855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 15:21:25 +0200 Subject: [PATCH 0854/1276] Halve drag box padding Allows the drag box borders to collapse in on themselves to a single line if the drag selection has zero width or height. --- osu.Game/Screens/Edit/Compose/Components/DragBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs index a256adbe4a..ecbac82db0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs @@ -136,7 +136,7 @@ namespace osu.Game.Screens.Edit.Compose.Components ? DrawWidth / ScreenSpaceDrawQuad.Width * 2 : DrawHeight / ScreenSpaceDrawQuad.Height * 2; - Padding = new MarginPadding(-lineThickness); + Padding = new MarginPadding(-lineThickness / 2); InternalChildren = new Drawable[] { From c533c93ffd088e2a2240fdccc822608fd1460317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 15:29:54 +0200 Subject: [PATCH 0855/1276] Remove leftover border thickness spec in mod settings area Was never supposed to be there, it was a vestige of a previous design iteration that went by unnoticed. --- osu.Game/Overlays/Mods/ModSettingsArea.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSettingsArea.cs b/osu.Game/Overlays/Mods/ModSettingsArea.cs index 9c5f3b7f11..08f563f93e 100644 --- a/osu.Game/Overlays/Mods/ModSettingsArea.cs +++ b/osu.Game/Overlays/Mods/ModSettingsArea.cs @@ -44,7 +44,6 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.Both, Masking = true, - BorderThickness = 2, Children = new Drawable[] { background = new Box From 112496204d26f8709428b9eab026969953330910 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 May 2022 18:50:28 +0300 Subject: [PATCH 0856/1276] Make nested class private --- .../Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 5682b5563e..b3df72f827 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return (snappedPosition, snappedTime); } - public class Ring : CircularProgress + private class Ring : CircularProgress { [Resolved] private IDistanceSnapProvider snapProvider { get; set; } From d78f1d158d3f5582fc99efd20a92c48e5e73331d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 May 2022 18:51:34 +0300 Subject: [PATCH 0857/1276] Use `GetEndTime()` instead of `StartTime` Companion to https://github.com/ppy/osu/pull/18103/commits/246479bf34c725d886742017099f9d509e58b2f7 --- .../Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index b3df72f827..6cb05f0596 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -131,7 +131,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (editorClock == null) return; - float distanceForCurrentTime = snapProvider.DurationToDistance(referenceObject, editorClock.CurrentTime - referenceObject.StartTime); + float distanceForCurrentTime = snapProvider.DurationToDistance(referenceObject, editorClock.CurrentTime - referenceObject.GetEndTime()); float timeBasedAlpha = Math.Clamp(1 - Math.Abs(distanceForCurrentTime - Size.X / 2) / 30, 0, 1); Colour = baseColour.Opacity(Math.Max(baseColour.A, timeBasedAlpha)); From 723fce81741de8b858edb0a60090b850fc18f67a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 May 2022 18:54:08 +0300 Subject: [PATCH 0858/1276] Move inverting value to happen outside clamp Looks to have no effect, but makes sense to happen outside instead. --- .../Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 6cb05f0596..c0a3f7d97f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -132,7 +132,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return; float distanceForCurrentTime = snapProvider.DurationToDistance(referenceObject, editorClock.CurrentTime - referenceObject.GetEndTime()); - float timeBasedAlpha = Math.Clamp(1 - Math.Abs(distanceForCurrentTime - Size.X / 2) / 30, 0, 1); + float timeBasedAlpha = 1 - Math.Clamp(Math.Abs(distanceForCurrentTime - Size.X / 2) / 30, 0, 1); Colour = baseColour.Opacity(Math.Max(baseColour.A, timeBasedAlpha)); } From cc251ed5c3fe4a50126817d8893fba4a4a591536 Mon Sep 17 00:00:00 2001 From: Hugo Denizart Date: Fri, 6 May 2022 19:52:25 +0200 Subject: [PATCH 0859/1276] =?UTF-8?q?=F0=9F=94=A7=20Update=20Sentry=20DSN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- osu.Game/Utils/SentryLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index dbf04283b6..d9c8199f75 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -24,7 +24,7 @@ namespace osu.Game.Utils var options = new SentryOptions { - Dsn = "https://5e342cd55f294edebdc9ad604d28bbd3@sentry.io/1255255", + Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2", Release = game.Version }; From 049fed42e2084497b6766d7af736026fe77b96b4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 May 2022 21:42:20 +0300 Subject: [PATCH 0860/1276] Fix side overlay offsetting not affecting fullscreen overlays --- osu.Game/OsuGame.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e9fe8c43de..7f47c15f68 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1153,6 +1153,7 @@ namespace osu.Game horizontalOffset += (Content.ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - Content.DrawWidth) * SIDE_OVERLAY_OFFSET_RATIO; ScreenOffsetContainer.X = horizontalOffset; + overlayContent.X = horizontalOffset; MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; } From 8080f784fd5f9a3e202d7f27808b8a08ac67835b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 May 2022 22:20:04 +0300 Subject: [PATCH 0861/1276] Apply offset by a factor of 0.8x from screen offset --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7f47c15f68..b3de23ee08 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1153,7 +1153,7 @@ namespace osu.Game horizontalOffset += (Content.ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - Content.DrawWidth) * SIDE_OVERLAY_OFFSET_RATIO; ScreenOffsetContainer.X = horizontalOffset; - overlayContent.X = horizontalOffset; + overlayContent.X = horizontalOffset * 0.8f; MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; } From 380cd1e03619e45ad580356342f99498c0f59592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 21:35:22 +0200 Subject: [PATCH 0862/1276] Add test coverage for lack of customisation on free mod select --- .../TestSceneFreeModSelectScreen.cs | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs index b5f901e51d..8eaa45696f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -6,24 +6,19 @@ using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.OnlinePlay; namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneFreeModSelectScreen : MultiplayerTestScene { + private FreeModSelectScreen freeModSelectScreen; + [Test] public void TestFreeModSelect() { - FreeModSelectScreen freeModSelectScreen = null; - - AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen - { - State = { Value = Visibility.Visible } - }); - AddUntilStep("all column content loaded", - () => freeModSelectScreen.ChildrenOfType().Any() - && freeModSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); + createFreeModSelect(); AddUntilStep("all visible mods are playable", () => this.ChildrenOfType() @@ -36,5 +31,26 @@ namespace osu.Game.Tests.Visual.Multiplayer freeModSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden; }); } + + [Test] + public void TestCustomisationNotAvailable() + { + createFreeModSelect(); + + AddStep("select difficulty adjust", () => freeModSelectScreen.SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); + AddWaitStep("wait some", 3); + AddAssert("customisation area not expanded", () => this.ChildrenOfType().Single().Height == 0); + } + + private void createFreeModSelect() + { + AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen + { + State = { Value = Visibility.Visible } + }); + AddUntilStep("all column content loaded", + () => freeModSelectScreen.ChildrenOfType().Any() + && freeModSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); + } } } From c199b8fcb66fade4234b6e24597d42f4afccc442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 21:35:36 +0200 Subject: [PATCH 0863/1276] Simplify state management in `ModColumn` Bad sign when you can't follow your own code. All of the various state changing methods were flattened into one because it was too hard to follow what was calling what and why. --- osu.Game/Overlays/Mods/ModColumn.cs | 43 ++++++++++------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index a792c0a81e..270839112a 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Mods set { filter = value; - updateFilter(); + updateState(); } } @@ -292,9 +292,7 @@ namespace osu.Game.Overlays.Mods { panelFlow.ChildrenEnumerable = loaded; - updateActiveState(); - updateToggleAllState(); - updateFilter(); + updateState(); foreach (var panel in panelFlow) { @@ -308,10 +306,19 @@ namespace osu.Game.Overlays.Mods }); } - private void updateActiveState() + private void updateState() { foreach (var panel in panelFlow) + { panel.Active.Value = SelectedMods.Contains(panel.Mod); + panel.ApplyFilter(Filter); + } + + if (toggleAllCheckbox != null && !SelectionAnimationRunning) + { + toggleAllCheckbox.Alpha = panelFlow.Any(panel => !panel.Filtered.Value) ? 1 : 0; + toggleAllCheckbox.Current.Value = panelFlow.Where(panel => !panel.Filtered.Value).All(panel => panel.Active.Value); + } } /// @@ -323,13 +330,12 @@ namespace osu.Game.Overlays.Mods private void panelStateChanged(ModPanel panel) { - updateToggleAllState(); - var newSelectedMods = panel.Active.Value ? SelectedMods.Append(panel.Mod) : SelectedMods.Except(panel.Mod.Yield()); SelectedMods = newSelectedMods.ToArray(); + updateState(); if (!externalSelectionUpdateInProgress) SelectionChangedByUser?.Invoke(); } @@ -364,7 +370,7 @@ namespace osu.Game.Overlays.Mods } SelectedMods = newSelection; - updateActiveState(); + updateState(); externalSelectionUpdateInProgress = false; } @@ -403,15 +409,6 @@ namespace osu.Game.Overlays.Mods } } - private void updateToggleAllState() - { - if (toggleAllCheckbox != null && !SelectionAnimationRunning) - { - toggleAllCheckbox.Alpha = panelFlow.Any(panel => !panel.Filtered.Value) ? 1 : 0; - toggleAllCheckbox.Current.Value = panelFlow.Where(panel => !panel.Filtered.Value).All(panel => panel.Active.Value); - } - } - /// /// Selects all mods. /// @@ -507,18 +504,6 @@ namespace osu.Game.Overlays.Mods #endregion - #region Filtering support - - private void updateFilter() - { - foreach (var modPanel in panelFlow) - modPanel.ApplyFilter(Filter); - - updateToggleAllState(); - } - - #endregion - #region Keyboard selection support protected override bool OnKeyDown(KeyDownEvent e) From 621f7467898e290bdd2d9d9fe3cc4daadd209366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 21:46:56 +0200 Subject: [PATCH 0864/1276] Do not modify selected mods through panel state change during external update --- osu.Game/Overlays/Mods/ModColumn.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 270839112a..f6fa591666 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -330,14 +330,16 @@ namespace osu.Game.Overlays.Mods private void panelStateChanged(ModPanel panel) { + if (externalSelectionUpdateInProgress) + return; + var newSelectedMods = panel.Active.Value ? SelectedMods.Append(panel.Mod) : SelectedMods.Except(panel.Mod.Yield()); SelectedMods = newSelectedMods.ToArray(); updateState(); - if (!externalSelectionUpdateInProgress) - SelectionChangedByUser?.Invoke(); + SelectionChangedByUser?.Invoke(); } /// From 8c73ed72078a47eb7f166164cb80e642b5486218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 21:38:29 +0200 Subject: [PATCH 0865/1276] Fix sequence equality check not using reference comparison --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index a1b4d7d8a0..838c3597ae 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Layout; +using osu.Framework.Lists; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Graphics; @@ -264,7 +265,9 @@ namespace osu.Game.Overlays.Mods { var candidateSelection = columnFlow.Columns.SelectMany(column => column.SelectedMods).ToArray(); - if (candidateSelection.SequenceEqual(SelectedMods.Value)) + // 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 + if (candidateSelection.SequenceEqual(SelectedMods.Value, new FuncEqualityComparer(ReferenceEquals))) return; SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection); From 9d62206176732dbc9b6e96b1f0258ee87dffe489 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Fri, 6 May 2022 22:56:35 +0100 Subject: [PATCH 0866/1276] Update new chat overlay day separator colours and spacing Updates the `DaySeparator` component to have separately settable colours for the text and the lines. Updates existing overrides of the `DaySeparator` to use the new colour setter. Create new `ChatOverlayDrawableChannel` with adjusted spacing and new `DaySeparator` colours. --- .../Visual/Online/TestSceneChatOverlayV2.cs | 6 ++-- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 33 ++++++++----------- osu.Game/Overlays/Chat/ChatTextBar.cs | 2 +- osu.Game/Overlays/Chat/DrawableChannel.cs | 18 +++++++++- osu.Game/Overlays/ChatOverlayV2.cs | 33 +++++++++++++++++-- 5 files changed, 64 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 7c77ac925e..9d894314f8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -365,7 +365,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click listing", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); - AddStep("Click drawable channel", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); + AddStep("Click drawable channel", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click channel list", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); @@ -381,8 +381,8 @@ namespace osu.Game.Tests.Visual.Online private Visibility loadingVisibility => chatOverlay.ChildrenOfType().Single().State.Value; - private DrawableChannel currentDrawableChannel => - chatOverlay.ChildrenOfType>().Single().Child; + private ChatOverlayDrawableChannel currentDrawableChannel => + chatOverlay.ChildrenOfType>().Single().Child; private ChannelListItem getChannelListItem(Channel channel) => chatOverlay.ChildrenOfType().Single(item => item.Channel == channel); diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index df210fcaf8..c03cd938a5 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -155,9 +155,7 @@ namespace osu.Game.Online.Chat { public Func CreateChatLineAction; - protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m); - - protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new CustomDaySeparator(time); + private Colour4 daySepColour; public StandAloneDrawableChannel(Channel channel) : base(channel) @@ -165,28 +163,23 @@ namespace osu.Game.Online.Chat } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { ChatLineFlow.Padding = new MarginPadding { Horizontal = 0 }; + daySepColour = colours.Yellow; } - private class CustomDaySeparator : DaySeparator + protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m); + + protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) { - public CustomDaySeparator(DateTimeOffset time) - : base(time) - { - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Colour = colours.Yellow; - TextSize = 14; - LineHeight = 1; - Padding = new MarginPadding { Horizontal = 10 }; - Margin = new MarginPadding { Vertical = 5 }; - } - } + TextSize = 14, + TextColour = daySepColour, + LineHeight = 1, + LineColour = daySepColour, + Padding = new MarginPadding { Horizontal = 10 }, + Margin = new MarginPadding { Vertical = 5 }, + }; } protected class StandAloneMessage : ChatLine diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 0fa3613d38..404d686d91 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Chat private Container searchIconContainer = null!; private ChatTextBox chatTextBox = null!; - private const float chatting_text_width = 240; + private const float chatting_text_width = 220; private const float search_icon_width = 40; [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 161fe1d5be..7e4e936bee 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; @@ -123,8 +124,9 @@ namespace osu.Game.Overlays.Chat protected virtual DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) { + TextColour = colours.ChatBlue.Lighten(0.7f), + LineColour = colours.ChatBlue.Lighten(0.7f), Margin = new MarginPadding { Vertical = 10 }, - Colour = colours.ChatBlue.Lighten(0.7f), }; private void newMessagesArrived(IEnumerable newMessages) => Schedule(() => @@ -212,6 +214,12 @@ namespace osu.Game.Overlays.Chat set => text.Font = text.Font.With(size: value); } + public ColourInfo TextColour + { + get => text.Colour; + set => text.Colour = value; + } + private float lineHeight = 2; public float LineHeight @@ -220,6 +228,14 @@ namespace osu.Game.Overlays.Chat set => lineHeight = leftBox.Height = rightBox.Height = value; } + private ColourInfo lineColour; + + public ColourInfo LineColour + { + get => lineColour; + set => lineColour = leftBox.Colour = rightBox.Colour = value; + } + private readonly SpriteText text; private readonly Box leftBox; private readonly Box rightBox; diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 4c7fa0f802..cc050a0760 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -3,6 +3,7 @@ #nullable enable +using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; @@ -37,7 +38,7 @@ namespace osu.Game.Overlays private LoadingLayer loading = null!; private ChannelListing channelListing = null!; private ChatTextBar textBar = null!; - private Container currentChannelContainer = null!; + private Container currentChannelContainer = null!; private readonly BindableFloat chatHeight = new BindableFloat(); @@ -120,7 +121,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background4, }, - currentChannelContainer = new Container + currentChannelContainer = new Container { RelativeSizeAxes = Axes.Both, }, @@ -268,7 +269,7 @@ namespace osu.Game.Overlays return; } - LoadComponentAsync(new DrawableChannel(newChannel), loaded => + LoadComponentAsync(new ChatOverlayDrawableChannel(newChannel), loaded => { currentChannelContainer.Clear(); currentChannelContainer.Add(loaded); @@ -311,4 +312,30 @@ namespace osu.Game.Overlays channelManager.PostMessage(message); } } + + public class ChatOverlayDrawableChannel : DrawableChannel + { + private Colour4 daySepTextColour; + private Colour4 daySepLineColour; + + public ChatOverlayDrawableChannel(Channel channel) + : base(channel) + { } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + ChatLineFlow.Padding = new MarginPadding(0); + daySepTextColour = colourProvider.Content1; + daySepLineColour = colourProvider.Background5; + } + + protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) + { + TextColour = daySepTextColour, + LineColour = daySepLineColour, + Margin = new MarginPadding { Vertical = 10 }, + Padding = new MarginPadding { Horizontal = 15 }, + }; + } } From 6fc808be22cb8bf32e8f1bd497f88efa7f89340c Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Fri, 6 May 2022 23:17:59 +0100 Subject: [PATCH 0867/1276] Code quality --- osu.Game/Overlays/ChatOverlayV2.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index cc050a0760..e1ac71a125 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -320,7 +320,8 @@ namespace osu.Game.Overlays public ChatOverlayDrawableChannel(Channel channel) : base(channel) - { } + { + } [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) From 71758390b5db8186bd149647d162417e5607e730 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 7 May 2022 13:37:36 +0900 Subject: [PATCH 0868/1276] Fix time based distance grid alpha not correctly accounting for distance spacing multiplier --- .../Edit/Compose/Components/CircularDistanceSnapGrid.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index c0a3f7d97f..2c6bb766ad 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -131,7 +131,12 @@ namespace osu.Game.Screens.Edit.Compose.Components if (editorClock == null) return; - float distanceForCurrentTime = snapProvider.DurationToDistance(referenceObject, editorClock.CurrentTime - referenceObject.GetEndTime()); + float distanceSpacingMultiplier = (float)snapProvider.DistanceSpacingMultiplier.Value; + double timeFromReferencePoint = editorClock.CurrentTime - referenceObject.GetEndTime(); + + float distanceForCurrentTime = snapProvider.DurationToDistance(referenceObject, timeFromReferencePoint) + * distanceSpacingMultiplier; + float timeBasedAlpha = 1 - Math.Clamp(Math.Abs(distanceForCurrentTime - Size.X / 2) / 30, 0, 1); Colour = baseColour.Opacity(Math.Max(baseColour.A, timeBasedAlpha)); From ce14fddcb2c2950285dd89d96b8587cd9c3fbf6f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 7 May 2022 14:02:07 +0900 Subject: [PATCH 0869/1276] Use above-1 offset instead for more correct feeling parallax --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b3de23ee08..69adca4190 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1153,7 +1153,7 @@ namespace osu.Game horizontalOffset += (Content.ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - Content.DrawWidth) * SIDE_OVERLAY_OFFSET_RATIO; ScreenOffsetContainer.X = horizontalOffset; - overlayContent.X = horizontalOffset * 0.8f; + overlayContent.X = horizontalOffset * 1.2f; MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; } From 0c6de331f3075fe701210031271d79bd4cdeef8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 15:59:19 +0200 Subject: [PATCH 0870/1276] Move footer button padding to a higher level --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 838c3597ae..06baea8364 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -145,13 +145,18 @@ namespace osu.Game.Overlays.Mods }); } + FooterContent.Padding = new MarginPadding + { + Vertical = PADDING, + Horizontal = 70 + }; + if (AllowCustomisation) { - Footer.Add(new ShearedToggleButton(200) + FooterContent.Add(new ShearedToggleButton(200) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = PADDING, Left = 70 }, Text = "Mod Customisation", Active = { BindTarget = customisationVisible } }); From 0b95594f60f8afeab0b3927944e4f28e66b8eb28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 16:02:32 +0200 Subject: [PATCH 0871/1276] Add some more padding between footer and column scroll --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 06baea8364..75f9f4e72e 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -95,6 +95,7 @@ namespace osu.Game.Overlays.Mods Padding = new MarginPadding { Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING, + Bottom = PADDING }, RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, From 852e4a97668421ad78b258d6366204ebb8cdd858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 16:31:59 +0200 Subject: [PATCH 0872/1276] Add select/deselect all buttons to free mod select screen --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 61 +++++++++++++------ .../Screens/OnlinePlay/FreeModSelectScreen.cs | 21 ++++++- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 75f9f4e72e..419a6ee7f7 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -47,11 +47,6 @@ namespace osu.Game.Overlays.Mods } } - /// - /// Whether configurable s can be configured by the local user. - /// - protected virtual bool AllowCustomisation => true; - /// /// Whether the total score multiplier calculated from the current selected set of mods should be shown. /// @@ -59,12 +54,27 @@ namespace osu.Game.Overlays.Mods protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys); + protected virtual Drawable[] CreateFooterButtons() => new Drawable[] + { + customisationButton = new ShearedToggleButton(200) + { + Text = "Mod Customisation", + Active = { BindTarget = customisationVisible } + }, + new ShearedButton(200) + { + Text = "Deselect All", + Action = DeselectAll + } + }; + private readonly BindableBool customisationVisible = new BindableBool(); private DifficultyMultiplierDisplay? multiplierDisplay; private ModSettingsArea modSettingsArea = null!; private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; + private ShearedToggleButton? customisationButton; [BackgroundDependencyLoader] private void load() @@ -146,22 +156,21 @@ namespace osu.Game.Overlays.Mods }); } - FooterContent.Padding = new MarginPadding + FooterContent.Child = new FillFlowContainer { - Vertical = PADDING, - Horizontal = 70 - }; - - if (AllowCustomisation) - { - FooterContent.Add(new ShearedToggleButton(200) + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Padding = new MarginPadding { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Text = "Mod Customisation", - Active = { BindTarget = customisationVisible } - }); - } + Vertical = PADDING, + Horizontal = 70 + }, + Spacing = new Vector2(10), + Children = CreateFooterButtons() + }; } private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null) @@ -216,7 +225,7 @@ namespace osu.Game.Overlays.Mods private void updateCustomisation(ValueChangedEvent> valueChangedEvent) { - if (!AllowCustomisation) + if (customisationButton == null) return; bool anyCustomisableMod = false; @@ -325,6 +334,18 @@ namespace osu.Game.Overlays.Mods } } + protected void SelectAll() + { + foreach (var column in columnFlow.Columns) + column.SelectAll(); + } + + protected void DeselectAll() + { + foreach (var column in columnFlow.Columns) + column.DeselectAll(); + } + public override bool OnPressed(KeyBindingPressEvent e) { if (e.Action == GlobalAction.Back && customisationVisible.Value) diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs index 5a7a60b479..52ca28ce5d 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; using osuTK.Input; @@ -10,7 +12,6 @@ namespace osu.Game.Screens.OnlinePlay { public class FreeModSelectScreen : ModSelectScreen { - protected override bool AllowCustomisation => false; protected override bool ShowTotalMultiplier => false; public new Func IsValidMod @@ -25,5 +26,23 @@ namespace osu.Game.Screens.OnlinePlay } protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys); + + protected override Drawable[] CreateFooterButtons() => new Drawable[] + { + new ShearedButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = "Select All", + Action = SelectAll + }, + new ShearedButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = "Deselect All", + Action = DeselectAll + } + }; } } From 35c106efaab09a067c7b456aabfda8ec7e24fdb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 20:54:36 +0200 Subject: [PATCH 0873/1276] Add test coverage for deselecting all mods on user mod select --- .../UserInterface/TestSceneModSelectScreen.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index c92a738b56..9e0c8206b7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -415,6 +415,23 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("unimplemented mod panel is filtered", () => getPanelForMod(typeof(TestUnimplementedMod)).Filtered.Value); } + [Test] + public void TestDeselectAllViaButton() + { + createScreen(); + changeRuleset(0); + + AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); + AddAssert("DT + HD selected", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + + AddStep("click deselect all button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Last()); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any()); + } + private void waitForColumnLoad() => AddUntilStep("all column content loaded", () => modSelectScreen.ChildrenOfType().Any() && modSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); From e1953c484c0ae08bb4846a1243bf4c3283640ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 16:43:24 +0200 Subject: [PATCH 0874/1276] Add test coverage for selecting/deselecting all mods on free mod select --- .../TestSceneFreeModSelectScreen.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs index 8eaa45696f..a02055d960 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -1,19 +1,32 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay; +using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneFreeModSelectScreen : MultiplayerTestScene { private FreeModSelectScreen freeModSelectScreen; + private readonly Bindable>> availableMods = new Bindable>>(); + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGameBase) + { + availableMods.BindTo(osuGameBase.AvailableMods); + } [Test] public void TestFreeModSelect() @@ -42,6 +55,26 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("customisation area not expanded", () => this.ChildrenOfType().Single().Height == 0); } + [Test] + public void TestSelectDeselectAll() + { + createFreeModSelect(); + + AddStep("click select all button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("all mods selected", assertAllAvailableModsSelected); + + AddStep("click deselect all button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Last()); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("all mods deselected", () => !freeModSelectScreen.SelectedMods.Value.Any()); + } + private void createFreeModSelect() { AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen @@ -52,5 +85,21 @@ namespace osu.Game.Tests.Visual.Multiplayer () => freeModSelectScreen.ChildrenOfType().Any() && freeModSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); } + + private bool assertAllAvailableModsSelected() + { + var allAvailableMods = availableMods.Value + .SelectMany(pair => pair.Value) + .Where(mod => mod.UserPlayable && mod.HasImplementation) + .ToList(); + + foreach (var availableMod in allAvailableMods) + { + if (freeModSelectScreen.SelectedMods.Value.All(selectedMod => selectedMod.GetType() != availableMod.GetType())) + return false; + } + + return true; + } } } From 9f96dd47d12db4a4df8ebfa9268eec86907fb886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 16:48:01 +0200 Subject: [PATCH 0875/1276] Remove schedule in panel load It was causing selection/deselection of all mods to work improperly if a select/deselect all operation was requested before the panel was scrolled into view. In general the schedule was an over-optimisation - the game-global set of available mods shouldn't be changing so often as to warrant such an aggressive debounce. --- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index f6fa591666..7f1d7e0541 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -275,7 +275,7 @@ namespace osu.Game.Overlays.Mods return; localAvailableMods = newMods; - Scheduler.AddOnce(loadPanels); + loadPanels(); } private CancellationTokenSource? cancellationTokenSource; From 9514a5cef7a6b291056697f1305fa159903103a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 17:33:32 +0200 Subject: [PATCH 0876/1276] Only load panels asynchronously outside of BDL --- osu.Game/Overlays/Mods/ModColumn.cs | 41 +++++++++++++++++++---------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 7f1d7e0541..6297b2d92e 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -275,30 +275,25 @@ namespace osu.Game.Overlays.Mods return; localAvailableMods = newMods; - loadPanels(); + + if (!IsLoaded) + // if we're coming from BDL, perform the first load synchronously to make sure everything is in place as early as possible. + onPanelsLoaded(createPanels()); + else + asyncLoadPanels(); } private CancellationTokenSource? cancellationTokenSource; - private void loadPanels() + private void asyncLoadPanels() { cancellationTokenSource?.Cancel(); - var panels = localAvailableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0))); + var panels = createPanels(); Task? loadTask; - latestLoadTask = loadTask = LoadComponentsAsync(panels, loaded => - { - panelFlow.ChildrenEnumerable = loaded; - - updateState(); - - foreach (var panel in panelFlow) - { - panel.Active.BindValueChanged(_ => panelStateChanged(panel)); - } - }, (cancellationTokenSource = new CancellationTokenSource()).Token); + latestLoadTask = loadTask = LoadComponentsAsync(panels, onPanelsLoaded, (cancellationTokenSource = new CancellationTokenSource()).Token); loadTask.ContinueWith(_ => { if (loadTask == latestLoadTask) @@ -306,6 +301,24 @@ namespace osu.Game.Overlays.Mods }); } + private IEnumerable createPanels() + { + var panels = localAvailableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0))); + return panels; + } + + private void onPanelsLoaded(IEnumerable loaded) + { + panelFlow.ChildrenEnumerable = loaded; + + updateState(); + + foreach (var panel in panelFlow) + { + panel.Active.BindValueChanged(_ => panelStateChanged(panel)); + } + } + private void updateState() { foreach (var panel in panelFlow) From 18e4c3ed0f6659ff7c3dc3b5bb089a7c09c04b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 18:08:11 +0200 Subject: [PATCH 0877/1276] Update mod columns even if they're not present/offscreen Important to make "select/deselect all" operations work on all columns simultaneously, even if they're offscreen. Unfortunately by the nature of how the existing selection animation works, it is hard-tied to the update loop, so we need to compensate. --- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 6297b2d92e..07871663a6 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -399,7 +399,7 @@ namespace osu.Game.Overlays.Mods private readonly Queue pendingSelectionOperations = new Queue(); - protected bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0; + internal bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0; protected override void Update() { diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 419a6ee7f7..5ffe07f34a 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -454,6 +454,8 @@ namespace osu.Game.Overlays.Mods FinishTransforms(); } + protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate || Column.SelectionAnimationRunning; + private void updateDim() { Colour4 targetColour; From 4ff96f82be57186874089d942beb35ee4766a015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 22:36:08 +0200 Subject: [PATCH 0878/1276] Dim other buttons if customisation panel is open --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 13 ++++++++++--- osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 5ffe07f34a..e5b4927a6a 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Mods protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys); - protected virtual Drawable[] CreateFooterButtons() => new Drawable[] + protected virtual IEnumerable CreateFooterButtons() => new[] { customisationButton = new ShearedToggleButton(200) { @@ -75,6 +75,7 @@ namespace osu.Game.Overlays.Mods private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; private ShearedToggleButton? customisationButton; + private FillFlowContainer footerButtonFlow = null!; [BackgroundDependencyLoader] private void load() @@ -156,7 +157,7 @@ namespace osu.Game.Overlays.Mods }); } - FooterContent.Child = new FillFlowContainer + FooterContent.Child = footerButtonFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -169,7 +170,7 @@ namespace osu.Game.Overlays.Mods Horizontal = 70 }, Spacing = new Vector2(10), - Children = CreateFooterButtons() + ChildrenEnumerable = CreateFooterButtons() }; } @@ -259,6 +260,12 @@ namespace osu.Game.Overlays.Mods MainAreaContent.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic); + foreach (var button in footerButtonFlow) + { + if (button != customisationButton) + button.Enabled.Value = !customisationVisible.Value; + } + float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0; modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic); diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs index 52ca28ce5d..6298e1f8c0 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; @@ -27,7 +28,7 @@ namespace osu.Game.Screens.OnlinePlay protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys); - protected override Drawable[] CreateFooterButtons() => new Drawable[] + protected override IEnumerable CreateFooterButtons() => new[] { new ShearedButton(200) { From 6ad990dfc3a32d824c08e29ad5dc7d20410b14e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 10:17:24 +0200 Subject: [PATCH 0879/1276] Add basic localisation strings for new mod select --- osu.Game/Localisation/CommonStrings.cs | 10 +++++++ .../Localisation/ModSelectScreenStrings.cs | 29 +++++++++++++++++++ osu.Game/Overlays/Mods/ModSelectScreen.cs | 9 +++--- .../Screens/OnlinePlay/FreeModSelectScreen.cs | 5 ++-- 4 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Localisation/ModSelectScreenStrings.cs diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 52e9811cf7..9cd626af0f 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -59,6 +59,16 @@ namespace osu.Game.Localisation /// public static LocalisableString Importing => new TranslatableString(getKey(@"importing"), @"Importing..."); + /// + /// "Deselect All" + /// + public static LocalisableString DeselectAll => new TranslatableString(getKey(@"deselect_all"), @"Deselect All"); + + /// + /// "Select All" + /// + public static LocalisableString SelectAll => new TranslatableString(getKey(@"select_all"), @"Select All"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Localisation/ModSelectScreenStrings.cs b/osu.Game/Localisation/ModSelectScreenStrings.cs new file mode 100644 index 0000000000..0c113fd381 --- /dev/null +++ b/osu.Game/Localisation/ModSelectScreenStrings.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class ModSelectScreenStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.ModSelectScreen"; + + /// + /// "Mod Select" + /// + public static LocalisableString ModSelectTitle => new TranslatableString(getKey(@"mod_select_title"), @"Mod Select"); + + /// + /// "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun." + /// + public static LocalisableString ModSelectDescription => new TranslatableString(getKey(@"mod_select_description"), @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."); + + /// + /// "Mod Customisation" + /// + public static LocalisableString ModCustomisation => new TranslatableString(getKey(@"mod_customisation"), @"Mod Customisation"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index e5b4927a6a..4a80a0637e 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -23,6 +23,7 @@ using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Input; +using osu.Game.Localisation; namespace osu.Game.Overlays.Mods { @@ -58,12 +59,12 @@ namespace osu.Game.Overlays.Mods { customisationButton = new ShearedToggleButton(200) { - Text = "Mod Customisation", + Text = ModSelectScreenStrings.ModCustomisation, Active = { BindTarget = customisationVisible } }, new ShearedButton(200) { - Text = "Deselect All", + Text = CommonStrings.DeselectAll, Action = DeselectAll } }; @@ -80,8 +81,8 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load() { - Header.Title = "Mod Select"; - Header.Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."; + Header.Title = ModSelectScreenStrings.ModSelectTitle; + Header.Description = ModSelectScreenStrings.ModSelectDescription; AddRange(new Drawable[] { diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs index 6298e1f8c0..5a7fe8a778 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -8,6 +8,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; using osuTK.Input; +using osu.Game.Localisation; namespace osu.Game.Screens.OnlinePlay { @@ -34,14 +35,14 @@ namespace osu.Game.Screens.OnlinePlay { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Text = "Select All", + Text = CommonStrings.SelectAll, Action = SelectAll }, new ShearedButton(200) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Text = "Deselect All", + Text = CommonStrings.DeselectAll, Action = DeselectAll } }; From 271d64fd545deaa4fb42eb6d6464b7283d55f871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 6 May 2022 22:30:07 +0200 Subject: [PATCH 0880/1276] Add back button to mod select overlays --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 4a80a0637e..2af65d89b5 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Mods private FillFlowContainer footerButtonFlow = null!; [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { Header.Title = ModSelectScreenStrings.ModSelectTitle; Header.Description = ModSelectScreenStrings.ModSelectDescription; @@ -171,7 +171,13 @@ namespace osu.Game.Overlays.Mods Horizontal = 70 }, Spacing = new Vector2(10), - ChildrenEnumerable = CreateFooterButtons() + ChildrenEnumerable = CreateFooterButtons().Prepend(new ShearedButton(200) + { + Text = CommonStrings.Back, + Action = Hide, + DarkerColour = colours.Pink2, + LighterColour = colours.Pink1 + }) }; } From 1c029552d7c9308f05969bb640c7adec2f51a889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 10:23:49 +0200 Subject: [PATCH 0881/1276] Add test coverage for mod select back button --- .../UserInterface/TestSceneModSelectScreen.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 9e0c8206b7..42ffeba444 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -432,6 +432,25 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any()); } + [Test] + public void TestCloseViaBackButton() + { + createScreen(); + changeRuleset(0); + + AddStep("select difficulty adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() }); + assertCustomisationToggleState(disabled: false, active: true); + AddAssert("back button disabled", () => !this.ChildrenOfType().First().Enabled.Value); + + AddStep("dismiss customisation area", () => InputManager.Key(Key.Escape)); + AddStep("click back button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + }); + AddAssert("mod select hidden", () => modSelectScreen.State.Value == Visibility.Hidden); + } + private void waitForColumnLoad() => AddUntilStep("all column content loaded", () => modSelectScreen.ChildrenOfType().Any() && modSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); From 9d3e67b10dbf9b6fc27405a50023f735a95d16fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 10:25:23 +0200 Subject: [PATCH 0882/1276] Update free mod select/deselect all test after back button addition --- .../Visual/Multiplayer/TestSceneFreeModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs index a02055d960..4eb14542ba 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -62,7 +62,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("click select all button", () => { - InputManager.MoveMouseTo(this.ChildrenOfType().First()); + InputManager.MoveMouseTo(this.ChildrenOfType().ElementAt(1)); InputManager.Click(MouseButton.Left); }); AddUntilStep("all mods selected", assertAllAvailableModsSelected); From fa0a256f4843f4cd5c3cbfcf1737e613f2ff856a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 10:48:15 +0200 Subject: [PATCH 0883/1276] Add localisable string for difficulty multiplier display --- .../DifficultyMultiplierDisplayStrings.cs | 19 +++++++++++++++++++ .../Mods/DifficultyMultiplierDisplay.cs | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs diff --git a/osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs b/osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs new file mode 100644 index 0000000000..c281d90190 --- /dev/null +++ b/osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class DifficultyMultiplierDisplayStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.DifficultyMultiplierDisplay"; + + /// + /// "Difficulty Multiplier" + /// + public static LocalisableString DifficultyMultiplier => new TranslatableString(getKey(@"difficulty_multiplier"), @"Difficulty Multiplier"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 1d848fe456..4ccec0dd87 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -15,6 +15,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osuTK; +using osu.Game.Localisation; namespace osu.Game.Overlays.Mods { @@ -99,7 +100,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.Centre, Margin = new MarginPadding { Horizontal = 18 }, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Text = "Difficulty Multiplier", + Text = DifficultyMultiplierDisplayStrings.DifficultyMultiplier, Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) } } From f761d4d4d4060597ff01624f513c849090b9160b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 10:56:03 +0200 Subject: [PATCH 0884/1276] Reuse "select/deselect all" localisable string on column toggle --- osu.Game/Overlays/Mods/ModColumn.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 07871663a6..cbd036c71f 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -22,6 +23,7 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; using osu.Game.Rulesets.Mods; using osu.Game.Utils; using osuTK; @@ -220,7 +222,6 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.CentreLeft, Scale = new Vector2(0.8f), RelativeSizeAxes = Axes.X, - LabelText = "Enable All", Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) }); panelFlow.Padding = new MarginPadding @@ -265,6 +266,19 @@ namespace osu.Game.Overlays.Mods contentBackground.Colour = colourProvider.Background4; } + protected override void LoadComplete() + { + base.LoadComplete(); + + toggleAllCheckbox?.Current.BindValueChanged(_ => updateToggleAllText(), true); + } + + private void updateToggleAllText() + { + Debug.Assert(toggleAllCheckbox != null); + toggleAllCheckbox.LabelText = toggleAllCheckbox.Current.Value ? CommonStrings.DeselectAll : CommonStrings.SelectAll; + } + private void updateLocalAvailableMods() { var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty()) From fd75963ca2d4ce479ce1965ae81390b45cf68da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 11:01:05 +0200 Subject: [PATCH 0885/1276] Dismiss mod select if select binding is pressed --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 7d0400aace..706222761d 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -384,7 +384,7 @@ namespace osu.Game.Overlays.Mods return true; } - if (e.Action == Hotkey) + if (e.Action == Hotkey || e.Action == GlobalAction.Select) { if (customisationVisible.Value) customisationVisible.Value = false; From 8ee3fdd0aaa9cd03339ed21576a2aaa080bc4ff3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 7 May 2022 12:15:11 +0300 Subject: [PATCH 0886/1276] Change mod scrolling behaviour to not scroll horizontally on columns/settings --- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- osu.Game/Overlays/Mods/ModSettingsArea.cs | 2 +- .../Mods/NestedVerticalScrollContainer.cs | 48 ------------------- 3 files changed, 2 insertions(+), 50 deletions(-) delete mode 100644 osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 07871663a6..1bc139a181 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -186,7 +186,7 @@ namespace osu.Game.Overlays.Mods }, new Drawable[] { - new NestedVerticalScrollContainer + new OsuScrollContainer(Direction.Vertical) { RelativeSizeAxes = Axes.Both, ClampExtension = 100, diff --git a/osu.Game/Overlays/Mods/ModSettingsArea.cs b/osu.Game/Overlays/Mods/ModSettingsArea.cs index 08f563f93e..f44e4bf07f 100644 --- a/osu.Game/Overlays/Mods/ModSettingsArea.cs +++ b/osu.Game/Overlays/Mods/ModSettingsArea.cs @@ -158,7 +158,7 @@ namespace osu.Game.Overlays.Mods new[] { Empty() }, new Drawable[] { - new NestedVerticalScrollContainer + new OsuScrollContainer(Direction.Vertical) { RelativeSizeAxes = Axes.Both, ClampExtension = 100, diff --git a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs deleted file mode 100644 index d27f97f3d2..0000000000 --- a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable enable - -using osu.Framework.Graphics; -using osu.Framework.Input.Events; -using osu.Game.Graphics.Containers; - -namespace osu.Game.Overlays.Mods -{ - /// - /// A scroll container that handles the case of vertically scrolling content inside a larger horizontally scrolling parent container. - /// - public class NestedVerticalScrollContainer : OsuScrollContainer - { - private ModSelectScreen.ColumnScrollContainer? parentScrollContainer; - - protected override void LoadComplete() - { - base.LoadComplete(); - - parentScrollContainer = this.FindClosestParent(); - } - - protected override bool OnScroll(ScrollEvent e) - { - if (parentScrollContainer == null) - return base.OnScroll(e); - - bool topRightInView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.TopRight); - bool bottomLeftInView = parentScrollContainer.ScreenSpaceDrawQuad.Contains(ScreenSpaceDrawQuad.BottomLeft); - - // If not completely on-screen, handle scroll but also allow parent to scroll at the same time (to hopefully bring our content into full view). - if (!topRightInView || !bottomLeftInView) - return false; - - bool scrollingPastEnd = e.ScrollDelta.Y < 0 && IsScrolledToEnd(); - bool scrollingPastStart = e.ScrollDelta.Y > 0 && Target <= 0; - - // If at either of our extents, delegate scroll to the horizontal parent container. - if (scrollingPastStart || scrollingPastEnd) - return false; - - return base.OnScroll(e); - } - } -} From d4c9de8596b88e268893892b36305048d1faa5ba Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 7 May 2022 13:23:48 +0300 Subject: [PATCH 0887/1276] Move button width in mod select to constant --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 8 +++++--- osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 2af65d89b5..a9af48fc06 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -29,6 +29,8 @@ namespace osu.Game.Overlays.Mods { public abstract class ModSelectScreen : ShearedOverlayContainer { + protected const int BUTTON_WIDTH = 200; + protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green; [Cached] @@ -57,12 +59,12 @@ namespace osu.Game.Overlays.Mods protected virtual IEnumerable CreateFooterButtons() => new[] { - customisationButton = new ShearedToggleButton(200) + customisationButton = new ShearedToggleButton(BUTTON_WIDTH) { Text = ModSelectScreenStrings.ModCustomisation, Active = { BindTarget = customisationVisible } }, - new ShearedButton(200) + new ShearedButton(BUTTON_WIDTH) { Text = CommonStrings.DeselectAll, Action = DeselectAll @@ -171,7 +173,7 @@ namespace osu.Game.Overlays.Mods Horizontal = 70 }, Spacing = new Vector2(10), - ChildrenEnumerable = CreateFooterButtons().Prepend(new ShearedButton(200) + ChildrenEnumerable = CreateFooterButtons().Prepend(new ShearedButton(BUTTON_WIDTH) { Text = CommonStrings.Back, Action = Hide, diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs index 5a7fe8a778..6d241af043 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -31,14 +31,14 @@ namespace osu.Game.Screens.OnlinePlay protected override IEnumerable CreateFooterButtons() => new[] { - new ShearedButton(200) + new ShearedButton(BUTTON_WIDTH) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Text = CommonStrings.SelectAll, Action = SelectAll }, - new ShearedButton(200) + new ShearedButton(BUTTON_WIDTH) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, From a716f62a6a373ed4e37827c7790f2927e5b402c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 12:59:15 +0200 Subject: [PATCH 0888/1276] Flash back button when keyboard back binding is pressed --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index a9af48fc06..8ec2260370 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -77,8 +77,10 @@ namespace osu.Game.Overlays.Mods private ModSettingsArea modSettingsArea = null!; private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; - private ShearedToggleButton? customisationButton; + private FillFlowContainer footerButtonFlow = null!; + private ShearedButton backButton = null!; + private ShearedToggleButton? customisationButton; [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -173,7 +175,7 @@ namespace osu.Game.Overlays.Mods Horizontal = 70 }, Spacing = new Vector2(10), - ChildrenEnumerable = CreateFooterButtons().Prepend(new ShearedButton(BUTTON_WIDTH) + ChildrenEnumerable = CreateFooterButtons().Prepend(backButton = new ShearedButton(BUTTON_WIDTH) { Text = CommonStrings.Back, Action = Hide, @@ -364,9 +366,12 @@ namespace osu.Game.Overlays.Mods public override bool OnPressed(KeyBindingPressEvent e) { - if (e.Action == GlobalAction.Back && customisationVisible.Value) + if (e.Action == GlobalAction.Back) { - customisationVisible.Value = false; + if (customisationVisible.Value) + customisationVisible.Value = false; + else + backButton.TriggerClick(); return true; } From 5d6d7bb324bab493478d8a843278362bfbdcc330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 13:10:03 +0200 Subject: [PATCH 0889/1276] Fix incorrect assert in multiplayer song select test scene --- .../Multiplayer/TestSceneMultiplayerMatchSongSelect.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index d170b12dc3..061fe5715b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -132,7 +132,11 @@ namespace osu.Game.Tests.Visual.Multiplayer private void assertHasFreeModButton(Type type, bool hasButton = true) { AddAssert($"{type.ReadableName()} {(hasButton ? "displayed" : "not displayed")} in freemod overlay", - () => this.ChildrenOfType().Single().ChildrenOfType().All(b => b.Mod.GetType() != type)); + () => this.ChildrenOfType() + .Single() + .ChildrenOfType() + .Where(panel => !panel.Filtered.Value) + .All(b => b.Mod.GetType() != type)); } private class TestMultiplayerMatchSongSelect : MultiplayerMatchSongSelect From 0c2b4a6c0b189571cd175c21e195c9193b8d0f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 15:44:22 +0200 Subject: [PATCH 0890/1276] Fix invalid `IsLoaded` check Would fail when `availableMods` would be changed from a different thread while the columns aren't loaded. --- osu.Game/Overlays/Mods/ModColumn.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index a1ad435733..346e62e5fa 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -250,9 +250,8 @@ namespace osu.Game.Overlays.Mods private void load(OsuGameBase game, OverlayColourProvider colourProvider, OsuColour colours) { availableMods.BindTo(game.AvailableMods); - // this `BindValueChanged` callback is intentionally here, to ensure that local available mods are constructed as early as possible. - // this is needed to make sure no external changes to mods are dropped while mod panels are asynchronously loading. - availableMods.BindValueChanged(_ => updateLocalAvailableMods(), true); + updateLocalAvailableMods(asyncLoadContent: false); + availableMods.BindValueChanged(_ => updateLocalAvailableMods(asyncLoadContent: true)); headerBackground.Colour = accentColour = colours.ForModType(ModType); @@ -279,7 +278,7 @@ namespace osu.Game.Overlays.Mods toggleAllCheckbox.LabelText = toggleAllCheckbox.Current.Value ? CommonStrings.DeselectAll : CommonStrings.SelectAll; } - private void updateLocalAvailableMods() + private void updateLocalAvailableMods(bool asyncLoadContent) { var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty()) .Select(m => m.DeepClone()) @@ -290,11 +289,10 @@ namespace osu.Game.Overlays.Mods localAvailableMods = newMods; - if (!IsLoaded) - // if we're coming from BDL, perform the first load synchronously to make sure everything is in place as early as possible. - onPanelsLoaded(createPanels()); - else + if (asyncLoadContent) asyncLoadPanels(); + else + onPanelsLoaded(createPanels()); } private CancellationTokenSource? cancellationTokenSource; From 0d32bf91eb19af79e5d61bab685fced1f3d26d2e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 May 2022 01:30:21 +0900 Subject: [PATCH 0891/1276] Hardcode hide key handling in `ModSelectScreen` --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 33 +++++++++---------- .../OnlinePlay/OnlinePlaySongSelect.cs | 2 -- osu.Game/Screens/Select/SongSelect.cs | 5 +-- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 3980f6f4b9..2a9e235364 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -20,11 +20,10 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Localisation; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Select; using osuTK; using osuTK.Input; -using osu.Game.Localisation; namespace osu.Game.Overlays.Mods { @@ -49,15 +48,6 @@ namespace osu.Game.Overlays.Mods } } - /// - /// Hotkey that should be used to hide the mod select screen. - /// - /// - /// This is handled locally here rather than via , because this overlay is being registered at the game level - /// and therefore takes away keyboard focus from the screen stack. - /// - internal GlobalAction? Hotkey { get; set; } - /// /// Whether the total score multiplier calculated from the current selected set of mods should be shown. /// @@ -382,6 +372,8 @@ namespace osu.Game.Overlays.Mods if (e.Repeat) return false; + // This is handled locally here because this overlay is being registered at the game level + // and therefore takes away keyboard focus from the screen stack. if (e.Action == GlobalAction.Back) { if (customisationVisible.Value) @@ -391,15 +383,20 @@ namespace osu.Game.Overlays.Mods return true; } - if (e.Action == Hotkey || e.Action == GlobalAction.Select) + switch (e.Action) { - if (customisationVisible.Value) - customisationVisible.Value = false; - Hide(); - return true; - } + case GlobalAction.ToggleModSelection: + case GlobalAction.Select: + { + if (customisationVisible.Value) + customisationVisible.Value = false; + Hide(); + return true; + } - return base.OnPressed(e); + default: + return base.OnPressed(e); + } } internal class ColumnScrollContainer : OsuScrollContainer diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 4b6c06481b..c4503773ad 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Beatmaps; -using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Overlays; @@ -163,7 +162,6 @@ namespace osu.Game.Screens.OnlinePlay protected override ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(OverlayColourScheme.Plum) { - Hotkey = GlobalAction.ToggleModSelection, IsValidMod = IsValidMod }; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 7317b20f88..eb5e996972 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -333,10 +333,7 @@ namespace osu.Game.Screens.Select (new FooterButtonOptions(), BeatmapOptions) }; - protected virtual ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen - { - Hotkey = GlobalAction.ToggleModSelection - }; + protected virtual ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(); protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { From 1d27ef18dc7d4a02aea655aa357ed8f62ec8a6ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 May 2022 01:49:29 +0900 Subject: [PATCH 0892/1276] Add xmldoc for `IsValidMod` --- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- osu.Game/Overlays/Mods/ModSelectScreen.cs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 346e62e5fa..f0741cdc40 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Mods private Func? filter; /// - /// Function determining whether each mod in the column should be displayed. + /// A function determining whether each mod in the column should be displayed. /// A return value of means that the mod is not filtered and therefore its corresponding panel should be displayed. /// A return value of means that the mod is filtered out and therefore its corresponding panel should be hidden. /// diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 2a9e235364..8098e8b0f6 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -36,6 +36,11 @@ namespace osu.Game.Overlays.Mods private Func isValidMod = m => true; + /// + /// A function determining whether each mod in the column should be displayed. + /// A return value of means that the mod is not filtered and therefore its corresponding panel should be displayed. + /// A return value of means that the mod is filtered out and therefore its corresponding panel should be hidden. + /// public Func IsValidMod { get => isValidMod; From 03c80d91978988229b338c4056ef3a7f6d282702 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 May 2022 01:58:37 +0900 Subject: [PATCH 0893/1276] Reorder methods / properties in `ModSelectScreen` for legibility --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 86 ++++++++++++++--------- 1 file changed, 52 insertions(+), 34 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 8098e8b0f6..ef96811144 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -60,29 +60,20 @@ namespace osu.Game.Overlays.Mods protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys); - protected virtual IEnumerable CreateFooterButtons() => new[] - { - customisationButton = new ShearedToggleButton(BUTTON_WIDTH) - { - Text = ModSelectScreenStrings.ModCustomisation, - Active = { BindTarget = customisationVisible } - }, - new ShearedButton(BUTTON_WIDTH) - { - Text = CommonStrings.DeselectAll, - Action = DeselectAll - } - }; + protected virtual IReadOnlyList ComputeNewModsFromSelection(IReadOnlyList oldSelection, IReadOnlyList newSelection) => newSelection; + + protected virtual IEnumerable CreateFooterButtons() => createDefaultFooterButtons(); private readonly BindableBool customisationVisible = new BindableBool(); - private DifficultyMultiplierDisplay? multiplierDisplay; private ModSettingsArea modSettingsArea = null!; private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; - private FillFlowContainer footerButtonFlow = null!; private ShearedButton backButton = null!; + + private DifficultyMultiplierDisplay? multiplierDisplay; + private ShearedToggleButton? customisationButton; protected ModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green) @@ -193,14 +184,6 @@ namespace osu.Game.Overlays.Mods }; } - private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null) - => new ColumnDimContainer(CreateModColumn(modType, toggleKeys)) - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - RequestScroll = column => columnScroll.ScrollIntoView(column, extraScroll: 140) - }; - protected override void LoadComplete() { base.LoadComplete(); @@ -224,6 +207,47 @@ namespace osu.Game.Overlays.Mods updateAvailableMods(); } + /// + /// Select all visible mods in all columns. + /// + protected void SelectAll() + { + foreach (var column in columnFlow.Columns) + column.SelectAll(); + } + + /// + /// Deselect all visible mods in all columns. + /// + protected void DeselectAll() + { + foreach (var column in columnFlow.Columns) + column.DeselectAll(); + } + + private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null) + => new ColumnDimContainer(CreateModColumn(modType, toggleKeys)) + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + RequestScroll = column => columnScroll.ScrollIntoView(column, extraScroll: 140) + }; + + private ShearedButton[] createDefaultFooterButtons() + => new[] + { + customisationButton = new ShearedToggleButton(BUTTON_WIDTH) + { + Text = ModSelectScreenStrings.ModCustomisation, + Active = { BindTarget = customisationVisible } + }, + new ShearedButton(BUTTON_WIDTH) + { + Text = CommonStrings.DeselectAll, + Action = DeselectAll + } + }; + private void updateMultiplier() { if (multiplierDisplay == null) @@ -314,7 +338,7 @@ namespace osu.Game.Overlays.Mods SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection); } - protected virtual IReadOnlyList ComputeNewModsFromSelection(IReadOnlyList oldSelection, IReadOnlyList newSelection) => newSelection; + #region Transition handling protected override void PopIn() { @@ -360,17 +384,9 @@ namespace osu.Game.Overlays.Mods } } - protected void SelectAll() - { - foreach (var column in columnFlow.Columns) - column.SelectAll(); - } + #endregion - protected void DeselectAll() - { - foreach (var column in columnFlow.Columns) - column.DeselectAll(); - } + #region Input handling public override bool OnPressed(KeyBindingPressEvent e) { @@ -404,6 +420,8 @@ namespace osu.Game.Overlays.Mods } } + #endregion + internal class ColumnScrollContainer : OsuScrollContainer { public ColumnScrollContainer() From 2278da563e95e26f4ed6718b6441067df7f67463 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 May 2022 02:03:28 +0900 Subject: [PATCH 0894/1276] Add various xmldoc for nested classes --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index ef96811144..0a1b4e857e 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -422,6 +422,9 @@ namespace osu.Game.Overlays.Mods #endregion + /// + /// Manages horizontal scrolling of mod columns, along with the "active" states of each column based on visibility. + /// internal class ColumnScrollContainer : OsuScrollContainer { public ColumnScrollContainer() @@ -460,6 +463,9 @@ namespace osu.Game.Overlays.Mods } } + /// + /// Manages padding and layout of mod columns. + /// internal class ColumnFlowContainer : FillFlowContainer { public IEnumerable Columns => Children.Select(dimWrapper => dimWrapper.Column); @@ -496,11 +502,21 @@ namespace osu.Game.Overlays.Mods } } + /// + /// Encapsulates a column and provides dim and input blocking based on an externally managed "active" state. + /// internal class ColumnDimContainer : Container { public ModColumn Column { get; } + /// + /// Tracks whether this column is in an interactive state. Generally only the case when the column is on-screen. + /// public readonly Bindable Active = new BindableBool(); + + /// + /// Invoked when the column is clicked while not active, requesting a scroll to be performed to bring it on-screen. + /// public Action? RequestScroll { get; set; } [Resolved] @@ -555,6 +571,9 @@ namespace osu.Game.Overlays.Mods } } + /// + /// A container which blocks and handles input, managing the "return from customisation" state change. + /// private class ClickToReturnContainer : Container { public BindableBool HandleMouse { get; } = new BindableBool(); From 46d3220c07ea99d4ef4dc67756ddee5d781f06a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 21:20:00 +0200 Subject: [PATCH 0895/1276] Isolate sample screens from global mods bindable Fixes scenario wherein entering the first run setup overlay, exiting at the "UI scale" step (which shows a song select), then moving to actua song select and trying to select a mod would lead to a crash. The crash was caused by two active mod screen instances attempting to swap the global mod bindable's mod instances for ones they owned. This logic - while generally problematic and hard to maintain - was fixing several issues with mod reference management and setting copying, so I'm letting it live another day. This change will mean that the song select preview on the "UI scale" step will not receive the same mods that the actual game has enabled. That said, it already doesn't use the same beatmap or ruleset, so this looks fine to break. --- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 862506add2..152d67ab27 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -19,6 +20,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.Select; @@ -131,6 +133,10 @@ namespace osu.Game.Overlays.FirstRunSetup [Cached(typeof(IBindable))] protected Bindable Beatmap { get; private set; } = new Bindable(); + [Cached] + [Cached(typeof(IBindable>))] + protected Bindable> SelectedMods { get; private set; } = new Bindable>(Array.Empty()); + public override bool HandlePositionalInput => false; public override bool HandleNonPositionalInput => false; public override bool PropagatePositionalInputSubTree => false; From 836de491353212ed8acc533437ac72b1c349af38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 22:41:48 +0200 Subject: [PATCH 0896/1276] Adjust skin editor scene library test for extensibility --- .../TestSceneSkinEditorSceneLibrary.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs index d3aeba2c0f..3e6cf325df 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs @@ -20,20 +20,21 @@ namespace osu.Game.Tests.Visual.Navigation { public class TestSceneSkinEditorSceneLibrary : OsuGameTestScene { - private SkinEditor skinEditor; + private TestPlaySongSelect songSelect; + private SkinEditor skinEditor => Game.ChildrenOfType().FirstOrDefault(); - public override void SetUpSteps() + private void advanceToSongSelect() { - base.SetUpSteps(); - - Screens.Select.SongSelect songSelect = null; PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + } + private void openSkinEditor() + { AddStep("open skin editor", () => { InputManager.PressKey(Key.ControlLeft); @@ -42,13 +43,15 @@ namespace osu.Game.Tests.Visual.Navigation InputManager.ReleaseKey(Key.ControlLeft); InputManager.ReleaseKey(Key.ShiftLeft); }); - - AddUntilStep("get skin editor", () => (skinEditor = Game.ChildrenOfType().FirstOrDefault()) != null); + AddUntilStep("skin editor loaded", () => skinEditor != null); } [Test] public void TestEditComponentDuringGameplay() { + advanceToSongSelect(); + openSkinEditor(); + switchToGameplayScene(); BarHitErrorMeter hitErrorMeter = null; @@ -85,6 +88,8 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestAutoplayCompatibleModsRetainedOnEnteringGameplay() { + advanceToSongSelect(); + openSkinEditor(); AddStep("select DT", () => Game.SelectedMods.Value = new Mod[] { new OsuModDoubleTime() }); switchToGameplayScene(); @@ -95,6 +100,8 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestAutoplayIncompatibleModsRemovedOnEnteringGameplay() { + advanceToSongSelect(); + openSkinEditor(); AddStep("select no fail and spun out", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModSpunOut() }); switchToGameplayScene(); @@ -105,6 +112,8 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestDuplicateAutoplayModRemovedOnEnteringGameplay() { + advanceToSongSelect(); + openSkinEditor(); AddStep("select autoplay", () => Game.SelectedMods.Value = new Mod[] { new OsuModAutoplay() }); switchToGameplayScene(); @@ -115,6 +124,8 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestCinemaModRemovedOnEnteringGameplay() { + advanceToSongSelect(); + openSkinEditor(); AddStep("select cinema", () => Game.SelectedMods.Value = new Mod[] { new OsuModCinema() }); switchToGameplayScene(); From a3e61fddcb06c21ed7e12d7af61bd9a7d03a260c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 22:43:45 +0200 Subject: [PATCH 0897/1276] Rename skin editor scene library test scene to be more general --- ...inEditorSceneLibrary.cs => TestSceneSkinEditorNavigation.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Navigation/{TestSceneSkinEditorSceneLibrary.cs => TestSceneSkinEditorNavigation.cs} (98%) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs similarity index 98% rename from osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs rename to osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs index 3e6cf325df..5359d992a1 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs @@ -18,7 +18,7 @@ using static osu.Game.Tests.Visual.Navigation.TestSceneScreenNavigation; namespace osu.Game.Tests.Visual.Navigation { - public class TestSceneSkinEditorSceneLibrary : OsuGameTestScene + public class TestSceneSkinEditorNavigation : OsuGameTestScene { private TestPlaySongSelect songSelect; private SkinEditor skinEditor => Game.ChildrenOfType().FirstOrDefault(); From bdea6d865443e51e2ae927b01d3f347473364907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 22:47:19 +0200 Subject: [PATCH 0898/1276] Add failing test for mod overlay not closing on entering skin editor --- .../Navigation/TestSceneSkinEditorNavigation.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs index 5359d992a1..68c44f49cc 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs @@ -4,6 +4,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Extensions; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; using osu.Game.Overlays.Settings; @@ -133,6 +134,16 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any()); } + [Test] + public void TestModOverlayClosesOnOpeningSkinEditor() + { + advanceToSongSelect(); + AddStep("open mod overlay", () => songSelect.ModSelectOverlay.Show()); + + openSkinEditor(); + AddUntilStep("mod overlay closed", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden); + } + private void switchToGameplayScene() { AddStep("Click gameplay scene button", () => skinEditor.ChildrenOfType().First(b => b.Text == "Gameplay").TriggerClick()); From a45ca2ad101957b9e00560a0b66a01508e724168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 22:50:10 +0200 Subject: [PATCH 0899/1276] Fix focused overlays not registering as such Also fixes bug wherein opening skin editor at song select with mod select open would show mod select on top of the skin editor. --- osu.Game/OsuGame.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index c2a96a9082..54c4231b06 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -201,6 +201,10 @@ namespace osu.Game externalOverlays.Add(overlayContainer); overlayContent.Add(overlayContainer); + + if (overlayContainer is OsuFocusedOverlayContainer focusedOverlayContainer) + focusedOverlays.Add(focusedOverlayContainer); + return new InvokeOnDisposal(() => unregisterBlockingOverlay(overlayContainer)); } @@ -223,6 +227,10 @@ namespace osu.Game private void unregisterBlockingOverlay(OverlayContainer overlayContainer) { externalOverlays.Remove(overlayContainer); + + if (overlayContainer is OsuFocusedOverlayContainer focusedOverlayContainer) + focusedOverlays.Remove(focusedOverlayContainer); + overlayContainer.Expire(); } From bec28c5b28219a9eccfedeed3818e97d41c291ea Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 7 May 2022 22:16:11 +0100 Subject: [PATCH 0900/1276] Remove virtual `ColourInfo` properties from `DaySeparator` --- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 3 +-- osu.Game/Overlays/Chat/DrawableChannel.cs | 18 +----------------- osu.Game/Overlays/ChatOverlayV2.cs | 10 ++-------- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index c03cd938a5..ac6bc190ca 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -174,9 +174,8 @@ namespace osu.Game.Online.Chat protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) { TextSize = 14, - TextColour = daySepColour, + Colour = daySepColour, LineHeight = 1, - LineColour = daySepColour, Padding = new MarginPadding { Horizontal = 10 }, Margin = new MarginPadding { Vertical = 5 }, }; diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 7e4e936bee..0dcb52c60a 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; @@ -124,8 +123,7 @@ namespace osu.Game.Overlays.Chat protected virtual DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) { - TextColour = colours.ChatBlue.Lighten(0.7f), - LineColour = colours.ChatBlue.Lighten(0.7f), + Colour = colours.ChatBlue.Lighten(0.7f), Margin = new MarginPadding { Vertical = 10 }, }; @@ -214,12 +212,6 @@ namespace osu.Game.Overlays.Chat set => text.Font = text.Font.With(size: value); } - public ColourInfo TextColour - { - get => text.Colour; - set => text.Colour = value; - } - private float lineHeight = 2; public float LineHeight @@ -228,14 +220,6 @@ namespace osu.Game.Overlays.Chat set => lineHeight = leftBox.Height = rightBox.Height = value; } - private ColourInfo lineColour; - - public ColourInfo LineColour - { - get => lineColour; - set => lineColour = leftBox.Colour = rightBox.Colour = value; - } - private readonly SpriteText text; private readonly Box leftBox; private readonly Box rightBox; diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index e1ac71a125..18a2f593dd 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -315,26 +315,20 @@ namespace osu.Game.Overlays public class ChatOverlayDrawableChannel : DrawableChannel { - private Colour4 daySepTextColour; - private Colour4 daySepLineColour; - public ChatOverlayDrawableChannel(Channel channel) : base(channel) { } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load() { ChatLineFlow.Padding = new MarginPadding(0); - daySepTextColour = colourProvider.Content1; - daySepLineColour = colourProvider.Background5; } protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) { - TextColour = daySepTextColour, - LineColour = daySepLineColour, + Colour = Colour4.White, Margin = new MarginPadding { Vertical = 10 }, Padding = new MarginPadding { Horizontal = 15 }, }; From f4acfbd0df4380f1ddf9800abae74135027119cf Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 7 May 2022 22:23:46 +0100 Subject: [PATCH 0901/1276] Change return type of `CreateDaySeparator` to `Drawable` --- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 2 +- osu.Game/Overlays/Chat/DrawableChannel.cs | 2 +- osu.Game/Overlays/ChatOverlayV2.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index ac6bc190ca..79c4ed6068 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -171,7 +171,7 @@ namespace osu.Game.Online.Chat protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m); - protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) + protected override Drawable CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) { TextSize = 14, Colour = daySepColour, diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 0dcb52c60a..f2d4a3e301 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -121,7 +121,7 @@ namespace osu.Game.Overlays.Chat protected virtual ChatLine CreateChatLine(Message m) => new ChatLine(m); - protected virtual DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) + protected virtual Drawable CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) { Colour = colours.ChatBlue.Lighten(0.7f), Margin = new MarginPadding { Vertical = 10 }, diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 18a2f593dd..1f2fb9724b 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -326,7 +326,7 @@ namespace osu.Game.Overlays ChatLineFlow.Padding = new MarginPadding(0); } - protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) + protected override Drawable CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) { Colour = Colour4.White, Margin = new MarginPadding { Vertical = 10 }, From c0643cae751fa27e49b5929b2e3ffba0737604b9 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 7 May 2022 23:24:34 +0100 Subject: [PATCH 0902/1276] Create `ChatOverlayDaySeparator` and use in `ChatOverlayDrawableChannel` --- osu.Game/Overlays/ChatOverlayV2.cs | 59 +++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 1f2fb9724b..b0f81c217a 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -17,7 +17,9 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Online.Chat; @@ -326,11 +328,58 @@ namespace osu.Game.Overlays ChatLineFlow.Padding = new MarginPadding(0); } - protected override Drawable CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) + protected override Drawable CreateDaySeparator(DateTimeOffset time) => new ChatOverlayDaySeparator(time); + + private class ChatOverlayDaySeparator : Container { - Colour = Colour4.White, - Margin = new MarginPadding { Vertical = 10 }, - Padding = new MarginPadding { Horizontal = 15 }, - }; + private readonly DateTimeOffset time; + + public ChatOverlayDaySeparator(DateTimeOffset time) + { + this.time = time; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding(15); + Child = new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 215), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + Drawable.Empty(), + new OsuSpriteText + { + Text = time.ToLocalTime().ToString("dd MMMM yyyy").ToUpper(), + Font = OsuFont.Torus.With(size: 15, weight: FontWeight.SemiBold), + Colour = colourProvider.Content1, + Margin = new MarginPadding { Right = 10 }, + }, + new Box + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = colourProvider.Background5, + RelativeSizeAxes = Axes.X, + Height = 2, + }, + }, + }, + }; + } + } } } From 8da7bbd32271d782d2b65d7f84878a2b46c9552a Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 7 May 2022 23:30:46 +0100 Subject: [PATCH 0903/1276] Code quality --- osu.Game/Overlays/ChatOverlayV2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index b0f81c217a..0702b3ad97 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -358,7 +358,7 @@ namespace osu.Game.Overlays }, Content = new[] { - new Drawable[] + new[] { Drawable.Empty(), new OsuSpriteText From 230c4e27b8baee58b553d651d289a5f199f3c962 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 May 2022 12:56:07 +0900 Subject: [PATCH 0904/1276] Simplify and centralise hiding logic for mod overlay Behaviourally, this also always toggles via button triggering to add the button flash animation. --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 40 ++++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 0a1b4e857e..5ec667bd90 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -393,30 +393,38 @@ namespace osu.Game.Overlays.Mods if (e.Repeat) return false; - // This is handled locally here because this overlay is being registered at the game level - // and therefore takes away keyboard focus from the screen stack. - if (e.Action == GlobalAction.Back) - { - if (customisationVisible.Value) - customisationVisible.Value = false; - else - backButton.TriggerClick(); - return true; - } - switch (e.Action) { + case GlobalAction.Back: + // Pressing the back binding should only go back one step at a time. + hideOverlay(false); + return true; + + // This is handled locally here because this overlay is being registered at the game level + // and therefore takes away keyboard focus from the screen stack. case GlobalAction.ToggleModSelection: case GlobalAction.Select: { - if (customisationVisible.Value) - customisationVisible.Value = false; - Hide(); + // Pressing toggle or select should completely hide the overlay in one shot. + hideOverlay(true); return true; } + } - default: - return base.OnPressed(e); + return base.OnPressed(e); + + void hideOverlay(bool immediate) + { + if (customisationVisible.Value) + { + Debug.Assert(customisationButton != null); + customisationButton.TriggerClick(); + + if (!immediate) + return; + } + + backButton.TriggerClick(); } } From 3eeedd802478279e91d72caf4971a027427cd4b9 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 13:45:21 +0900 Subject: [PATCH 0905/1276] Fix per-hit object slider velocity ignored in osu!catch --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7ddbc2f768..b91a74c4a1 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -183,15 +183,15 @@ namespace osu.Game.Beatmaps.Formats SampleControlPoint lastRelevantSamplePoint = null; DifficultyControlPoint lastRelevantDifficultyPoint = null; - bool isOsuRuleset = onlineRulesetID == 0; + // In osu!taiko and osu!mania, a scroll speed is stored as "slider velocity" in legacy formats. + // In that case, a scrolling speed change is a global effect and per-hit object difficulty control points are ignored. + bool scrollSpeedEncodedAsSliderVelocity = onlineRulesetID == 1 || onlineRulesetID == 3; // iterate over hitobjects and pull out all required sample and difficulty changes extractDifficultyControlPoints(beatmap.HitObjects); extractSampleControlPoints(beatmap.HitObjects); - // handle scroll speed, which is stored as "slider velocity" in legacy formats. - // this is relevant for scrolling ruleset beatmaps. - if (!isOsuRuleset) + if (scrollSpeedEncodedAsSliderVelocity) { foreach (var point in legacyControlPoints.EffectPoints) legacyControlPoints.Add(point.Time, new DifficultyControlPoint { SliderVelocity = point.ScrollSpeed }); @@ -242,7 +242,7 @@ namespace osu.Game.Beatmaps.Formats IEnumerable collectDifficultyControlPoints(IEnumerable hitObjects) { - if (!isOsuRuleset) + if (scrollSpeedEncodedAsSliderVelocity) yield break; foreach (var hitObject in hitObjects) From 125628dd20076054c6e5a8ab040d08b729558d6c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 15:52:14 +0900 Subject: [PATCH 0906/1276] Fix hit object not shown in timline while placement is waiting --- .../Edit/Blueprints/BananaShowerPlacementBlueprint.cs | 2 ++ .../Edit/Blueprints/JuiceStreamPlacementBlueprint.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs index 6dea8b0712..4613bfd36e 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs @@ -23,6 +23,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints base.Update(); outline.UpdateFrom(HitObjectContainer, HitObject); + + BeginPlacement(); } protected override bool OnMouseDown(MouseDownEvent e) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs index cff5bc2417..e9c8e2bb2c 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -47,6 +47,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints base.LoadComplete(); inputManager = GetContainingInputManager(); + + BeginPlacement(); } protected override bool OnMouseDown(MouseDownEvent e) From 9ae019eb3945acb74eecebf58aa7baeeaa2a3e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 16:17:23 +0200 Subject: [PATCH 0907/1276] Move `ISamplePlaybackDisabler` to more general namespace --- .../Visual/Gameplay/TestSceneGameplaySamplePlayback.cs | 1 + osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs | 1 - osu.Game/{Screens/Play => Audio}/ISamplePlaybackDisabler.cs | 4 ++-- osu.Game/Screens/Edit/Editor.cs | 1 + osu.Game/Screens/Play/Player.cs | 1 + osu.Game/Skinning/PausableSkinnableSound.cs | 1 - 6 files changed, 5 insertions(+), 4 deletions(-) rename osu.Game/{Screens/Play => Audio}/ISamplePlaybackDisabler.cs (85%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs index ae2bc60fc6..815cc09448 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Testing; +using osu.Game.Audio; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects.Drawables; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs index 64afe1235b..3953ef8b33 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs @@ -15,7 +15,6 @@ using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Framework.Testing; using osu.Game.Audio; -using osu.Game.Screens.Play; using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay diff --git a/osu.Game/Screens/Play/ISamplePlaybackDisabler.cs b/osu.Game/Audio/ISamplePlaybackDisabler.cs similarity index 85% rename from osu.Game/Screens/Play/ISamplePlaybackDisabler.cs rename to osu.Game/Audio/ISamplePlaybackDisabler.cs index 6b37021fe6..65d36e9171 100644 --- a/osu.Game/Screens/Play/ISamplePlaybackDisabler.cs +++ b/osu.Game/Audio/ISamplePlaybackDisabler.cs @@ -4,11 +4,11 @@ using osu.Framework.Bindables; using osu.Game.Skinning; -namespace osu.Game.Screens.Play +namespace osu.Game.Audio { /// /// Allows a component to disable sample playback dynamically as required. - /// Handled by . + /// Automatically handled by . /// public interface ISamplePlaybackDisabler { diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 3fde033587..143d975104 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -19,6 +19,7 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Screens; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ae3eb1ed8b..2f37b578f2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -16,6 +16,7 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Threading; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Skinning/PausableSkinnableSound.cs b/osu.Game/Skinning/PausableSkinnableSound.cs index 10b8c47028..b34351d4e7 100644 --- a/osu.Game/Skinning/PausableSkinnableSound.cs +++ b/osu.Game/Skinning/PausableSkinnableSound.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Threading; using osu.Game.Audio; -using osu.Game.Screens.Play; namespace osu.Game.Skinning { From cbd1169495a283d4d439cfc16edd856b23304f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 16:23:30 +0200 Subject: [PATCH 0908/1276] Move cache declarations of `ISamplePlaybackDisabler` to interface --- osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs | 1 - osu.Game/Audio/ISamplePlaybackDisabler.cs | 2 ++ osu.Game/Screens/Edit/Editor.cs | 1 - osu.Game/Screens/Play/Player.cs | 1 - 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs index 3953ef8b33..31abcb6748 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs @@ -130,7 +130,6 @@ namespace osu.Game.Tests.Visual.Gameplay } [Cached(typeof(ISkinSource))] - [Cached(typeof(ISamplePlaybackDisabler))] private class TestSkinSourceContainer : Container, ISkinSource, ISamplePlaybackDisabler { [Resolved] diff --git a/osu.Game/Audio/ISamplePlaybackDisabler.cs b/osu.Game/Audio/ISamplePlaybackDisabler.cs index 65d36e9171..2f49e94f34 100644 --- a/osu.Game/Audio/ISamplePlaybackDisabler.cs +++ b/osu.Game/Audio/ISamplePlaybackDisabler.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Skinning; @@ -10,6 +11,7 @@ namespace osu.Game.Audio /// Allows a component to disable sample playback dynamically as required. /// Automatically handled by . /// + [Cached] public interface ISamplePlaybackDisabler { /// diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 143d975104..947c184009 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -51,7 +51,6 @@ using osuTK.Input; namespace osu.Game.Screens.Edit { [Cached(typeof(IBeatSnapProvider))] - [Cached(typeof(ISamplePlaybackDisabler))] [Cached] public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IKeyBindingHandler, IBeatSnapProvider, ISamplePlaybackDisabler { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2f37b578f2..51c1e6b43b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -38,7 +38,6 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play { [Cached] - [Cached(typeof(ISamplePlaybackDisabler))] public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler, ILocalUserPlayInfo { /// From 81ca534f878879af0cbb3384fb26fb8084aaeae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 16:28:28 +0200 Subject: [PATCH 0909/1276] Implement `ISamplePlaybackDisabler` in mod select --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 5ec667bd90..4e87b5955a 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -15,6 +15,7 @@ using osu.Framework.Input.Events; using osu.Framework.Layout; using osu.Framework.Lists; using osu.Framework.Utils; +using osu.Game.Audio; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -27,7 +28,7 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public abstract class ModSelectScreen : ShearedOverlayContainer + public abstract class ModSelectScreen : ShearedOverlayContainer, ISamplePlaybackDisabler { protected const int BUTTON_WIDTH = 200; @@ -188,6 +189,8 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); + State.BindValueChanged(_ => samplePlaybackDisabled.Value = State.Value == Visibility.Hidden, true); + ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); SelectedMods.BindValueChanged(val => @@ -430,6 +433,13 @@ namespace osu.Game.Overlays.Mods #endregion + #region Sample playback control + + private readonly Bindable samplePlaybackDisabled = new BindableBool(true); + IBindable ISamplePlaybackDisabler.SamplePlaybackDisabled => samplePlaybackDisabled; + + #endregion + /// /// Manages horizontal scrolling of mod columns, along with the "active" states of each column based on visibility. /// From b92d95a17acb60a0ba671add4a80fc74eda4957f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 May 2022 16:35:34 +0200 Subject: [PATCH 0910/1276] Fix mod panels playing samples when hidden at a higher level --- osu.Game/Audio/ISamplePlaybackDisabler.cs | 1 + osu.Game/Overlays/Mods/ModPanel.cs | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Audio/ISamplePlaybackDisabler.cs b/osu.Game/Audio/ISamplePlaybackDisabler.cs index 2f49e94f34..4167316780 100644 --- a/osu.Game/Audio/ISamplePlaybackDisabler.cs +++ b/osu.Game/Audio/ISamplePlaybackDisabler.cs @@ -10,6 +10,7 @@ namespace osu.Game.Audio /// /// Allows a component to disable sample playback dynamically as required. /// Automatically handled by . + /// May also be manually handled locally to particular components. /// [Cached] public interface ISamplePlaybackDisabler diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index f2a97da3b2..4c4951307d 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -12,6 +14,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Utils; +using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -21,8 +24,6 @@ using osu.Game.Rulesets.UI; using osuTK; using osuTK.Input; -#nullable enable - namespace osu.Game.Overlays.Mods { public class ModPanel : OsuClickableContainer @@ -50,6 +51,7 @@ namespace osu.Game.Overlays.Mods private Colour4 activeColour; + private readonly Bindable samplePlaybackDisabled = new BindableBool(); private Sample? sampleOff; private Sample? sampleOn; @@ -139,13 +141,16 @@ namespace osu.Game.Overlays.Mods Action = Active.Toggle; } - [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuColour colours) + [BackgroundDependencyLoader(true)] + private void load(AudioManager audio, OsuColour colours, ISamplePlaybackDisabler? samplePlaybackDisabler) { sampleOn = audio.Samples.Get(@"UI/check-on"); sampleOff = audio.Samples.Get(@"UI/check-off"); activeColour = colours.ForModType(Mod.Type); + + if (samplePlaybackDisabler != null) + ((IBindable)samplePlaybackDisabled).BindTo(samplePlaybackDisabler.SamplePlaybackDisabled); } protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet); @@ -166,6 +171,9 @@ namespace osu.Game.Overlays.Mods private void playStateChangeSamples() { + if (samplePlaybackDisabled.Value) + return; + if (Active.Value) sampleOn?.Play(); else From 778497b9e27c0bce96d5580d71d9bb915c997b00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 May 2022 17:00:20 +0900 Subject: [PATCH 0911/1276] Scroll mod select slightly into view on first display --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 4e87b5955a..959d9ad2fc 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -208,6 +208,14 @@ namespace osu.Game.Overlays.Mods customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); updateAvailableMods(); + + // Start scrolled slightly to the right to give the user a sense that + // there is more horizontal content available. + ScheduleAfterChildren(() => + { + columnScroll.ScrollTo(100, false); + columnScroll.ScrollToStart(); + }); } /// From b8cb2c1b82570100a6c60388f65d7fa2e240c7fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 May 2022 19:57:03 +0900 Subject: [PATCH 0912/1276] Increase scroll amount slightly --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 959d9ad2fc..fc06af3f9d 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -213,7 +213,7 @@ namespace osu.Game.Overlays.Mods // there is more horizontal content available. ScheduleAfterChildren(() => { - columnScroll.ScrollTo(100, false); + columnScroll.ScrollTo(200, false); columnScroll.ScrollToStart(); }); } From dcf0d5a9d5b93045385c8f4b9f5fe16844294bb3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 21:49:42 +0900 Subject: [PATCH 0913/1276] Fix slider velocity wrongly decoded as scrolling speed in osu!catch --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 79d8bd3bb3..3a893a1238 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -431,9 +431,10 @@ namespace osu.Game.Beatmaps.Formats OmitFirstBarLine = omitFirstBarSignature, }; - bool isOsuRuleset = beatmap.BeatmapInfo.Ruleset.OnlineID == 0; - // scrolling rulesets use effect points rather than difficulty points for scroll speed adjustments. - if (!isOsuRuleset) + int onlineRulesetID = beatmap.BeatmapInfo.Ruleset.OnlineID; + + // osu!taiko and osu!mania use effect points rather than difficulty points for scroll speed adjustments. + if (onlineRulesetID == 1 || onlineRulesetID == 3) effectPoint.ScrollSpeed = speedMultiplier; addControlPoint(time, effectPoint, timingChange); From 92ccec20d787d08e19d81437ab7c89e148175abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 8 May 2022 14:44:54 +0200 Subject: [PATCH 0914/1276] Hide mod columns if all mods within are filtered out --- osu.Game/Overlays/Mods/ModColumn.cs | 13 +++++++ osu.Game/Overlays/Mods/ModSelectScreen.cs | 45 +++++++++++++++++------ 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index f0741cdc40..89f472a290 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -55,8 +55,18 @@ namespace osu.Game.Overlays.Mods } } + /// + /// Determines whether this column should accept user input. + /// public Bindable Active = new BindableBool(true); + private readonly Bindable allFiltered = new BindableBool(); + + /// + /// True if all of the panels in this column have been filtered out by the current . + /// + public IBindable AllFiltered => allFiltered; + /// /// List of mods marked as selected in this column. /// @@ -339,6 +349,9 @@ namespace osu.Game.Overlays.Mods panel.ApplyFilter(Filter); } + allFiltered.Value = panelFlow.All(panel => panel.Filtered.Value); + Alpha = allFiltered.Value ? 0 : 1; + if (toggleAllCheckbox != null && !SelectionAnimationRunning) { toggleAllCheckbox.Alpha = panelFlow.Any(panel => !panel.Filtered.Value) ? 1 : 0; diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index fc06af3f9d..6a3df3fb05 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -237,11 +237,11 @@ namespace osu.Game.Overlays.Mods } private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null) - => new ColumnDimContainer(CreateModColumn(modType, toggleKeys)) + => new ColumnDimContainer(CreateModColumn(modType, toggleKeys).With(column => column.Filter = IsValidMod)) { AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, - RequestScroll = column => columnScroll.ScrollIntoView(column, extraScroll: 140) + RequestScroll = column => columnScroll.ScrollIntoView(column, extraScroll: 140), }; private ShearedButton[] createDefaultFooterButtons() @@ -351,6 +351,8 @@ namespace osu.Game.Overlays.Mods #region Transition handling + private const float distance = 700; + protected override void PopIn() { const double fade_in_duration = 400; @@ -362,13 +364,26 @@ namespace osu.Game.Overlays.Mods .FadeIn(fade_in_duration / 2, Easing.OutQuint) .ScaleTo(1, fade_in_duration, Easing.OutElastic); + int nonFilteredColumnCount = 0; + for (int i = 0; i < columnFlow.Count; i++) { - columnFlow[i].Column - .TopLevelContent - .Delay(i * 30) - .MoveToY(0, fade_in_duration, Easing.OutQuint) - .FadeIn(fade_in_duration, Easing.OutQuint); + var column = columnFlow[i].Column; + + double delay = column.AllFiltered.Value ? 0 : nonFilteredColumnCount * 30; + double duration = column.AllFiltered.Value ? 0 : fade_in_duration; + float startingYPosition = 0; + if (!column.AllFiltered.Value) + startingYPosition = nonFilteredColumnCount % 2 == 0 ? -distance : distance; + + column.TopLevelContent + .MoveToY(startingYPosition) + .Delay(delay) + .MoveToY(0, duration, Easing.OutQuint) + .FadeIn(duration, Easing.OutQuint); + + if (!column.AllFiltered.Value) + nonFilteredColumnCount += 1; } } @@ -382,16 +397,24 @@ namespace osu.Game.Overlays.Mods .FadeOut(fade_out_duration / 2, Easing.OutQuint) .ScaleTo(0.75f, fade_out_duration, Easing.OutQuint); + int nonFilteredColumnCount = 0; + for (int i = 0; i < columnFlow.Count; i++) { - const float distance = 700; - var column = columnFlow[i].Column; + double duration = column.AllFiltered.Value ? 0 : fade_out_duration; + float newYPosition = 0; + if (!column.AllFiltered.Value) + newYPosition = nonFilteredColumnCount % 2 == 0 ? -distance : distance; + column.FlushPendingSelections(); column.TopLevelContent - .MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint) - .FadeOut(fade_out_duration, Easing.OutQuint); + .MoveToY(newYPosition, duration, Easing.OutQuint) + .FadeOut(duration, Easing.OutQuint); + + if (!column.AllFiltered.Value) + nonFilteredColumnCount += 1; } } From 38c004d734be28cdc973bbd8eb9f4ca19e5b55db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 8 May 2022 14:45:01 +0200 Subject: [PATCH 0915/1276] Add test coverage for hiding mod columns --- .../UserInterface/TestSceneModSelectScreen.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 42ffeba444..fa7758df59 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -451,6 +451,36 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("mod select hidden", () => modSelectScreen.State.Value == Visibility.Hidden); } + [Test] + public void TestColumnHiding() + { + AddStep("create screen", () => Child = modSelectScreen = new UserModSelectScreen + { + RelativeSizeAxes = Axes.Both, + State = { Value = Visibility.Visible }, + SelectedMods = { BindTarget = SelectedMods }, + IsValidMod = mod => mod.Type == ModType.DifficultyIncrease || mod.Type == ModType.Conversion + }); + waitForColumnLoad(); + changeRuleset(0); + + AddAssert("two columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 2); + + AddStep("unset filter", () => modSelectScreen.IsValidMod = _ => true); + AddAssert("all columns visible", () => this.ChildrenOfType().All(col => col.IsPresent)); + + AddStep("filter out everything", () => modSelectScreen.IsValidMod = _ => false); + AddAssert("no columns visible", () => this.ChildrenOfType().All(col => !col.IsPresent)); + + AddStep("hide", () => modSelectScreen.Hide()); + AddStep("set filter for 3 columns", () => modSelectScreen.IsValidMod = mod => mod.Type == ModType.DifficultyReduction + || mod.Type == ModType.Automation + || mod.Type == ModType.Conversion); + + AddStep("show", () => modSelectScreen.Show()); + AddUntilStep("3 columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 3); + } + private void waitForColumnLoad() => AddUntilStep("all column content loaded", () => modSelectScreen.ChildrenOfType().Any() && modSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); From 077c77d52459c92b3b4e4f1beb9f634a0585eada Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 8 May 2022 16:00:07 +0300 Subject: [PATCH 0916/1276] Add method for scaling results screen in tests --- .../Multiplayer/TestSceneMultiplayerTeamResults.cs | 12 ++++++++++++ .../Visual/Ranking/TestSceneResultsScreen.cs | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs index bcb36a585f..0237298fa1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -8,11 +8,23 @@ using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Tests.Resources; +using osuTK; namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiplayerTeamResults : ScreenTestScene { + [Test] + public void TestScaling() + { + // scheduling is needed as scaling the content immediately causes the entire scene to shake badly, for some odd reason. + AddSliderStep("scale", 0.5f, 1.6f, 1f, v => Schedule(() => + { + Stack.Scale = new Vector2(v); + Stack.Size = new Vector2(1f / v); + })); + } + [TestCase(7483253, 1048576)] [TestCase(1048576, 7483253)] [TestCase(1048576, 1048576)] diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index 4eed2a25f5..a1d51683e4 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -56,6 +56,17 @@ namespace osu.Game.Tests.Visual.Ranking }); } + [Test] + public void TestScaling() + { + // scheduling is needed as scaling the content immediately causes the entire scene to shake badly, for some odd reason. + AddSliderStep("scale", 0.5f, 1.6f, 1f, v => Schedule(() => + { + Content.Scale = new Vector2(v); + Content.Size = new Vector2(1f / v); + })); + } + [Test] public void TestResultsWithoutPlayer() { From 298c2a1828bf628d59a5bd7ec1b671e925732ec9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 8 May 2022 16:03:57 +0300 Subject: [PATCH 0917/1276] Replace vertical scrolling in results screen with size-preserving container --- .../MultiplayerTeamResultsScreen.cs | 6 ++--- osu.Game/Screens/Ranking/ResultsScreen.cs | 26 ++----------------- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs index 3f0f3e043c..117415ac8f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs @@ -46,9 +46,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { const float winner_background_half_height = 250; - VerticalScrollContent.Anchor = VerticalScrollContent.Origin = Anchor.TopCentre; - VerticalScrollContent.Scale = new Vector2(0.9f); - VerticalScrollContent.Y = 75; + Content.Anchor = Content.Origin = Anchor.TopCentre; + Content.Scale = new Vector2(0.9f); + Content.Y = 75; var redScore = teamScores.First().Value; var blueScore = teamScores.Last().Value; diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 98514cd846..2c91b17917 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -14,7 +14,6 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Online.API; @@ -41,7 +40,7 @@ namespace osu.Game.Screens.Ranking protected ScorePanelList ScorePanelList { get; private set; } - protected VerticalScrollContainer VerticalScrollContent { get; private set; } + protected Container Content { get; private set; } [Resolved(CanBeNull = true)] private Player player { get; set; } @@ -79,10 +78,9 @@ namespace osu.Game.Screens.Ranking { new Drawable[] { - VerticalScrollContent = new VerticalScrollContainer + Content = new DrawSizePreservingFillContainer { RelativeSizeAxes = Axes.Both, - ScrollbarVisible = false, Child = new Container { RelativeSizeAxes = Axes.Both, @@ -346,25 +344,5 @@ namespace osu.Game.Screens.Ranking public void OnReleased(KeyBindingReleaseEvent e) { } - - protected class VerticalScrollContainer : OsuScrollContainer - { - protected override Container Content => content; - - private readonly Container content; - - public VerticalScrollContainer() - { - Masking = false; - - base.Content.Add(content = new Container { RelativeSizeAxes = Axes.X }); - } - - protected override void Update() - { - base.Update(); - content.Height = Math.Max(screen_height, DrawHeight); - } - } } } From 7f1ad149d5775606497144af30d94e801222e8a7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 8 May 2022 16:04:24 +0300 Subject: [PATCH 0918/1276] Remove no longer necessary horizontal scroll blocker --- osu.Game/Screens/Ranking/ScorePanelList.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index c2ef5529e8..a5341242e2 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -85,7 +85,6 @@ namespace osu.Game.Screens.Ranking InternalChild = scroll = new Scroll { RelativeSizeAxes = Axes.Both, - HandleScroll = () => expandedPanel?.IsHovered != true, // handle horizontal scroll only when not hovering the expanded panel. Child = flow = new Flow { Anchor = Anchor.Centre, @@ -359,11 +358,6 @@ namespace osu.Game.Screens.Ranking /// public float? InstantScrollTarget; - /// - /// Whether this container should handle scroll trigger events. - /// - public Func HandleScroll; - protected override void UpdateAfterChildren() { if (InstantScrollTarget != null) @@ -374,10 +368,6 @@ namespace osu.Game.Screens.Ranking base.UpdateAfterChildren(); } - - public override bool HandlePositionalInput => HandleScroll(); - - public override bool HandleNonPositionalInput => HandleScroll(); } } } From 6f4cdccf6c5430991c8e4c87a6ad60698faff4ce Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 8 May 2022 16:20:26 +0300 Subject: [PATCH 0919/1276] Remove no longer required constant --- osu.Game/Screens/Ranking/ResultsScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 2c91b17917..87e49fcc5e 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -27,7 +27,6 @@ namespace osu.Game.Screens.Ranking public abstract class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler { protected const float BACKGROUND_BLUR = 20; - private static readonly float screen_height = 768 - TwoLayerButton.SIZE_EXTENDED.Y; public override bool DisallowExternalBeatmapRulesetChanges => true; From 4e0155fa4bac0dd8a8316f4cf1e80b610a465e17 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 18:32:01 +0900 Subject: [PATCH 0920/1276] Make `JuiceStreamPath` time based instead of distance based. And remove the "slope limit" feature. TODO: for a juice stream with a large slope, the slider velocity of the hit object should be changed. --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 8 +- .../JuiceStreamPathTest.cs | 95 +++++------ .../Blueprints/Components/EditablePath.cs | 30 ++-- .../Components/PlacementEditablePath.cs | 10 +- .../Components/SelectionEditablePath.cs | 12 +- .../JuiceStreamPlacementBlueprint.cs | 6 +- .../JuiceStreamSelectionBlueprint.cs | 6 +- .../Objects/JuiceStreamPath.cs | 158 ++++++++---------- .../Objects/JuiceStreamPathVertex.cs | 10 +- 9 files changed, 157 insertions(+), 178 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index fb77fb1efd..123316f461 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -234,10 +234,10 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor { var path = new JuiceStreamPath(); for (int i = 1; i < times.Length; i++) - path.Add((times[i] - times[0]) * velocity, positions[i] - positions[0]); + path.Add(times[i] - times[0], positions[i] - positions[0]); var sliderPath = new SliderPath(); - path.ConvertToSliderPath(sliderPath, 0); + path.ConvertToSliderPath(sliderPath, 0, velocity); addBlueprintStep(times[0], positions[0], sliderPath, velocity); } @@ -245,11 +245,11 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor private void addVertexCheckStep(int count, int index, double time, float x) => AddAssert($"vertex {index} of {count} at {time}, {x}", () => { - double expectedDistance = (time - hitObject.StartTime) * hitObject.Velocity; + double expectedTime = time - hitObject.StartTime; float expectedX = x - hitObject.OriginalX; var vertices = getVertices(); return vertices.Count == count && - Precision.AlmostEquals(vertices[index].Distance, expectedDistance, 1e-3) && + Precision.AlmostEquals(vertices[index].Time, expectedTime, 1e-3) && Precision.AlmostEquals(vertices[index].X, expectedX); }); diff --git a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs index 8fa96fb8c9..5248d5a96a 100644 --- a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -37,14 +36,14 @@ namespace osu.Game.Rulesets.Catch.Tests { case 0: { - double distance = rng.NextDouble() * scale * 2 - scale; + double time = rng.NextDouble() * scale * 2 - scale; if (integralValues) - distance = Math.Round(distance); + time = Math.Round(time); - float oldX = path.PositionAtDistance(distance); - int index = path.InsertVertex(distance); + float oldX = path.PositionAtTime(time); + int index = path.InsertVertex(time); Assert.That(path.Vertices.Count, Is.EqualTo(vertexCount + 1)); - Assert.That(path.Vertices[index].Distance, Is.EqualTo(distance)); + Assert.That(path.Vertices[index].Time, Is.EqualTo(time)); Assert.That(path.Vertices[index].X, Is.EqualTo(oldX)); break; } @@ -52,20 +51,20 @@ namespace osu.Game.Rulesets.Catch.Tests case 1: { int index = rng.Next(path.Vertices.Count); - double distance = path.Vertices[index].Distance; + double time = path.Vertices[index].Time; float newX = (float)(rng.NextDouble() * scale * 2 - scale); if (integralValues) newX = MathF.Round(newX); path.SetVertexPosition(index, newX); Assert.That(path.Vertices.Count, Is.EqualTo(vertexCount)); - Assert.That(path.Vertices[index].Distance, Is.EqualTo(distance)); + Assert.That(path.Vertices[index].Time, Is.EqualTo(time)); Assert.That(path.Vertices[index].X, Is.EqualTo(newX)); break; } } - assertInvariants(path.Vertices, checkSlope); + assertInvariants(path.Vertices); } } @@ -76,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Tests path.Add(10, 5); path.Add(20, -5); - int removeCount = path.RemoveVertices((v, i) => v.Distance == 10 && i == 1); + int removeCount = path.RemoveVertices((v, i) => v.Time == 10 && i == 1); Assert.That(removeCount, Is.EqualTo(1)); Assert.That(path.Vertices, Is.EqualTo(new[] { @@ -131,8 +130,9 @@ namespace osu.Game.Rulesets.Catch.Tests })); } - [Test] - public void TestRandomConvertFromSliderPath() + [TestCase(10)] + [TestCase(0.1)] + public void TestRandomConvertFromSliderPath(double velocity) { var rng = new Random(1); var path = new JuiceStreamPath(); @@ -162,28 +162,28 @@ namespace osu.Game.Rulesets.Catch.Tests else sliderPath.ExpectedDistance.Value = null; - path.ConvertFromSliderPath(sliderPath); - Assert.That(path.Vertices[0].Distance, Is.EqualTo(0)); - Assert.That(path.Distance, Is.EqualTo(sliderPath.Distance).Within(1e-3)); - assertInvariants(path.Vertices, true); + path.ConvertFromSliderPath(sliderPath, velocity); + Assert.That(path.Vertices[0].Time, Is.EqualTo(0)); + Assert.That(path.Duration * velocity, Is.EqualTo(sliderPath.Distance).Within(1e-3)); + assertInvariants(path.Vertices); - double[] sampleDistances = Enumerable.Range(0, 10) - .Select(_ => rng.NextDouble() * sliderPath.Distance) - .ToArray(); + double[] sampleTimes = Enumerable.Range(0, 10) + .Select(_ => rng.NextDouble() * sliderPath.Distance / velocity) + .ToArray(); - foreach (double distance in sampleDistances) + foreach (double time in sampleTimes) { - float expected = sliderPath.PositionAt(distance / sliderPath.Distance).X; - Assert.That(path.PositionAtDistance(distance), Is.EqualTo(expected).Within(1e-3)); + float expected = sliderPath.PositionAt(time * velocity / sliderPath.Distance).X; + Assert.That(path.PositionAtTime(time), Is.EqualTo(expected).Within(1e-3)); } - path.ResampleVertices(sampleDistances); - assertInvariants(path.Vertices, true); + path.ResampleVertices(sampleTimes); + assertInvariants(path.Vertices); - foreach (double distance in sampleDistances) + foreach (double time in sampleTimes) { - float expected = sliderPath.PositionAt(distance / sliderPath.Distance).X; - Assert.That(path.PositionAtDistance(distance), Is.EqualTo(expected).Within(1e-3)); + float expected = sliderPath.PositionAt(time * velocity / sliderPath.Distance).X; + Assert.That(path.PositionAtTime(time), Is.EqualTo(expected).Within(1e-3)); } } } @@ -201,17 +201,17 @@ namespace osu.Game.Rulesets.Catch.Tests do { - double distance = rng.NextDouble() * 1e3; + double time = rng.NextDouble() * 1e3; float x = (float)(rng.NextDouble() * 1e3); - path.Add(distance, x); + path.Add(time, x); } while (rng.Next(5) != 0); float sliderStartY = (float)(rng.NextDouble() * JuiceStreamPath.OSU_PLAYFIELD_HEIGHT); - path.ConvertToSliderPath(sliderPath, sliderStartY); - Assert.That(sliderPath.Distance, Is.EqualTo(path.Distance).Within(1e-3)); - Assert.That(sliderPath.ControlPoints[0].Position.X, Is.EqualTo(path.Vertices[0].X)); - assertInvariants(path.Vertices, true); + double requiredVelocity = path.ComputeRequiredVelocity(); + double velocity = Math.Clamp(requiredVelocity, 1, 100); + + path.ConvertToSliderPath(sliderPath, sliderStartY, velocity); foreach (var point in sliderPath.ControlPoints) { @@ -219,11 +219,18 @@ namespace osu.Game.Rulesets.Catch.Tests Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT)); } + Assert.That(sliderPath.ControlPoints[0].Position.X, Is.EqualTo(path.Vertices[0].X)); + + // The path is preserved only if required velocity is used. + if (velocity < requiredVelocity) continue; + + Assert.That(sliderPath.Distance / velocity, Is.EqualTo(path.Duration).Within(1e-3)); + for (int i = 0; i < 10; i++) { - double distance = rng.NextDouble() * path.Distance; - float expected = path.PositionAtDistance(distance); - Assert.That(sliderPath.PositionAt(distance / sliderPath.Distance).X, Is.EqualTo(expected).Within(1e-3)); + double time = rng.NextDouble() * path.Duration; + float expected = path.PositionAtTime(time); + Assert.That(sliderPath.PositionAt(time * velocity / sliderPath.Distance).X, Is.EqualTo(expected).Within(3e-3)); } } } @@ -244,7 +251,7 @@ namespace osu.Game.Rulesets.Catch.Tests path.Add(20, 0); checkNewId(); - path.RemoveVertices((v, _) => v.Distance == 20); + path.RemoveVertices((v, _) => v.Time == 20); checkNewId(); path.ResampleVertices(new double[] { 5, 10, 15 }); @@ -253,7 +260,7 @@ namespace osu.Game.Rulesets.Catch.Tests path.Clear(); checkNewId(); - path.ConvertFromSliderPath(new SliderPath()); + path.ConvertFromSliderPath(new SliderPath(), 1); checkNewId(); void checkNewId() @@ -263,25 +270,19 @@ namespace osu.Game.Rulesets.Catch.Tests } } - private void assertInvariants(IReadOnlyList vertices, bool checkSlope) + private void assertInvariants(IReadOnlyList vertices) { Assert.That(vertices, Is.Not.Empty); for (int i = 0; i < vertices.Count; i++) { - Assert.That(double.IsFinite(vertices[i].Distance)); + Assert.That(double.IsFinite(vertices[i].Time)); Assert.That(float.IsFinite(vertices[i].X)); } for (int i = 1; i < vertices.Count; i++) { - Assert.That(vertices[i].Distance, Is.GreaterThanOrEqualTo(vertices[i - 1].Distance)); - - if (!checkSlope) continue; - - float xDiff = Math.Abs(vertices[i].X - vertices[i - 1].X); - double distanceDiff = vertices[i].Distance - vertices[i - 1].Distance; - Assert.That(xDiff, Is.LessThanOrEqualTo(distanceDiff).Within(Precision.FLOAT_EPSILON)); + Assert.That(vertices[i].Time, Is.GreaterThanOrEqualTo(vertices[i - 1].Time)); } } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index 1a43a10c81..3004d3644c 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components public int VertexCount => path.Vertices.Count; - protected readonly Func PositionToDistance; + protected readonly Func PositionToTime; protected IReadOnlyList VertexStates => vertexStates; @@ -44,9 +44,9 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components [CanBeNull] private IBeatSnapProvider beatSnapProvider { get; set; } - protected EditablePath(Func positionToDistance) + protected EditablePath(Func positionToTime) { - PositionToDistance = positionToDistance; + PositionToTime = positionToTime; Anchor = Anchor.BottomLeft; } @@ -59,13 +59,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components while (InternalChildren.Count < path.Vertices.Count) AddInternal(new VertexPiece()); - double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity); + double timeToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1); for (int i = 0; i < VertexCount; i++) { var piece = (VertexPiece)InternalChildren[i]; var vertex = path.Vertices[i]; - piece.Position = new Vector2(vertex.X, (float)(vertex.Distance * distanceToYFactor)); + piece.Position = new Vector2(vertex.X, (float)(vertex.Time * timeToYFactor)); piece.UpdateFrom(vertexStates[i]); } } @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components public void InitializeFromHitObject(JuiceStream hitObject) { var sliderPath = hitObject.Path; - path.ConvertFromSliderPath(sliderPath); + path.ConvertFromSliderPath(sliderPath, hitObject.Velocity); // If the original slider path has non-linear type segments, resample the vertices at nested hit object times to reduce the number of vertices. if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.Linear)) @@ -92,11 +92,11 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components public void UpdateHitObjectFromPath(JuiceStream hitObject) { - path.ConvertToSliderPath(hitObject.Path, hitObject.LegacyConvertedY); + path.ConvertToSliderPath(hitObject.Path, hitObject.LegacyConvertedY, hitObject.Velocity); if (beatSnapProvider == null) return; - double endTime = hitObject.StartTime + path.Distance / hitObject.Velocity; + double endTime = hitObject.StartTime + path.Duration; double snappedEndTime = beatSnapProvider.SnapTime(endTime, hitObject.StartTime); hitObject.Path.ExpectedDistance.Value = (snappedEndTime - hitObject.StartTime) * hitObject.Velocity; } @@ -108,9 +108,9 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; - protected int AddVertex(double distance, float x) + protected int AddVertex(double time, float x) { - int index = path.InsertVertex(distance); + int index = path.InsertVertex(time); path.SetVertexPosition(index, x); vertexStates.Insert(index, new VertexState()); @@ -138,9 +138,9 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components return true; } - protected void MoveSelectedVertices(double distanceDelta, float xDelta) + protected void MoveSelectedVertices(double timeDelta, float xDelta) { - // Because the vertex list may be reordered due to distance change, the state list must be reordered as well. + // Because the vertex list may be reordered due to time change, the state list must be reordered as well. previousVertexStates.Clear(); previousVertexStates.AddRange(vertexStates); @@ -152,11 +152,11 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components for (int i = 1; i < vertexCount; i++) { var state = previousVertexStates[i]; - double distance = state.VertexBeforeChange.Distance; + double time = state.VertexBeforeChange.Time; if (state.IsSelected) - distance += distanceDelta; + time += timeDelta; - int newIndex = path.InsertVertex(Math.Max(0, distance)); + int newIndex = path.InsertVertex(Math.Max(0, time)); vertexStates.Insert(newIndex, state); } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs index 158872fbab..511aec5e5d 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs @@ -15,15 +15,15 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components /// private JuiceStreamPathVertex lastVertex; - public PlacementEditablePath(Func positionToDistance) - : base(positionToDistance) + public PlacementEditablePath(Func positionToTime) + : base(positionToTime) { } public void AddNewVertex() { var endVertex = Vertices[^1]; - int index = AddVertex(endVertex.Distance, endVertex.X); + int index = AddVertex(endVertex.Time, endVertex.X); for (int i = 0; i < VertexCount; i++) { @@ -41,9 +41,9 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components public void MoveLastVertex(Vector2 screenSpacePosition) { Vector2 position = ToRelativePosition(screenSpacePosition); - double distanceDelta = PositionToDistance(position.Y) - lastVertex.Distance; + double timeDelta = PositionToTime(position.Y) - lastVertex.Time; float xDelta = position.X - lastVertex.X; - MoveSelectedVertices(distanceDelta, xDelta); + MoveSelectedVertices(timeDelta, xDelta); } } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index 8c7314d0b6..b4c353313c 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -27,15 +27,15 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components [CanBeNull] private IEditorChangeHandler changeHandler { get; set; } - public SelectionEditablePath(Func positionToDistance) - : base(positionToDistance) + public SelectionEditablePath(Func positionToTime) + : base(positionToTime) { } public void AddVertex(Vector2 relativePosition) { - double distance = Math.Max(0, PositionToDistance(relativePosition.Y)); - int index = AddVertex(distance, relativePosition.X); + double time = Math.Max(0, PositionToTime(relativePosition.Y)); + int index = AddVertex(time, relativePosition.X); selectOnly(index); } @@ -83,9 +83,9 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components protected override void OnDrag(DragEvent e) { Vector2 mousePosition = ToRelativePosition(e.ScreenSpaceMousePosition); - double distanceDelta = PositionToDistance(mousePosition.Y) - PositionToDistance(dragStartPosition.Y); + double timeDelta = PositionToTime(mousePosition.Y) - PositionToTime(dragStartPosition.Y); float xDelta = mousePosition.X - dragStartPosition.X; - MoveSelectedVertices(distanceDelta, xDelta); + MoveSelectedVertices(timeDelta, xDelta); } protected override void OnDragEnd(DragEndEvent e) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs index cff5bc2417..4a5a1d8160 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { scrollingPath = new ScrollingPath(), nestedOutlineContainer = new NestedOutlineContainer(), - editablePath = new PlacementEditablePath(positionToDistance) + editablePath = new PlacementEditablePath(positionToTime) }; } @@ -119,10 +119,10 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints lastEditablePathId = editablePath.PathId; } - private double positionToDistance(float relativeYPosition) + private double positionToTime(float relativeYPosition) { double time = HitObjectContainer.TimeAtPosition(relativeYPosition, HitObject.StartTime); - return (time - HitObject.StartTime) * HitObject.Velocity; + return time - HitObject.StartTime; } } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index 890d059d19..12054a1d16 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { scrollingPath = new ScrollingPath(), nestedOutlineContainer = new NestedOutlineContainer(), - editablePath = new SelectionEditablePath(positionToDistance) + editablePath = new SelectionEditablePath(positionToTime) }; } @@ -145,10 +145,10 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints return new RectangleF(left, top, right - left, bottom - top).Inflate(objectRadius); } - private double positionToDistance(float relativeYPosition) + private double positionToTime(float relativeYPosition) { double time = HitObjectContainer.TimeAtPosition(relativeYPosition, HitObject.StartTime); - return (time - HitObject.StartTime) * HitObject.Velocity; + return time - HitObject.StartTime; } private void initializeJuiceStreamPath() diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs index 7207833fe6..d8cea3945c 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs @@ -20,11 +20,6 @@ namespace osu.Game.Rulesets.Catch.Objects /// However, the representation is difficult to work with. /// This represents the path in a more convenient way, a polyline connecting list of s. /// - /// - /// The path can be regarded as a function from the closed interval [Vertices[0].Distance, Vertices[^1].Distance] to the x position, given by . - /// To ensure the path is convertible to a , the slope of the function must not be more than 1 everywhere, - /// and this slope condition is always maintained as an invariant. - /// /// public class JuiceStreamPath { @@ -46,9 +41,9 @@ namespace osu.Game.Rulesets.Catch.Objects public int InvalidationID { get; private set; } = 1; /// - /// The difference between first vertex's and last vertex's . + /// The difference between first vertex's and last vertex's . /// - public double Distance => vertices[^1].Distance - vertices[0].Distance; + public double Duration => vertices[^1].Time - vertices[0].Time; /// /// This list should always be non-empty. @@ -59,15 +54,15 @@ namespace osu.Game.Rulesets.Catch.Objects }; /// - /// Compute the x-position of the path at the given . + /// Compute the x-position of the path at the given . /// /// - /// When the given distance is outside of the path, the x position at the corresponding endpoint is returned, + /// When the given time is outside of the path, the x position at the corresponding endpoint is returned, /// - public float PositionAtDistance(double distance) + public float PositionAtTime(double time) { - int index = vertexIndexAtDistance(distance); - return positionAtDistance(distance, index); + int index = vertexIndexAtTime(time); + return positionAtTime(time, index); } /// @@ -81,19 +76,19 @@ namespace osu.Game.Rulesets.Catch.Objects } /// - /// Insert a vertex at given . - /// The is used as the position of the new vertex. + /// Insert a vertex at given . + /// The is used as the position of the new vertex. /// Thus, the set of points of the path is not changed (up to floating-point precision). /// /// The index of the new vertex. - public int InsertVertex(double distance) + public int InsertVertex(double time) { - if (!double.IsFinite(distance)) - throw new ArgumentOutOfRangeException(nameof(distance)); + if (!double.IsFinite(time)) + throw new ArgumentOutOfRangeException(nameof(time)); - int index = vertexIndexAtDistance(distance); - float x = positionAtDistance(distance, index); - vertices.Insert(index, new JuiceStreamPathVertex(distance, x)); + int index = vertexIndexAtTime(time); + float x = positionAtTime(time, index); + vertices.Insert(index, new JuiceStreamPathVertex(time, x)); invalidate(); return index; @@ -101,7 +96,6 @@ namespace osu.Game.Rulesets.Catch.Objects /// /// Move the vertex of given to the given position . - /// When the distances between vertices are too small for the new vertex positions, the adjacent vertices are moved towards . /// public void SetVertexPosition(int index, float newX) { @@ -111,32 +105,17 @@ namespace osu.Game.Rulesets.Catch.Objects if (!float.IsFinite(newX)) throw new ArgumentOutOfRangeException(nameof(newX)); - var newVertex = new JuiceStreamPathVertex(vertices[index].Distance, newX); - - for (int i = index - 1; i >= 0 && !canConnect(vertices[i], newVertex); i--) - { - float clampedX = clampToConnectablePosition(newVertex, vertices[i]); - vertices[i] = new JuiceStreamPathVertex(vertices[i].Distance, clampedX); - } - - for (int i = index + 1; i < vertices.Count; i++) - { - float clampedX = clampToConnectablePosition(newVertex, vertices[i]); - vertices[i] = new JuiceStreamPathVertex(vertices[i].Distance, clampedX); - } - - vertices[index] = newVertex; + vertices[index] = new JuiceStreamPathVertex(vertices[index].Time, newX); invalidate(); } /// - /// Add a new vertex at given and position. - /// Adjacent vertices are moved when necessary in the same way as . + /// Add a new vertex at given and position. /// - public void Add(double distance, float x) + public void Add(double time, float x) { - int index = InsertVertex(distance); + int index = InsertVertex(time); SetVertexPosition(index, x); } @@ -163,22 +142,22 @@ namespace osu.Game.Rulesets.Catch.Objects } /// - /// Recreate this path by using difference set of vertices at given distances. - /// In addition to the given , the first vertex and the last vertex are always added to the new path. - /// New vertices use the positions on the original path. Thus, s at are preserved. + /// Recreate this path by using difference set of vertices at given time points. + /// In addition to the given , the first vertex and the last vertex are always added to the new path. + /// New vertices use the positions on the original path. Thus, s at are preserved. /// - public void ResampleVertices(IEnumerable sampleDistances) + public void ResampleVertices(IEnumerable sampleTimes) { var sampledVertices = new List(); - foreach (double distance in sampleDistances) + foreach (double time in sampleTimes) { - if (!double.IsFinite(distance)) - throw new ArgumentOutOfRangeException(nameof(sampleDistances)); + if (!double.IsFinite(time)) + throw new ArgumentOutOfRangeException(nameof(sampleTimes)); - double clampedDistance = Math.Clamp(distance, vertices[0].Distance, vertices[^1].Distance); - float x = PositionAtDistance(clampedDistance); - sampledVertices.Add(new JuiceStreamPathVertex(clampedDistance, x)); + double clampedTime = Math.Clamp(time, vertices[0].Time, vertices[^1].Time); + float x = PositionAtTime(clampedTime); + sampledVertices.Add(new JuiceStreamPathVertex(clampedTime, x)); } sampledVertices.Sort(); @@ -196,37 +175,57 @@ namespace osu.Game.Rulesets.Catch.Objects /// /// Duplicated vertices are automatically removed. /// - public void ConvertFromSliderPath(SliderPath sliderPath) + public void ConvertFromSliderPath(SliderPath sliderPath, double velocity) { var sliderPathVertices = new List(); sliderPath.GetPathToProgress(sliderPathVertices, 0, 1); - double distance = 0; + double time = 0; vertices.Clear(); vertices.Add(new JuiceStreamPathVertex(0, sliderPathVertices.FirstOrDefault().X)); for (int i = 1; i < sliderPathVertices.Count; i++) { - distance += Vector2.Distance(sliderPathVertices[i - 1], sliderPathVertices[i]); + time += Vector2.Distance(sliderPathVertices[i - 1], sliderPathVertices[i]) / velocity; - if (!Precision.AlmostEquals(vertices[^1].Distance, distance)) - vertices.Add(new JuiceStreamPathVertex(distance, sliderPathVertices[i].X)); + if (!Precision.AlmostEquals(vertices[^1].Time, time)) + Add(time, sliderPathVertices[i].X); } invalidate(); } + /// + /// Computes the minimum slider velocity required to convert this path to a . + /// + public double ComputeRequiredVelocity() + { + double maximumSlope = 0; + + for (int i = 1; i < vertices.Count; i++) + { + double xDifference = Math.Abs((double)vertices[i].X - vertices[i - 1].X); + double timeDifference = vertices[i].Time - vertices[i - 1].Time; + maximumSlope = Math.Max(maximumSlope, xDifference / timeDifference); + } + + return maximumSlope; + } + /// /// Convert the path of this to a and write the result to . /// The resulting slider is "folded" to make it vertically contained in the playfield `(0..)` assuming the slider start position is . + /// + /// The velocity of the converted slider is assumed to be . + /// To preserve the path, should be at least the value returned by . /// - public void ConvertToSliderPath(SliderPath sliderPath, float sliderStartY) + public void ConvertToSliderPath(SliderPath sliderPath, float sliderStartY, double velocity) { const float margin = 1; // Note: these two variables and `sliderPath` are modified by the local functions. - double currentDistance = 0; + double currentTime = 0; Vector2 lastPosition = new Vector2(vertices[0].X, 0); sliderPath.ControlPoints.Clear(); @@ -237,10 +236,10 @@ namespace osu.Game.Rulesets.Catch.Objects sliderPath.ControlPoints[^1].Type = PathType.Linear; float deltaX = vertices[i].X - lastPosition.X; - double length = vertices[i].Distance - currentDistance; + double length = (vertices[i].Time - currentTime) * velocity; // Should satisfy `deltaX^2 + deltaY^2 = length^2`. - // By invariants, the expression inside the `sqrt` is (almost) non-negative. + // The expression inside the `sqrt` is (almost) non-negative if the slider velocity is large enough. double deltaY = Math.Sqrt(Math.Max(0, length * length - (double)deltaX * deltaX)); // When `deltaY` is small, one segment is always enough. @@ -280,59 +279,38 @@ namespace osu.Game.Rulesets.Catch.Objects { Vector2 nextPosition = new Vector2(nextX, nextY); sliderPath.ControlPoints.Add(new PathControlPoint(nextPosition)); - currentDistance += Vector2.Distance(lastPosition, nextPosition); + currentTime += Vector2.Distance(lastPosition, nextPosition) / velocity; lastPosition = nextPosition; } } /// - /// Find the index at which a new vertex with can be inserted. + /// Find the index at which a new vertex with can be inserted. /// - private int vertexIndexAtDistance(double distance) + private int vertexIndexAtTime(double time) { - // The position of `(distance, Infinity)` is uniquely determined because infinite positions are not allowed. - int i = vertices.BinarySearch(new JuiceStreamPathVertex(distance, float.PositiveInfinity)); + // The position of `(time, Infinity)` is uniquely determined because infinite positions are not allowed. + int i = vertices.BinarySearch(new JuiceStreamPathVertex(time, float.PositiveInfinity)); return i < 0 ? ~i : i; } /// - /// Compute the position at the given , assuming is the vertex index returned by . + /// Compute the position at the given , assuming is the vertex index returned by . /// - private float positionAtDistance(double distance, int index) + private float positionAtTime(double time, int index) { if (index <= 0) return vertices[0].X; if (index >= vertices.Count) return vertices[^1].X; - double length = vertices[index].Distance - vertices[index - 1].Distance; - if (Precision.AlmostEquals(length, 0)) + double duration = vertices[index].Time - vertices[index - 1].Time; + if (Precision.AlmostEquals(duration, 0)) return vertices[index].X; float deltaX = vertices[index].X - vertices[index - 1].X; - return (float)(vertices[index - 1].X + deltaX * ((distance - vertices[index - 1].Distance) / length)); - } - - /// - /// Check the two vertices can connected directly while satisfying the slope condition. - /// - private bool canConnect(JuiceStreamPathVertex vertex1, JuiceStreamPathVertex vertex2, float allowance = 0) - { - double xDistance = Math.Abs((double)vertex2.X - vertex1.X); - float length = (float)Math.Abs(vertex2.Distance - vertex1.Distance); - return xDistance <= length + allowance; - } - - /// - /// Move the position of towards the position of - /// until the vertex pair satisfies the condition . - /// - /// The resulting position of . - private float clampToConnectablePosition(JuiceStreamPathVertex fixedVertex, JuiceStreamPathVertex movableVertex) - { - float length = (float)Math.Abs(movableVertex.Distance - fixedVertex.Distance); - return Math.Clamp(movableVertex.X, fixedVertex.X - length, fixedVertex.X + length); + return (float)(vertices[index - 1].X + deltaX * ((time - vertices[index - 1].Time) / duration)); } private void invalidate() => InvalidationID++; diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs index 58c50603c4..afef2e637f 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs @@ -12,22 +12,22 @@ namespace osu.Game.Rulesets.Catch.Objects /// public readonly struct JuiceStreamPathVertex : IComparable { - public readonly double Distance; + public readonly double Time; public readonly float X; - public JuiceStreamPathVertex(double distance, float x) + public JuiceStreamPathVertex(double time, float x) { - Distance = distance; + Time = time; X = x; } public int CompareTo(JuiceStreamPathVertex other) { - int c = Distance.CompareTo(other.Distance); + int c = Time.CompareTo(other.Time); return c != 0 ? c : X.CompareTo(other.X); } - public override string ToString() => $"({Distance}, {X})"; + public override string ToString() => $"({Time}, {X})"; } } From 9ffa90602bc675ed0a37eac66845b34f36948f8f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 21:17:57 +0900 Subject: [PATCH 0921/1276] Automatically set slider velocity from juice stream path --- .../Blueprints/Components/EditablePath.cs | 22 +++++++++++++++++-- .../Objects/JuiceStreamPath.cs | 5 +++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index 3004d3644c..20e303dad1 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -92,13 +92,31 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components public void UpdateHitObjectFromPath(JuiceStream hitObject) { - path.ConvertToSliderPath(hitObject.Path, hitObject.LegacyConvertedY, hitObject.Velocity); + // The SV setting may need to be changed for the current path. + var svBindable = hitObject.DifficultyControlPoint.SliderVelocityBindable; + double svToVelocityFactor = hitObject.Velocity / svBindable.Value; + double requiredVelocity = path.ComputeRequiredVelocity(); + + // The value is pre-rounded here because setting it to the bindable will rounded to the nearest value + // but it should be always rounded up to satisfy the required minimum velocity condition. + // + // This is rounded to integers instead of using the precision of the bindable + // because it results in a smaller number of non-redundant control points. + // + // The value is clamped here by the bindable min and max values. + // In case the required velocity is too large, the path is not preserved. + svBindable.Value = Math.Ceiling(requiredVelocity / svToVelocityFactor); + + // Calculate the velocity using the resulting SV because `hitObject.Velocity` is not recomputed yet. + double velocity = svBindable.Value * svToVelocityFactor; + + path.ConvertToSliderPath(hitObject.Path, hitObject.LegacyConvertedY, velocity); if (beatSnapProvider == null) return; double endTime = hitObject.StartTime + path.Duration; double snappedEndTime = beatSnapProvider.SnapTime(endTime, hitObject.StartTime); - hitObject.Path.ExpectedDistance.Value = (snappedEndTime - hitObject.StartTime) * hitObject.Velocity; + hitObject.Path.ExpectedDistance.Value = (snappedEndTime - hitObject.StartTime) * velocity; } public Vector2 ToRelativePosition(Vector2 screenSpacePosition) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs index d8cea3945c..61f4c580ae 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs @@ -207,6 +207,11 @@ namespace osu.Game.Rulesets.Catch.Objects { double xDifference = Math.Abs((double)vertices[i].X - vertices[i - 1].X); double timeDifference = vertices[i].Time - vertices[i - 1].Time; + + // A short segment won't affect the resulting path much anyways so ignore it to avoid divide-by-zero. + if (Precision.AlmostEquals(timeDifference, 0)) + continue; + maximumSlope = Math.Max(maximumSlope, xDifference / timeDifference); } From 0e98bb28bda2c7dcedba4d4637493e61c3c28d8e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 21:21:51 +0900 Subject: [PATCH 0922/1276] Fix wrong resampling times are used for juice stream path --- .../Edit/Blueprints/Components/EditablePath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index 20e303dad1..652d083c4e 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { path.ResampleVertices(hitObject.NestedHitObjects .Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used. - .Select(h => (h.StartTime - hitObject.StartTime) * hitObject.Velocity)); + .Select(h => h.StartTime - hitObject.StartTime)); } vertexStates.Clear(); From 670922c8e563c202b6d7e396450f64ce77ecb78d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 21:32:05 +0900 Subject: [PATCH 0923/1276] Use latest slider velocity for juice stream velocity computation. This fixes one-frame glitch in editor when slider velocity is changed. --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 282afb6343..d34452cdbb 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -27,10 +27,16 @@ namespace osu.Game.Rulesets.Catch.Objects public int RepeatCount { get; set; } [JsonIgnore] - public double Velocity { get; private set; } + private double velocityFactor; [JsonIgnore] - public double TickDistance { get; private set; } + private double tickDistanceFactor; + + [JsonIgnore] + public double Velocity => velocityFactor * DifficultyControlPoint.SliderVelocity; + + [JsonIgnore] + public double TickDistance => tickDistanceFactor * DifficultyControlPoint.SliderVelocity; /// /// The length of one span of this . @@ -43,10 +49,8 @@ namespace osu.Game.Rulesets.Catch.Objects TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity; - - Velocity = scoringDistance / timingPoint.BeatLength; - TickDistance = scoringDistance / difficulty.SliderTickRate; + velocityFactor = base_scoring_distance * difficulty.SliderMultiplier / timingPoint.BeatLength; + tickDistanceFactor = base_scoring_distance * difficulty.SliderMultiplier / difficulty.SliderTickRate; } protected override void CreateNestedHitObjects(CancellationToken cancellationToken) From 37c9aac49f836bb49f57c0ea246998dcf6815144 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 21:35:06 +0900 Subject: [PATCH 0924/1276] Make `ScrollingPath` use time instead of distance. This is consistent as other components now use time instead of distance. --- .../Edit/Blueprints/Components/ScrollingPath.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs index 109bf61ea5..cfaca2f9a4 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { private readonly Path drawablePath; - private readonly List<(double Distance, float X)> vertices = new List<(double, float)>(); + private readonly List<(double Time, float X)> vertices = new List<(double, float)>(); public ScrollingPath() { @@ -35,16 +35,16 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components public void UpdatePathFrom(ScrollingHitObjectContainer hitObjectContainer, JuiceStream hitObject) { - double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity); + double timeToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1); - computeDistanceXs(hitObject); + computeTimeXs(hitObject); drawablePath.Vertices = vertices - .Select(v => new Vector2(v.X, (float)(v.Distance * distanceToYFactor))) + .Select(v => new Vector2(v.X, (float)(v.Time * timeToYFactor))) .ToArray(); drawablePath.OriginPosition = drawablePath.PositionInBoundingBox(Vector2.Zero); } - private void computeDistanceXs(JuiceStream hitObject) + private void computeTimeXs(JuiceStream hitObject) { vertices.Clear(); @@ -54,17 +54,17 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components if (sliderVertices.Count == 0) return; - double distance = 0; + double time = 0; Vector2 lastPosition = Vector2.Zero; for (int repeat = 0; repeat < hitObject.RepeatCount + 1; repeat++) { foreach (var position in sliderVertices) { - distance += Vector2.Distance(lastPosition, position); + time += Vector2.Distance(lastPosition, position) / hitObject.Velocity; lastPosition = position; - vertices.Add((distance, position.X)); + vertices.Add((time, position.X)); } sliderVertices.Reverse(); From 7daa3d8eb79b59756fad96810012c01e3a167dfe Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 21:54:14 +0900 Subject: [PATCH 0925/1276] Remove now-redundant velocity calculation Velocity is computed from the up-to-date SV now. --- .../Edit/Blueprints/Components/EditablePath.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index 652d083c4e..e038562b4b 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -107,16 +107,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components // In case the required velocity is too large, the path is not preserved. svBindable.Value = Math.Ceiling(requiredVelocity / svToVelocityFactor); - // Calculate the velocity using the resulting SV because `hitObject.Velocity` is not recomputed yet. - double velocity = svBindable.Value * svToVelocityFactor; - - path.ConvertToSliderPath(hitObject.Path, hitObject.LegacyConvertedY, velocity); + path.ConvertToSliderPath(hitObject.Path, hitObject.LegacyConvertedY, hitObject.Velocity); if (beatSnapProvider == null) return; double endTime = hitObject.StartTime + path.Duration; double snappedEndTime = beatSnapProvider.SnapTime(endTime, hitObject.StartTime); - hitObject.Path.ExpectedDistance.Value = (snappedEndTime - hitObject.StartTime) * velocity; + hitObject.Path.ExpectedDistance.Value = (snappedEndTime - hitObject.StartTime) * hitObject.Velocity; } public Vector2 ToRelativePosition(Vector2 screenSpacePosition) From d8a4f9d37ddb42a2e0a73f51e5112edab432964a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 22:33:16 +0900 Subject: [PATCH 0926/1276] Update juice stream blueprint tests No "clipping" occur anymore for vertex positions. Instead, clipping may occur when the path is converted to a slider. Add tests for automatic slider velocity change. --- .../TestSceneJuiceStreamPlacementBlueprint.cs | 29 +++++------------ .../TestSceneJuiceStreamSelectionBlueprint.cs | 31 +++++-------------- 2 files changed, 15 insertions(+), 45 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs index 981efc9a13..b1adc4901c 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor { public class TestSceneJuiceStreamPlacementBlueprint : CatchPlacementBlueprintTestScene { - private const double velocity = 0.5; + private const double velocity_factor = 0.5; private JuiceStream lastObject => LastObject?.HitObject as JuiceStream; @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor { var playable = base.GetPlayableBeatmap(); playable.Difficulty.SliderTickRate = 5; - playable.Difficulty.SliderMultiplier = velocity * 10; + playable.Difficulty.SliderMultiplier = velocity_factor * 10; return playable; } @@ -43,6 +43,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1])); AddAssert("start position is correct", () => Precision.AlmostEquals(lastObject.OriginalX, positions[0])); AddAssert("end position is correct", () => Precision.AlmostEquals(lastObject.EndX, positions[1])); + AddAssert("default slider velocity", () => lastObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault); } [Test] @@ -66,28 +67,21 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor } [Test] - public void TestVelocityLimit() + public void TestSliderVelocityChange() { double[] times = { 100, 300 }; float[] positions = { 200, 500 }; addPlacementSteps(times, positions); - addPathCheckStep(times, new float[] { 200, 300 }); - } + addPathCheckStep(times, positions); - [Test] - public void TestPreviousVerticesAreFixed() - { - double[] times = { 100, 300, 500, 700 }; - float[] positions = { 200, 400, 100, 500 }; - addPlacementSteps(times, positions); - addPathCheckStep(times, new float[] { 200, 300, 200, 300 }); + AddAssert("slider velocity changed", () => !lastObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault); } [Test] public void TestClampedPositionIsRestored() { double[] times = { 100, 300, 500 }; - float[] positions = { 200, 200, 0, 250 }; + float[] positions = { 200, 200, -3000, 250 }; addMoveAndClickSteps(times[0], positions[0]); addMoveAndClickSteps(times[1], positions[1]); @@ -97,15 +91,6 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor addPathCheckStep(times, new float[] { 200, 200, 250 }); } - [Test] - public void TestFirstVertexIsFixed() - { - double[] times = { 100, 200 }; - float[] positions = { 100, 300 }; - addPlacementSteps(times, positions); - addPathCheckStep(times, new float[] { 100, 150 }); - } - [Test] public void TestOutOfOrder() { diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 123316f461..22a839d847 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -101,31 +101,16 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor } [Test] - public void TestClampedPositionIsRestored() + public void TestSliderVelocityChange() { - const double velocity = 0.25; - double[] times = { 100, 500, 700 }; - float[] positions = { 100, 100, 100 }; - addBlueprintStep(times, positions, velocity); + double[] times = { 100, 300 }; + float[] positions = { 200, 300 }; + addBlueprintStep(times, positions); + AddAssert("default slider velocity", () => hitObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault); addDragStartStep(times[1], positions[1]); - - AddMouseMoveStep(times[1], 200); - addVertexCheckStep(3, 1, times[1], 200); - addVertexCheckStep(3, 2, times[2], 150); - - AddMouseMoveStep(times[1], 100); - addVertexCheckStep(3, 1, times[1], 100); - // Stored position is restored. - addVertexCheckStep(3, 2, times[2], positions[2]); - - AddMouseMoveStep(times[1], 300); - addDragEndStep(); - addDragStartStep(times[1], 300); - - AddMouseMoveStep(times[1], 100); - // Position is different because a changed position is committed when the previous drag is ended. - addVertexCheckStep(3, 2, times[2], 250); + AddMouseMoveStep(times[1], 400); + AddAssert("slider velocity changed", () => !hitObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault); } [Test] @@ -174,7 +159,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor addAddVertexSteps(500, 150); addVertexCheckStep(3, 1, 500, 150); - addAddVertexSteps(90, 220); + addAddVertexSteps(90, 200); addVertexCheckStep(4, 1, times[0], positions[0]); addAddVertexSteps(750, 180); From 6bdcf893b7d6c960d8154c9908b78f8c92a6c273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 8 May 2022 15:13:29 +0200 Subject: [PATCH 0927/1276] Move alpha management closer to screen level Felt bad messing with alpha at the column level. --- osu.Game/Overlays/Mods/ModColumn.cs | 1 - osu.Game/Overlays/Mods/ModSelectScreen.cs | 13 ++++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 89f472a290..3a2fda0bb0 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -350,7 +350,6 @@ namespace osu.Game.Overlays.Mods } allFiltered.Value = panelFlow.All(panel => panel.Filtered.Value); - Alpha = allFiltered.Value ? 0 : 1; if (toggleAllCheckbox != null && !SelectionAnimationRunning) { diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 6a3df3fb05..a2e73b9575 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -580,17 +580,20 @@ namespace osu.Game.Overlays.Mods protected override void LoadComplete() { base.LoadComplete(); - Active.BindValueChanged(_ => updateDim(), true); + Active.BindValueChanged(_ => updateState()); + Column.AllFiltered.BindValueChanged(_ => updateState(), true); FinishTransforms(); } protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate || Column.SelectionAnimationRunning; - private void updateDim() + private void updateState() { Colour4 targetColour; - if (Active.Value) + Column.Alpha = Column.AllFiltered.Value ? 0 : 1; + + if (Column.Active.Value) targetColour = Colour4.White; else targetColour = IsHovered ? colours.GrayC : colours.Gray8; @@ -609,14 +612,14 @@ namespace osu.Game.Overlays.Mods protected override bool OnHover(HoverEvent e) { base.OnHover(e); - updateDim(); + updateState(); return Active.Value; } protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - updateDim(); + updateState(); } } From d964b4f23c47df79e7c27acdcb7056b7b8774210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 8 May 2022 15:22:32 +0200 Subject: [PATCH 0928/1276] Fix uneven spacing when some mod columns are hidden --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index a2e73b9575..8b19e38954 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -129,7 +129,6 @@ namespace osu.Game.Overlays.Mods Shear = new Vector2(SHEAR, 0), RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, - Spacing = new Vector2(10, 0), Margin = new MarginPadding { Horizontal = 70 }, Children = new[] { @@ -237,12 +236,21 @@ namespace osu.Game.Overlays.Mods } private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null) - => new ColumnDimContainer(CreateModColumn(modType, toggleKeys).With(column => column.Filter = IsValidMod)) + { + 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 }; + }); + + return new ColumnDimContainer(column) { AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, - RequestScroll = column => columnScroll.ScrollIntoView(column, extraScroll: 140), + RequestScroll = col => columnScroll.ScrollIntoView(col, extraScroll: 140), }; + } private ShearedButton[] createDefaultFooterButtons() => new[] From 483a611c411ea64e24ca8e3a63fe58cb53cc99d3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 8 May 2022 23:10:51 +0900 Subject: [PATCH 0929/1276] Fix `BeginPlacement` location. --- .../Edit/Blueprints/BananaShowerPlacementBlueprint.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs index 4613bfd36e..44cfea7779 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs @@ -18,13 +18,18 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints InternalChild = outline = new TimeSpanOutline(); } + protected override void LoadComplete() + { + base.LoadComplete(); + + BeginPlacement(); + } + protected override void Update() { base.Update(); outline.UpdateFrom(HitObjectContainer, HitObject); - - BeginPlacement(); } protected override bool OnMouseDown(MouseDownEvent e) From 158f1342608ff147b27ee48ef52652bded7632d2 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 9 May 2022 00:01:05 +0900 Subject: [PATCH 0930/1276] Fix duration is negative while placing banana shower in catch editor. Timeline blueprint is glitched when the hit object has negative duration. Negative duration is unwanted anyways so placement implementation is fixed instead of supporting it in timline blueprint. --- .../TestSceneBananaShowerPlacementBlueprint.cs | 3 +++ .../BananaShowerPlacementBlueprint.cs | 18 +++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs index cca3701a60..fec253924f 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs @@ -55,7 +55,10 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor AddMoveStep(end_time, 0); AddClickStep(MouseButton.Left); + AddMoveStep(start_time, 0); + AddAssert("duration is positive", () => ((BananaShower)CurrentBlueprint.HitObject).Duration > 0); + AddClickStep(MouseButton.Right); AddAssert("start time is correct", () => Precision.AlmostEquals(LastObject.HitObject.StartTime, start_time)); AddAssert("end time is correct", () => Precision.AlmostEquals(LastObject.HitObject.GetEndTime(), end_time)); diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs index 6dea8b0712..c3a5306b3f 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Input.Events; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; @@ -13,6 +14,9 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { private readonly TimeSpanOutline outline; + private double placementStartTime; + private double placementEndTime; + public BananaShowerPlacementBlueprint() { InternalChild = outline = new TimeSpanOutline(); @@ -38,13 +42,6 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints case PlacementState.Active: if (e.Button != MouseButton.Right) break; - // If the duration is negative, swap the start and the end time to make the duration positive. - if (HitObject.Duration < 0) - { - HitObject.StartTime = HitObject.EndTime; - HitObject.Duration = -HitObject.Duration; - } - EndPlacement(HitObject.Duration > 0); return true; } @@ -61,13 +58,16 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints switch (PlacementActive) { case PlacementState.Waiting: - HitObject.StartTime = time; + placementStartTime = placementEndTime = time; break; case PlacementState.Active: - HitObject.EndTime = time; + placementEndTime = time; break; } + + HitObject.StartTime = Math.Min(placementStartTime, placementEndTime); + HitObject.EndTime = Math.Max(placementStartTime, placementEndTime); } } } From 172d14bcc6675e042b7852845c6122222530c4c5 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 8 May 2022 11:40:14 -0700 Subject: [PATCH 0931/1276] Improve code quality of mod column flow container --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 29 ++++------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index fc06af3f9d..b232acf11b 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -12,7 +12,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; -using osu.Framework.Layout; using osu.Framework.Lists; using osu.Framework.Utils; using osu.Game.Audio; @@ -125,12 +124,15 @@ namespace osu.Game.Overlays.Mods ScrollbarOverlapsContent = false, Child = columnFlow = new ColumnFlowContainer { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, Direction = FillDirection.Horizontal, Shear = new Vector2(SHEAR, 0), RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Spacing = new Vector2(10, 0), Margin = new MarginPadding { Horizontal = 70 }, + Padding = new MarginPadding { Bottom = 10 }, Children = new[] { createModColumnContent(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), @@ -490,19 +492,12 @@ namespace osu.Game.Overlays.Mods } /// - /// Manages padding and layout of mod columns. + /// Manages layout of mod columns. /// internal class ColumnFlowContainer : FillFlowContainer { public IEnumerable Columns => Children.Select(dimWrapper => dimWrapper.Column); - private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize); - - public ColumnFlowContainer() - { - AddLayout(drawSizeLayout); - } - public override void Add(ColumnDimContainer dimContainer) { base.Add(dimContainer); @@ -510,22 +505,6 @@ namespace osu.Game.Overlays.Mods Debug.Assert(dimContainer != null); dimContainer.Column.Shear = Vector2.Zero; } - - protected override void Update() - { - base.Update(); - - if (!drawSizeLayout.IsValid) - { - Padding = new MarginPadding - { - Left = DrawHeight * SHEAR, - Bottom = 10 - }; - - drawSizeLayout.Validate(); - } - } } /// From bc839be4d85653fc0872c7e9d923129072eeedff Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 4 May 2022 09:26:58 -0700 Subject: [PATCH 0932/1276] Add failing rapid back button exit test --- .../Navigation/TestSceneScreenNavigation.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index f8eee7be56..9674ef7ae1 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Leaderboards; using osu.Game.Overlays; @@ -503,6 +504,22 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("test dispose doesn't crash", () => Game.Dispose()); } + [Test] + public void TestRapidBackButtonExit() + { + AddStep("set hold delay to 0", () => Game.LocalConfig.SetValue(OsuSetting.UIHoldActivationDelay, 0.0)); + + AddStep("press escape twice rapidly", () => + { + InputManager.Key(Key.Escape); + InputManager.Key(Key.Escape); + }); + + pushEscape(); + + AddAssert("exit dialog is shown", () => Game.Dependencies.Get().CurrentDialog != null); + } + private Func playToResults() { Player player = null; From 21e1f4546a7dc35eb056748899dd423c6273cf0e Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 4 May 2022 09:27:53 -0700 Subject: [PATCH 0933/1276] Fix popup dialog potentially not clicking last button when dismissed --- osu.Game/Overlays/Dialog/PopupDialog.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index d08b6b7beb..5959fe656c 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -100,10 +100,6 @@ namespace osu.Game.Overlays.Dialog } } - // We always want dialogs to show their appear animation, so we request they start hidden. - // Normally this would not be required, but is here due to the manual Show() call that occurs before LoadComplete(). - protected override bool StartHidden => true; - protected PopupDialog() { RelativeSizeAxes = Axes.Both; @@ -272,7 +268,7 @@ namespace osu.Game.Overlays.Dialog protected override void PopOut() { - if (!actionInvoked && content.IsPresent) + if (!actionInvoked) // In the case a user did not choose an action before a hide was triggered, press the last button. // This is presumed to always be a sane default "cancel" action. buttonsContainer.Last().TriggerClick(); From 5f80e344c3f1bcf4953dedd6092d18c1d7d366eb Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 9 May 2022 00:15:08 +0100 Subject: [PATCH 0934/1276] Update day separator date text to right align against username --- osu.Game/Overlays/ChatOverlayV2.cs | 45 +++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 0702b3ad97..8c832baa38 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -349,26 +349,51 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, ColumnDimensions = new[] { - new Dimension(GridSizeMode.Absolute, 215), - new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 200), + new Dimension(GridSizeMode.Absolute, 15), new Dimension(), }, Content = new[] { new[] { - Drawable.Empty(), - new OsuSpriteText + new GridContainer { - Text = time.ToLocalTime().ToString("dd MMMM yyyy").ToUpper(), - Font = OsuFont.Torus.With(size: 15, weight: FontWeight.SemiBold), - Colour = colourProvider.Content1, - Margin = new MarginPadding { Right = 10 }, + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 15), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new[] + { + new Circle + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Colour = colourProvider.Background5, + RelativeSizeAxes = Axes.X, + Height = 2, + }, + Drawable.Empty(), + new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Text = time.ToLocalTime().ToString("dd MMMM yyyy").ToUpper(), + Font = OsuFont.Torus.With(size: 15, weight: FontWeight.SemiBold), + Colour = colourProvider.Content1, + }, + }, + }, }, - new Box + Drawable.Empty(), + new Circle { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, From c221058f0f6cb269ed1a9746a6f999a1027c6740 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 9 May 2022 00:26:00 +0100 Subject: [PATCH 0935/1276] Simplify fetching `DrawableChannel` in `ChatOverlayV2` test --- osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 9d894314f8..259f5540b7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -365,7 +365,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click listing", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); - AddStep("Click drawable channel", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); + AddStep("Click drawable channel", () => clickDrawable(currentDrawableChannel)); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click channel list", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); @@ -381,8 +381,8 @@ namespace osu.Game.Tests.Visual.Online private Visibility loadingVisibility => chatOverlay.ChildrenOfType().Single().State.Value; - private ChatOverlayDrawableChannel currentDrawableChannel => - chatOverlay.ChildrenOfType>().Single().Child; + private DrawableChannel currentDrawableChannel => + chatOverlay.ChildrenOfType().Single(); private ChannelListItem getChannelListItem(Channel channel) => chatOverlay.ChildrenOfType().Single(item => item.Channel == channel); From aeb29849897521108bfc8d94a416272ec2bd69fa Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 9 May 2022 00:29:40 +0100 Subject: [PATCH 0936/1276] Move `ChatOverlayDrawableChannel` to it's own file --- .../Chat/ChatOverlayDrawableChannel.cs | 109 ++++++++++++++++++ osu.Game/Overlays/ChatOverlayV2.cs | 96 --------------- 2 files changed, 109 insertions(+), 96 deletions(-) create mode 100644 osu.Game/Overlays/Chat/ChatOverlayDrawableChannel.cs diff --git a/osu.Game/Overlays/Chat/ChatOverlayDrawableChannel.cs b/osu.Game/Overlays/Chat/ChatOverlayDrawableChannel.cs new file mode 100644 index 0000000000..793d81fe26 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChatOverlayDrawableChannel.cs @@ -0,0 +1,109 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat +{ + public class ChatOverlayDrawableChannel : DrawableChannel + { + public ChatOverlayDrawableChannel(Channel channel) + : base(channel) + { + } + + [BackgroundDependencyLoader] + private void load() + { + ChatLineFlow.Padding = new MarginPadding(0); + } + + protected override Drawable CreateDaySeparator(DateTimeOffset time) => new ChatOverlayDaySeparator(time); + + private class ChatOverlayDaySeparator : Container + { + private readonly DateTimeOffset time; + + public ChatOverlayDaySeparator(DateTimeOffset time) + { + this.time = time; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding(15); + Child = new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 200), + new Dimension(GridSizeMode.Absolute, 15), + new Dimension(), + }, + Content = new[] + { + new[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 15), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new[] + { + new Circle + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Colour = colourProvider.Background5, + RelativeSizeAxes = Axes.X, + Height = 2, + }, + Drawable.Empty(), + new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Text = time.ToLocalTime().ToString("dd MMMM yyyy").ToUpper(), + Font = OsuFont.Torus.With(size: 15, weight: FontWeight.SemiBold), + Colour = colourProvider.Content1, + }, + }, + }, + }, + Drawable.Empty(), + new Circle + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = colourProvider.Background5, + RelativeSizeAxes = Axes.X, + Height = 2, + }, + }, + }, + }; + } + } + } +} diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 8c832baa38..071f64ce29 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -3,7 +3,6 @@ #nullable enable -using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; @@ -17,9 +16,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Configuration; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Online.Chat; @@ -314,97 +311,4 @@ namespace osu.Game.Overlays channelManager.PostMessage(message); } } - - public class ChatOverlayDrawableChannel : DrawableChannel - { - public ChatOverlayDrawableChannel(Channel channel) - : base(channel) - { - } - - [BackgroundDependencyLoader] - private void load() - { - ChatLineFlow.Padding = new MarginPadding(0); - } - - protected override Drawable CreateDaySeparator(DateTimeOffset time) => new ChatOverlayDaySeparator(time); - - private class ChatOverlayDaySeparator : Container - { - private readonly DateTimeOffset time; - - public ChatOverlayDaySeparator(DateTimeOffset time) - { - this.time = time; - } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Padding = new MarginPadding(15); - Child = new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.Absolute, 200), - new Dimension(GridSizeMode.Absolute, 15), - new Dimension(), - }, - Content = new[] - { - new[] - { - new GridContainer - { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 15), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new[] - { - new Circle - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Colour = colourProvider.Background5, - RelativeSizeAxes = Axes.X, - Height = 2, - }, - Drawable.Empty(), - new OsuSpriteText - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Text = time.ToLocalTime().ToString("dd MMMM yyyy").ToUpper(), - Font = OsuFont.Torus.With(size: 15, weight: FontWeight.SemiBold), - Colour = colourProvider.Content1, - }, - }, - }, - }, - Drawable.Empty(), - new Circle - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Colour = colourProvider.Background5, - RelativeSizeAxes = Axes.X, - Height = 2, - }, - }, - }, - }; - } - } - } } From 623e2c94ae69cf5049c1c6001863319ca6029ef2 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 9 May 2022 00:32:22 +0100 Subject: [PATCH 0937/1276] Adjust padding --- osu.Game/Overlays/Chat/ChatOverlayDrawableChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChatOverlayDrawableChannel.cs b/osu.Game/Overlays/Chat/ChatOverlayDrawableChannel.cs index 793d81fe26..3b47adc4b7 100644 --- a/osu.Game/Overlays/Chat/ChatOverlayDrawableChannel.cs +++ b/osu.Game/Overlays/Chat/ChatOverlayDrawableChannel.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.Chat { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Padding = new MarginPadding(15); + Padding = new MarginPadding { Horizontal = 15, Vertical = 20 }; Child = new GridContainer { RelativeSizeAxes = Axes.X, From 3b4fdf20f9c5bb510ab660373c2a8ec498e18321 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 9 May 2022 12:15:54 +0900 Subject: [PATCH 0938/1276] Prevent throwing exceptions on first run without internet --- osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs index 46f5b418bd..fc39887e79 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs @@ -153,7 +153,17 @@ namespace osu.Game.Beatmaps } }; - Task.Run(() => cacheDownloadRequest.PerformAsync()); + Task.Run(async () => + { + try + { + await cacheDownloadRequest.PerformAsync(); + } + catch + { + // Prevent throwing unobserved exceptions, as they will be logged from the network request to the log file anyway. + } + }); } private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmapInfo) From e4521b1fff489cc92164d8d5de8c16cb628bd675 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 10:16:57 +0300 Subject: [PATCH 0939/1276] Revert scale locking changes for now --- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 87e49fcc5e..70a1b69bcf 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Ranking { new Drawable[] { - Content = new DrawSizePreservingFillContainer + Content = new Container { RelativeSizeAxes = Axes.Both, Child = new Container From 3407a299ef30657d040736d71c7e84687adf927f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 10:35:12 +0300 Subject: [PATCH 0940/1276] Revert "Revert scale locking changes for now" This reverts commit e4521b1fff489cc92164d8d5de8c16cb628bd675. --- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 70a1b69bcf..87e49fcc5e 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Ranking { new Drawable[] { - Content = new Container + Content = new DrawSizePreservingFillContainer { RelativeSizeAxes = Axes.Both, Child = new Container From 422531d8ec5cb7240b4948c9e426663982e2a973 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 10:35:13 +0300 Subject: [PATCH 0941/1276] Revert "Remove no longer required constant" This reverts commit 6f4cdccf6c5430991c8e4c87a6ad60698faff4ce. --- osu.Game/Screens/Ranking/ResultsScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 87e49fcc5e..2c91b17917 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -27,6 +27,7 @@ namespace osu.Game.Screens.Ranking public abstract class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler { protected const float BACKGROUND_BLUR = 20; + private static readonly float screen_height = 768 - TwoLayerButton.SIZE_EXTENDED.Y; public override bool DisallowExternalBeatmapRulesetChanges => true; From 2f3ac61b479bae1e4ecef58ffef024809ac1f5b4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 10:35:14 +0300 Subject: [PATCH 0942/1276] Revert "Replace vertical scrolling in results screen with size-preserving container" This reverts commit 298c2a1828bf628d59a5bd7ec1b671e925732ec9. --- .../MultiplayerTeamResultsScreen.cs | 6 ++--- osu.Game/Screens/Ranking/ResultsScreen.cs | 26 +++++++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs index 117415ac8f..3f0f3e043c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs @@ -46,9 +46,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { const float winner_background_half_height = 250; - Content.Anchor = Content.Origin = Anchor.TopCentre; - Content.Scale = new Vector2(0.9f); - Content.Y = 75; + VerticalScrollContent.Anchor = VerticalScrollContent.Origin = Anchor.TopCentre; + VerticalScrollContent.Scale = new Vector2(0.9f); + VerticalScrollContent.Y = 75; var redScore = teamScores.First().Value; var blueScore = teamScores.Last().Value; diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 2c91b17917..98514cd846 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -14,6 +14,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Online.API; @@ -40,7 +41,7 @@ namespace osu.Game.Screens.Ranking protected ScorePanelList ScorePanelList { get; private set; } - protected Container Content { get; private set; } + protected VerticalScrollContainer VerticalScrollContent { get; private set; } [Resolved(CanBeNull = true)] private Player player { get; set; } @@ -78,9 +79,10 @@ namespace osu.Game.Screens.Ranking { new Drawable[] { - Content = new DrawSizePreservingFillContainer + VerticalScrollContent = new VerticalScrollContainer { RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, Child = new Container { RelativeSizeAxes = Axes.Both, @@ -344,5 +346,25 @@ namespace osu.Game.Screens.Ranking public void OnReleased(KeyBindingReleaseEvent e) { } + + protected class VerticalScrollContainer : OsuScrollContainer + { + protected override Container Content => content; + + private readonly Container content; + + public VerticalScrollContainer() + { + Masking = false; + + base.Content.Add(content = new Container { RelativeSizeAxes = Axes.X }); + } + + protected override void Update() + { + base.Update(); + content.Height = Math.Max(screen_height, DrawHeight); + } + } } } From 72552ecc855a1a30483dd80d4b9c9c41726ac7c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 May 2022 16:49:20 +0900 Subject: [PATCH 0943/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 2866ec24a6..97d9dbc380 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fa7563da55..2f32c843c0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index f987ae9bf8..b483267696 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From a16f2349aa6c9b2bf4246e1fc970dcebf5ada2e4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 9 May 2022 17:55:40 +0900 Subject: [PATCH 0944/1276] Fix next queued item not selecting after gameplay --- .../TestSceneMultiplayerMatchSubScreen.cs | 37 +++++++++++++++++++ .../OnlinePlay/DrawableRoomPlaylistItem.cs | 8 ++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 2abde82e92..6173580f0b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -21,6 +21,7 @@ using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.UI; +using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; @@ -176,5 +177,41 @@ namespace osu.Game.Tests.Visual.Multiplayer .ChildrenOfType() .SingleOrDefault(panel => !panel.Filtered.Value)?.Mod is OsuModDoubleTime); } + + [Test] + public void TestNextPlaylistItemSelectedAfterCompletion() + { + AddStep("add two playlist items", () => + { + SelectedRoom.Value.Playlist.AddRange(new[] + { + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID + }, + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID + } + }); + }); + + ClickButtonWhenEnabled(); + + AddUntilStep("wait for join", () => RoomJoined); + + ClickButtonWhenEnabled(); + ClickButtonWhenEnabled(); + + AddStep("change user to loaded", () => MultiplayerClient.ChangeState(MultiplayerUserState.Loaded)); + AddUntilStep("user playing", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Playing); + AddStep("abort gameplay", () => MultiplayerClient.AbortGameplay()); + + AddUntilStep("last playlist item selected", () => + { + var lastItem = this.ChildrenOfType().Single(p => p.Item.ID == MultiplayerClient.APIRoom?.Playlist.Last().ID); + return lastItem.IsSelectedItem; + }); + } } } diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 459b861d96..2618e15d31 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -65,6 +65,8 @@ namespace osu.Game.Screens.OnlinePlay public readonly PlaylistItem Item; + public bool IsSelectedItem => SelectedItem.Value?.ID == Item.ID; + private readonly DelayedLoadWrapper onScreenLoader = new DelayedLoadWrapper(Empty) { RelativeSizeAxes = Axes.Both }; private readonly IBindable valid = new Bindable(); @@ -128,12 +130,10 @@ namespace osu.Game.Screens.OnlinePlay SelectedItem.BindValueChanged(selected => { - bool isCurrent = selected.NewValue == Model; - if (!valid.Value) { // Don't allow selection when not valid. - if (isCurrent) + if (IsSelectedItem) { SelectedItem.Value = selected.OldValue; } @@ -142,7 +142,7 @@ namespace osu.Game.Screens.OnlinePlay return; } - maskingContainer.BorderThickness = isCurrent ? 5 : 0; + maskingContainer.BorderThickness = IsSelectedItem ? 5 : 0; }, true); valid.BindValueChanged(_ => Scheduler.AddOnce(refresh)); From 0fe121f48a8954d25fef2b8059fea1f60d967549 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 13:25:22 +0300 Subject: [PATCH 0945/1276] Move and rename settings toolbox group test scene --- .../TestSceneSettingsToolboxGroup.cs} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename osu.Game.Tests/Visual/{Gameplay/TestSceneReplaySettingsOverlay.cs => UserInterface/TestSceneSettingsToolboxGroup.cs} (85%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs similarity index 85% rename from osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs index f8fab784cc..5ca30d07b0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; @@ -8,12 +8,12 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.PlayerSettings; -namespace osu.Game.Tests.Visual.Gameplay +namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneReplaySettingsOverlay : OsuTestScene + public class TestSceneSettingsToolboxGroup : OsuTestScene { - public TestSceneReplaySettingsOverlay() + public TestSceneSettingsToolboxGroup() { ExampleContainer container; From deda1c83e67779a748d82138215846519dbcfea7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 13:44:18 +0300 Subject: [PATCH 0946/1276] Add failing test case --- .../TestSceneSettingsToolboxGroup.cs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs index 5ca30d07b0..76657afbcd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs @@ -1,17 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.PlayerSettings; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneSettingsToolboxGroup : OsuTestScene + public class TestSceneSettingsToolboxGroup : OsuManualInputManagerTestScene { public TestSceneSettingsToolboxGroup() { @@ -46,6 +50,22 @@ namespace osu.Game.Tests.Visual.UserInterface })); } + [Test] + public void TestClickExpandButtonMultipleTimes() + { + SettingsToolboxGroup group = null; + + AddAssert("group expanded by default", () => (group = this.ChildrenOfType().First()).Expanded.Value); + AddStep("click expand button multiple times", () => + { + InputManager.MoveMouseTo(group.ChildrenOfType().Single()); + Scheduler.AddDelayed(() => InputManager.Click(MouseButton.Left), 100); + Scheduler.AddDelayed(() => InputManager.Click(MouseButton.Left), 200); + Scheduler.AddDelayed(() => InputManager.Click(MouseButton.Left), 300); + }); + AddAssert("group contracted", () => !group.Expanded.Value); + } + private class ExampleContainer : PlayerSettingsGroup { public ExampleContainer() From 702c6ae658d671a21985f28ec199b3bc18d7f5bd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 13:49:31 +0300 Subject: [PATCH 0947/1276] Fix `SettingsToolboxGroup` not clearing transforms before updating autosize --- osu.Game/Overlays/SettingsToolboxGroup.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 808d4fc422..36d4f03e02 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -156,6 +156,8 @@ namespace osu.Game.Overlays private void updateExpandedState(ValueChangedEvent expanded) { + content.ClearTransforms(); + if (expanded.NewValue) content.AutoSizeAxes = Axes.Y; else From 70a90722e8dac2147feaffb764064a65ae5d031c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 May 2022 19:48:53 +0900 Subject: [PATCH 0948/1276] Fix dropdown item hover colour not being set in time Turns out to be an osu!-side issue. The colour transform was being shortcutted for the non-displayed case, which meant it was not in a good state in time for the first hover. Closes https://github.com/ppy/osu/issues/18163#issuecomment-1120747301 --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index b1d4691938..4e391c8221 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -184,14 +184,12 @@ namespace osu.Game.Graphics.UserInterface protected override void UpdateBackgroundColour() { - if (!IsPreSelected && !IsSelected) - { - Background.FadeOut(600, Easing.OutQuint); - return; - } - - Background.FadeIn(100, Easing.OutQuint); Background.FadeColour(IsPreSelected ? BackgroundColourHover : BackgroundColourSelected, 100, Easing.OutQuint); + + if (IsPreSelected || IsSelected) + Background.FadeIn(100, Easing.OutQuint); + else + Background.FadeOut(600, Easing.OutQuint); } protected override void UpdateForegroundColour() From 5f3bea846b0e2fe0c435629c9ee056e6b86d3742 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 9 May 2022 11:15:12 +0100 Subject: [PATCH 0949/1276] Simplify fetching of `DrawableChannel` --- osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 7c77ac925e..259f5540b7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -365,7 +365,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click listing", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); - AddStep("Click drawable channel", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); + AddStep("Click drawable channel", () => clickDrawable(currentDrawableChannel)); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click channel list", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); @@ -382,7 +382,7 @@ namespace osu.Game.Tests.Visual.Online chatOverlay.ChildrenOfType().Single().State.Value; private DrawableChannel currentDrawableChannel => - chatOverlay.ChildrenOfType>().Single().Child; + chatOverlay.ChildrenOfType().Single(); private ChannelListItem getChannelListItem(Channel channel) => chatOverlay.ChildrenOfType().Single(item => item.Channel == channel); From 9ec8b609a8395e1f814604b2c1f1c097f7a90d5b Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 9 May 2022 12:00:49 +0100 Subject: [PATCH 0950/1276] Update channel visibility tests to be more correct --- .../Visual/Online/TestSceneChatOverlayV2.cs | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 259f5540b7..9956b8977f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -121,6 +121,19 @@ namespace osu.Game.Tests.Visual.Online }); } + [Test] + public void TestBasic() + { + AddStep("Show overlay with channel", () => + { + chatOverlay.Show(); + Channel joinedChannel = channelManager.JoinChannel(testChannel1); + channelManager.CurrentChannel.Value = joinedChannel; + }); + AddAssert("Overlay is visible", () => chatOverlay.State.Value == Visibility.Visible); + AddAssert("Channel is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + } + [Test] public void TestShowHide() { @@ -158,20 +171,17 @@ namespace osu.Game.Tests.Visual.Online public void TestChannelSelection() { AddStep("Show overlay", () => chatOverlay.Show()); - AddAssert("Listing is visible", () => listingVisibility == Visibility.Visible); + AddAssert("Listing is visible", () => listingIsVisible); AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); - AddAssert("Listing is hidden", () => listingVisibility == Visibility.Hidden); - AddAssert("Loading is hidden", () => loadingVisibility == Visibility.Hidden); - AddAssert("Current channel is correct", () => channelManager.CurrentChannel.Value == testChannel1); - AddAssert("DrawableChannel is correct", () => currentDrawableChannel.Channel == testChannel1); + AddAssert("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); } [Test] public void TestSearchInListing() { AddStep("Show overlay", () => chatOverlay.Show()); - AddAssert("Listing is visible", () => listingVisibility == Visibility.Visible); + AddAssert("Listing is visible", () => listingIsVisible); AddStep("Search for 'number 2'", () => chatOverlayTextBox.Text = "number 2"); AddUntilStep("Only channel 2 visibile", () => { @@ -263,6 +273,7 @@ namespace osu.Game.Tests.Visual.Online }); }); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); + AddAssert("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); } [Test] @@ -285,8 +296,7 @@ namespace osu.Game.Tests.Visual.Online }); }); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel2)); - AddAssert("Channel 2 is selected", () => channelManager.CurrentChannel.Value == testChannel2); - AddAssert("Channel 2 is visible", () => currentDrawableChannel.Channel == testChannel2); + AddAssert("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); } [Test] @@ -310,8 +320,7 @@ namespace osu.Game.Tests.Visual.Online }); AddStep("Leave channel 2", () => channelManager.LeaveChannel(testChannel2)); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel2)); - AddAssert("Channel 2 is selected", () => channelManager.CurrentChannel.Value == testChannel2); - AddAssert("Channel 2 is visible", () => currentDrawableChannel.Channel == testChannel2); + AddAssert("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); } [Test] @@ -331,6 +340,7 @@ namespace osu.Game.Tests.Visual.Online }); }); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); + AddAssert("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); } [Test] @@ -351,6 +361,7 @@ namespace osu.Game.Tests.Visual.Online }); AddStep("Set null channel", () => channelManager.CurrentChannel.Value = null); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); + AddAssert("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); } [Test] @@ -375,11 +386,14 @@ namespace osu.Game.Tests.Visual.Online AddAssert("TextBox is not focused", () => InputManager.FocusedDrawable == null); } - private Visibility listingVisibility => - chatOverlay.ChildrenOfType().Single().State.Value; + private bool listingIsVisible => + chatOverlay.ChildrenOfType().Single().State.Value == Visibility.Visible; - private Visibility loadingVisibility => - chatOverlay.ChildrenOfType().Single().State.Value; + private bool loadingIsVisible => + chatOverlay.ChildrenOfType().Single().State.Value == Visibility.Visible; + + private bool channelIsVisible => + !listingIsVisible && !loadingIsVisible; private DrawableChannel currentDrawableChannel => chatOverlay.ChildrenOfType().Single(); From d4cc2bd7dd813733c927b69999ef8bcb801450cc Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 9 May 2022 12:01:30 +0100 Subject: [PATCH 0951/1276] Ensure channel selector is dismissed when the current channel is changed --- osu.Game/Overlays/ChatOverlayV2.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 4c7fa0f802..48b34726f8 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -191,8 +191,6 @@ namespace osu.Game.Overlays channelManager.CurrentChannel.Value = channel; } - selectorActive.Value = false; - channel.HighlightedMessage.Value = message; Show(); @@ -268,6 +266,8 @@ namespace osu.Game.Overlays return; } + selectorActive.Value = false; + LoadComponentAsync(new DrawableChannel(newChannel), loaded => { currentChannelContainer.Clear(); From ceb6276d2ffd3e0e9b900225ea28bd12efdca146 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 18:19:29 +0300 Subject: [PATCH 0952/1276] Add failing test case --- .../UserInterface/TestSceneRoundedButton.cs | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs index 9ccfba7c74..ef3c0c7fa4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs @@ -2,11 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; +using osuTK; namespace osu.Game.Tests.Visual.UserInterface { @@ -22,15 +27,10 @@ namespace osu.Game.Tests.Visual.UserInterface RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Colour4.DarkGray - }, button = new RoundedButton { Width = 400, - Text = "Test button", + Text = "Test Button", Anchor = Anchor.Centre, Origin = Anchor.Centre, Action = () => { } @@ -40,5 +40,39 @@ namespace osu.Game.Tests.Visual.UserInterface AddToggleStep("toggle disabled", disabled => button.Action = disabled ? (Action)null : () => { }); } + + [Test] + public void TestOverlay() + { + IEnumerable schemes = Enum.GetValues(typeof(OverlayColourScheme)).Cast(); + + AddStep("create buttons", () => + { + Child = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5f), + ChildrenEnumerable = schemes.Select(c => new DependencyProvidingContainer + { + AutoSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] { (typeof(OverlayColourProvider), new OverlayColourProvider(c)) }, + Child = new RoundedButton + { + Width = 400, + Text = $"Test {c}", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = () => { }, + } + }), + }; + }); + + AddAssert("first button has correct colour", () => this.ChildrenOfType().First().BackgroundColour == new OverlayColourProvider(schemes.First()).Highlight1); + AddToggleStep("toggle disabled", disabled => this.ChildrenOfType().ForEach(b => b.Action = disabled ? (Action)null : () => { })); + } } } From 172524ff8a3812ffce89361c5552a6426529df33 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 18:21:00 +0300 Subject: [PATCH 0953/1276] Move default background colour specification to `OsuButton` --- osu.Game/Graphics/UserInterface/OsuButton.cs | 8 +++++--- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 11 ----------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 29a797bd78..6fa63e5874 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -12,6 +13,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface @@ -86,11 +88,11 @@ namespace osu.Game.Graphics.UserInterface AddInternal(new HoverClickSounds(hoverSounds.Value)); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + [BackgroundDependencyLoader(permitNulls: true)] + private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) { if (backgroundColour == null) - BackgroundColour = colours.BlueDark; + BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; } protected override void LoadComplete() diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index f535a32b39..b1529774d9 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -2,13 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using JetBrains.Annotations; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -26,13 +22,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } - [BackgroundDependencyLoader(true)] - private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) - { - if (BackgroundColour == Color4.White) - BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; - } - protected override void LoadComplete() { base.LoadComplete(); From ace25af949ab39946e4a8b57103b40247c96dbfa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 20:55:26 +0300 Subject: [PATCH 0954/1276] Revert "Move default background colour specification to `OsuButton`" This reverts commit 172524ff8a3812ffce89361c5552a6426529df33. --- osu.Game/Graphics/UserInterface/OsuButton.cs | 8 +++----- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 11 +++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 6fa63e5874..29a797bd78 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -13,7 +12,6 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface @@ -88,11 +86,11 @@ namespace osu.Game.Graphics.UserInterface AddInternal(new HoverClickSounds(hoverSounds.Value)); } - [BackgroundDependencyLoader(permitNulls: true)] - private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) + [BackgroundDependencyLoader] + private void load(OsuColour colours) { if (backgroundColour == null) - BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; + BackgroundColour = colours.BlueDark; } protected override void LoadComplete() diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index b1529774d9..f535a32b39 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -2,9 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -22,6 +26,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) + { + if (BackgroundColour == Color4.White) + BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; + } + protected override void LoadComplete() { base.LoadComplete(); From 1fcfeac05f8c499ae86c36d32bcfdaf0bfc736f4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 18:52:09 +0300 Subject: [PATCH 0955/1276] Fix `RoundedButton` not using its default background colour --- osu.Game/Graphics/UserInterface/OsuButton.cs | 31 ++++++++++++++----- .../Graphics/UserInterfaceV2/RoundedButton.cs | 4 +-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 29a797bd78..08514d94c3 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -33,9 +32,12 @@ namespace osu.Game.Graphics.UserInterface private Color4? backgroundColour; + /// + /// Sets a custom background colour to this button, replacing the provided default. + /// public Color4 BackgroundColour { - get => backgroundColour ?? Color4.White; + get => backgroundColour ?? defaultBackgroundColour; set { backgroundColour = value; @@ -43,6 +45,23 @@ namespace osu.Game.Graphics.UserInterface } } + private Color4 defaultBackgroundColour; + + /// + /// Sets a default background colour to this button. + /// + protected Color4 DefaultBackgroundColour + { + get => defaultBackgroundColour; + set + { + defaultBackgroundColour = value; + + if (backgroundColour == null) + Background.FadeColour(value); + } + } + protected override Container Content { get; } protected Box Hover; @@ -89,8 +108,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - if (backgroundColour == null) - BackgroundColour = colours.BlueDark; + DefaultBackgroundColour = colours.BlueDark; } protected override void LoadComplete() @@ -106,10 +124,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnClick(ClickEvent e) { if (Enabled.Value) - { - Debug.Assert(backgroundColour != null); - Background.FlashColour(backgroundColour.Value.Lighten(0.4f), 200); - } + Background.FlashColour(BackgroundColour.Lighten(0.4f), 200); return base.OnClick(e); } diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index f535a32b39..ec56b6d784 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -29,8 +28,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 [BackgroundDependencyLoader(true)] private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) { - if (BackgroundColour == Color4.White) - BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; + DefaultBackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; } protected override void LoadComplete() From 5726cf660f7ec219804fb0ebb6315ab5af5017ed Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 21:19:34 +0300 Subject: [PATCH 0956/1276] Improve test coverage to use existing `ThemeComparisonTestScene` --- .../UserInterface/TestSceneRoundedButton.cs | 74 +++++-------------- 1 file changed, 18 insertions(+), 56 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs index ef3c0c7fa4..f45c55d912 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs @@ -1,78 +1,40 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; -using osuTK; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneRoundedButton : OsuTestScene + public class TestSceneRoundedButton : ThemeComparisonTestScene { - [Test] - public void TestBasic() + private readonly BindableBool enabled = new BindableBool(true); + + protected override Drawable CreateContent() => new RoundedButton { - RoundedButton button = null; + Width = 400, + Text = "Test button", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Enabled = { BindTarget = enabled }, + }; - AddStep("create button", () => Child = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - button = new RoundedButton - { - Width = 400, - Text = "Test Button", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = () => { } - } - } - }); - - AddToggleStep("toggle disabled", disabled => button.Action = disabled ? (Action)null : () => { }); + [Test] + public void TestDisabled() + { + AddToggleStep("toggle disabled", disabled => enabled.Value = !disabled); } [Test] - public void TestOverlay() + public void TestBackgroundColour() { - IEnumerable schemes = Enum.GetValues(typeof(OverlayColourScheme)).Cast(); - - AddStep("create buttons", () => - { - Child = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(5f), - ChildrenEnumerable = schemes.Select(c => new DependencyProvidingContainer - { - AutoSizeAxes = Axes.Both, - CachedDependencies = new (Type, object)[] { (typeof(OverlayColourProvider), new OverlayColourProvider(c)) }, - Child = new RoundedButton - { - Width = 400, - Text = $"Test {c}", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = () => { }, - } - }), - }; - }); - - AddAssert("first button has correct colour", () => this.ChildrenOfType().First().BackgroundColour == new OverlayColourProvider(schemes.First()).Highlight1); - AddToggleStep("toggle disabled", disabled => this.ChildrenOfType().ForEach(b => b.Action = disabled ? (Action)null : () => { })); + AddStep("set red scheme", () => CreateThemedContent(OverlayColourScheme.Red)); + AddAssert("first button has correct colour", () => Cell(0, 1).ChildrenOfType().First().BackgroundColour == new OverlayColourProvider(OverlayColourScheme.Red).Highlight1); } } } From ebb64d1f1ab7fb2b398ba11803690f0e83d56651 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 9 May 2022 20:15:43 +0100 Subject: [PATCH 0957/1276] Use `AddUntilStep` to wait for channel to load and become visible --- .../Visual/Online/TestSceneChatOverlayV2.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 9956b8977f..bf1767cc96 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -131,7 +131,7 @@ namespace osu.Game.Tests.Visual.Online channelManager.CurrentChannel.Value = joinedChannel; }); AddAssert("Overlay is visible", () => chatOverlay.State.Value == Visibility.Visible); - AddAssert("Channel is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + AddUntilStep("Channel is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); } [Test] @@ -174,7 +174,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Listing is visible", () => listingIsVisible); AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); - AddAssert("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); } [Test] @@ -273,7 +273,7 @@ namespace osu.Game.Tests.Visual.Online }); }); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); - AddAssert("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); } [Test] @@ -296,7 +296,7 @@ namespace osu.Game.Tests.Visual.Online }); }); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel2)); - AddAssert("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); + AddUntilStep("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); } [Test] @@ -320,7 +320,7 @@ namespace osu.Game.Tests.Visual.Online }); AddStep("Leave channel 2", () => channelManager.LeaveChannel(testChannel2)); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel2)); - AddAssert("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); + AddUntilStep("Channel 2 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); } [Test] @@ -340,7 +340,7 @@ namespace osu.Game.Tests.Visual.Online }); }); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); - AddAssert("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); } [Test] @@ -361,7 +361,7 @@ namespace osu.Game.Tests.Visual.Online }); AddStep("Set null channel", () => channelManager.CurrentChannel.Value = null); AddStep("Highlight message", () => chatOverlay.HighlightMessage(message, testChannel1)); - AddAssert("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); } [Test] From 38e463d31da24d4721efc22e0541505831268c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 9 May 2022 22:16:04 +0200 Subject: [PATCH 0958/1276] Add failing test case for invalid mod adjustment management --- .../UserInterface/TestSceneModSelectScreen.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index fa7758df59..661465b484 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -481,6 +481,38 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("3 columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 3); } + [Test] + public void TestCorrectAudioAdjustmentDeapplication() + { + createScreen(); + changeRuleset(0); + + AddStep("allow track adjustments", () => MusicController.AllowTrackAdjustments = true); + + AddStep("set wind up", () => modSelectScreen.SelectedMods.Value = new[] { new ModWindUp() }); + AddStep("open customisation menu", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + AddAssert("frequency above 1", () => MusicController.CurrentTrack.AggregateFrequency.Value > 1); + AddAssert("tempo is 1", () => MusicController.CurrentTrack.AggregateTempo.Value == 1); + + AddStep("turn off pitch adjustment", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + AddAssert("frequency is 1", () => MusicController.CurrentTrack.AggregateFrequency.Value == 1); + AddAssert("tempo above 1", () => MusicController.CurrentTrack.AggregateTempo.Value > 1); + + AddStep("reset mods", () => modSelectScreen.SelectedMods.SetDefault()); + AddAssert("frequency is 1", () => MusicController.CurrentTrack.AggregateFrequency.Value == 1); + AddAssert("tempo is 1", () => MusicController.CurrentTrack.AggregateTempo.Value == 1); + + AddStep("disallow track adjustments", () => MusicController.AllowTrackAdjustments = false); + } + private void waitForColumnLoad() => AddUntilStep("all column content loaded", () => modSelectScreen.ChildrenOfType().Any() && modSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); From 96ea4ee7b33136a4fb563520c730c971eaf63026 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 23:49:05 +0300 Subject: [PATCH 0959/1276] Add explanatory comment --- osu.Game/Overlays/SettingsToolboxGroup.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 36d4f03e02..077762c0d0 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -156,6 +156,8 @@ namespace osu.Game.Overlays private void updateExpandedState(ValueChangedEvent expanded) { + // clearing transforms is necessary to avoid a previous height transform + // potentially continuing to get processed while content has changed to autosize. content.ClearTransforms(); if (expanded.NewValue) From beb86a7f7c909f27ba0f8fa217338e32a067bac4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 23:57:08 +0300 Subject: [PATCH 0960/1276] Remove usage of player settings in `TestSceneSettingsToolboxGroup` --- .../TestSceneSettingsToolboxGroup.cs | 73 ++++++++----------- 1 file changed, 29 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs index 76657afbcd..8ef24e58a0 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs @@ -4,12 +4,11 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; -using osu.Game.Screens.Play.HUD; -using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Overlays.Settings; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface @@ -17,45 +16,39 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneSettingsToolboxGroup : OsuManualInputManagerTestScene { - public TestSceneSettingsToolboxGroup() + private SettingsToolboxGroup group; + + [SetUp] + public void SetUp() => Schedule(() => { - ExampleContainer container; - - Add(new PlayerSettingsOverlay + Child = group = new SettingsToolboxGroup("example") { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - State = { Value = Visibility.Visible } - }); - - Add(container = new ExampleContainer()); - - AddStep(@"Add button", () => container.Add(new TriangleButton - { - RelativeSizeAxes = Axes.X, - Text = @"Button", - })); - - AddStep(@"Add checkbox", () => container.Add(new PlayerCheckbox - { - LabelText = "Checkbox", - })); - - AddStep(@"Add textbox", () => container.Add(new FocusedTextBox - { - RelativeSizeAxes = Axes.X, - Height = 30, - PlaceholderText = "Textbox", - HoldFocus = false, - })); - } + Children = new Drawable[] + { + new RoundedButton + { + RelativeSizeAxes = Axes.X, + Text = @"Button", + Enabled = { Value = true }, + }, + new OsuCheckbox + { + LabelText = @"Checkbox", + }, + new OutlinedTextBox + { + RelativeSizeAxes = Axes.X, + Height = 30, + PlaceholderText = @"Textbox", + } + }, + }; + }); [Test] public void TestClickExpandButtonMultipleTimes() { - SettingsToolboxGroup group = null; - - AddAssert("group expanded by default", () => (group = this.ChildrenOfType().First()).Expanded.Value); + AddAssert("group expanded by default", () => group.Expanded.Value); AddStep("click expand button multiple times", () => { InputManager.MoveMouseTo(group.ChildrenOfType().Single()); @@ -65,13 +58,5 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddAssert("group contracted", () => !group.Expanded.Value); } - - private class ExampleContainer : PlayerSettingsGroup - { - public ExampleContainer() - : base("example") - { - } - } } } From 2481201a734ef248e2f99f58734f136389b93393 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 9 May 2022 22:58:46 +0100 Subject: [PATCH 0961/1276] Fix selector dismissal behaviour during user actions --- osu.Game/Overlays/ChatOverlayV2.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 48b34726f8..e59bee7977 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -165,7 +165,12 @@ namespace osu.Game.Overlays }; channelList.OnRequestLeave += channel => channelManager.LeaveChannel(channel); - channelListing.OnRequestJoin += channel => channelManager.JoinChannel(channel); + channelListing.OnRequestJoin += channel => + { + channelManager.JoinChannel(channel); + // Manually joining a channel should keep the selector open + selectorActive.Value = true; + }; channelListing.OnRequestLeave += channel => channelManager.LeaveChannel(channel); textBar.OnSearchTermsChanged += searchTerms => channelListing.SearchTerm = searchTerms; @@ -255,6 +260,10 @@ namespace osu.Game.Overlays // Channel is null when leaving the currently selected channel if (newChannel == null) { + // Don't need to autoswitch if the selector is visible + if (selectorActive.Value) + return; + // Find another channel to switch to newChannel = channelManager.JoinedChannels.FirstOrDefault(c => c != channel.OldValue); From 2b7eeadac0fbf52419050fbd8e4fe69ff93de893 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 10 May 2022 14:48:41 +0900 Subject: [PATCH 0962/1276] Workaround bad performance when selecting all freemods --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 16975cfd1d..1c629b7a4f 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -192,7 +192,8 @@ namespace osu.Game.Overlays.Mods State.BindValueChanged(_ => samplePlaybackDisabled.Value = State.Value == Visibility.Hidden, true); - ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); + if (customisationButton != null) + ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); SelectedMods.BindValueChanged(val => { From 8ccf2ee0759e58e14d8544c32297e410a8c5fb5e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 10 May 2022 15:07:08 +0900 Subject: [PATCH 0963/1276] Add inline comment --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 1c629b7a4f..912c09f05c 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -192,6 +192,8 @@ namespace osu.Game.Overlays.Mods State.BindValueChanged(_ => samplePlaybackDisabled.Value = State.Value == Visibility.Hidden, true); + // This is an optimisation to prevent refreshing the available settings controls when it can be + // reasonably assumed that the settings panel is never to be displayed (e.g. FreeModSelectScreen). if (customisationButton != null) ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); From bcd91ac743885c1bce1df4bf6a84de925004b73d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 10:02:32 +0300 Subject: [PATCH 0964/1276] Move exception soft-handling logic to `OsuGameBase` --- osu.Desktop/Program.cs | 22 ---------------------- osu.Game/OsuGameBase.cs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index eb9045d9ce..405f0a8006 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -4,8 +4,6 @@ using System; using System.IO; using System.Runtime.Versioning; -using System.Threading; -using System.Threading.Tasks; using osu.Desktop.LegacyIpc; using osu.Framework; using osu.Framework.Development; @@ -63,8 +61,6 @@ namespace osu.Desktop using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { BindIPC = true })) { - host.ExceptionThrown += handleException; - if (!host.IsPrimaryInstance) { if (args.Length > 0 && args[0].Contains('.')) // easy way to check for a file import in args @@ -131,23 +127,5 @@ namespace osu.Desktop // tools.SetProcessAppUserModelId(); }); } - - private static int allowableExceptions = DebugUtils.IsDebugBuild ? 0 : 1; - - /// - /// Allow a maximum of one unhandled exception, per second of execution. - /// - /// - private static bool handleException(Exception arg) - { - bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0; - - Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} ."); - - // restore the stock of allowable exceptions after a short delay. - Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions)); - - return continueExecution; - } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 324fcada89..c5b69a3637 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; @@ -180,9 +181,16 @@ namespace osu.Game /// protected DatabaseContextFactory EFContextFactory { get; private set; } + /// + /// Number of exceptions to allow before aborting execution. + /// + protected virtual int SoftHandledExceptions => 0; + public OsuGameBase() { Name = @"osu!"; + + allowableExceptions = SoftHandledExceptions; } [BackgroundDependencyLoader] @@ -408,6 +416,8 @@ namespace osu.Game LocalConfig ??= UseDevelopmentServer ? new DevelopmentOsuConfigManager(Storage) : new OsuConfigManager(Storage); + + host.ExceptionThrown += onExceptionThrown; } /// @@ -505,6 +515,23 @@ namespace osu.Game AvailableMods.Value = dict; } + private int allowableExceptions; + + /// + /// Allows a maximum of one unhandled exception, per second of execution. + /// + private bool onExceptionThrown(Exception _) + { + bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0; + + Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {SoftHandledExceptions} more allowable exceptions" : "denied")} ."); + + // restore the stock of allowable exceptions after a short delay. + Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions)); + + return continueExecution; + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -514,6 +541,9 @@ namespace osu.Game LocalConfig?.Dispose(); realm?.Dispose(); + + if (Host != null) + Host.ExceptionThrown -= onExceptionThrown; } } } From 725f5f4dcb00616e5f7f65ab6595918ad2812a8a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 10:02:41 +0300 Subject: [PATCH 0965/1276] Allow one more exception per second in `OsuGame` and `TournamentGame` --- osu.Game.Tournament/TournamentGame.cs | 3 +++ osu.Game/OsuGame.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 7967f54b49..d9db0e6d20 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; +using osu.Framework.Development; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -39,6 +40,8 @@ namespace osu.Game.Tournament private Bindable windowMode; private LoadingSpinner loadingSpinner; + protected override int SoftHandledExceptions => DebugUtils.IsDebugBuild ? 0 : 1; + [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig, GameHost host) { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 54c4231b06..6961dce910 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -13,6 +13,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Configuration; +using osu.Framework.Development; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -175,6 +176,8 @@ namespace osu.Game private readonly List visibleBlockingOverlays = new List(); + protected override int SoftHandledExceptions => DebugUtils.IsDebugBuild ? 0 : 1; + public OsuGame(string[] args = null) { this.args = args; From dc3c73f72300af57ad18b9671178c0bed2e1206d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:03:03 +0900 Subject: [PATCH 0966/1276] Enable sentry session tracking --- osu.Game/Utils/SentryLogger.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index d9c8199f75..7e0449cec8 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -25,6 +25,8 @@ namespace osu.Game.Utils var options = new SentryOptions { Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2", + AutoSessionTracking = true, + IsEnvironmentUser = false, Release = game.Version }; From 09c21cde8ca15a02ddd29dba669cb200fa9919fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:08:42 +0900 Subject: [PATCH 0967/1276] Add log level translation --- osu.Game/Utils/SentryLogger.cs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 7e0449cec8..c39d718d62 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -50,12 +50,37 @@ namespace osu.Game.Utils if (lastException != null && lastException.Message == exception.Message && exception.StackTrace.StartsWith(lastException.StackTrace, StringComparison.Ordinal)) return; lastException = exception; - sentry.CaptureEvent(new SentryEvent(exception) { Message = entry.Message }, sentryScope); + sentry.CaptureEvent(new SentryEvent(exception) + { + Message = entry.Message, + Level = getSentryLevel(entry.Level), + }, sentryScope); } else sentryScope.AddBreadcrumb(DateTimeOffset.Now, entry.Message, entry.Target.ToString(), "navigation"); } + private SentryLevel? getSentryLevel(LogLevel entryLevel) + { + switch (entryLevel) + { + case LogLevel.Debug: + return SentryLevel.Debug; + + case LogLevel.Verbose: + return SentryLevel.Info; + + case LogLevel.Important: + return SentryLevel.Warning; + + case LogLevel.Error: + return SentryLevel.Error; + + default: + throw new ArgumentOutOfRangeException(nameof(entryLevel), entryLevel, null); + } + } + private bool shouldSubmitException(Exception exception) { switch (exception) From 64cc6ebddbb090edc7a39bb83415b68dc7f25ece Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:12:31 +0900 Subject: [PATCH 0968/1276] Add local user tracking to sentry reporting --- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Utils/SentryLogger.cs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 324fcada89..3f0610e9aa 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -125,7 +125,7 @@ namespace osu.Game protected MusicController MusicController { get; private set; } - protected IAPIProvider API { get; set; } + protected internal IAPIProvider API { get; protected set; } protected Storage Storage { get; set; } diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index c39d718d62..5cecc4d776 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -4,7 +4,10 @@ using System; using System.IO; using System.Net; +using JetBrains.Annotations; +using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Game.Online.API.Requests.Responses; using Sentry; namespace osu.Game.Utils @@ -18,6 +21,9 @@ namespace osu.Game.Utils private Scope sentryScope; private Exception lastException; + [UsedImplicitly] + private readonly IBindable localUser; + public SentryLogger(OsuGame game) { if (!game.IsDeployedBuild) return; @@ -34,6 +40,16 @@ namespace osu.Game.Utils sentryScope = new Scope(options); Logger.NewEntry += processLogEntry; + + localUser = game.API.LocalUser.GetBoundCopy(); + localUser.BindValueChanged(user => + { + sentryScope.User = new User + { + Username = user.NewValue.Username, + Id = user.NewValue.Id.ToString(), + }; + }); } private void processLogEntry(LogEntry entry) @@ -50,6 +66,7 @@ namespace osu.Game.Utils if (lastException != null && lastException.Message == exception.Message && exception.StackTrace.StartsWith(lastException.StackTrace, StringComparison.Ordinal)) return; lastException = exception; + sentry.CaptureEvent(new SentryEvent(exception) { Message = entry.Message, From a5b454edc72537daaaf428bca8b34bf8a9c926b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:21:49 +0900 Subject: [PATCH 0969/1276] Remove unnecessary DI caching of `SentryLogger` --- osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs | 2 -- osu.Game/OsuGame.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index 0f8337deb6..e4871f611e 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -23,7 +23,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Menu; using osu.Game.Skinning; -using osu.Game.Utils; namespace osu.Game.Tests.Visual.Navigation { @@ -33,7 +32,6 @@ namespace osu.Game.Tests.Visual.Navigation private IReadOnlyList requiredGameDependencies => new[] { typeof(OsuGame), - typeof(SentryLogger), typeof(OsuLogo), typeof(IdleTracker), typeof(OnScreenDisplay), diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 54c4231b06..a6c57998b0 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -258,8 +258,6 @@ namespace osu.Game { dependencies.CacheAs(this); - dependencies.Cache(SentryLogger); - dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 }); // bind config int to database RulesetInfo From 3338bffce3809d78d0f00e6b0b2bf332e62c8af7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:25:10 +0900 Subject: [PATCH 0970/1276] Attach user to sentry later in startup flow --- osu.Game/OsuGame.cs | 2 ++ osu.Game/OsuGameBase.cs | 2 +- osu.Game/Utils/SentryLogger.cs | 26 +++++++++++++++----------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a6c57998b0..b8abef38a8 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -258,6 +258,8 @@ namespace osu.Game { dependencies.CacheAs(this); + SentryLogger.AttachUser(API.LocalUser); + dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 }); // bind config int to database RulesetInfo diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 3f0610e9aa..324fcada89 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -125,7 +125,7 @@ namespace osu.Game protected MusicController MusicController { get; private set; } - protected internal IAPIProvider API { get; protected set; } + protected IAPIProvider API { get; set; } protected Storage Storage { get; set; } diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 5cecc4d776..92f2902c0e 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; +using System.Diagnostics; using System.IO; using System.Net; -using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.API.Requests.Responses; @@ -19,10 +21,9 @@ namespace osu.Game.Utils { private SentryClient sentry; private Scope sentryScope; - private Exception lastException; + private Exception? lastException; - [UsedImplicitly] - private readonly IBindable localUser; + private IBindable? localUser; public SentryLogger(OsuGame game) { @@ -40,16 +41,21 @@ namespace osu.Game.Utils sentryScope = new Scope(options); Logger.NewEntry += processLogEntry; + } - localUser = game.API.LocalUser.GetBoundCopy(); - localUser.BindValueChanged(user => + public void AttachUser(IBindable user) + { + Debug.Assert(localUser == null); + + localUser = user.GetBoundCopy(); + localUser.BindValueChanged(u => { sentryScope.User = new User { - Username = user.NewValue.Username, - Id = user.NewValue.Id.ToString(), + Username = u.NewValue.Username, + Id = u.NewValue.Id.ToString(), }; - }); + }, true); } private void processLogEntry(LogEntry entry) @@ -137,8 +143,6 @@ namespace osu.Game.Utils protected virtual void Dispose(bool isDisposing) { Logger.NewEntry -= processLogEntry; - sentry = null; - sentryScope = null; } #endregion From 9734d778f4bda85e2f38cd03e326c09cb3c40ac7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:44:54 +0900 Subject: [PATCH 0971/1276] Update sentry SDK usage in line with more recent specifications --- osu.Game/Utils/SentryLogger.cs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 92f2902c0e..02bcdd281a 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -19,26 +19,24 @@ namespace osu.Game.Utils /// public class SentryLogger : IDisposable { - private SentryClient sentry; - private Scope sentryScope; private Exception? lastException; private IBindable? localUser; + private readonly IDisposable? sentrySession; + public SentryLogger(OsuGame game) { - if (!game.IsDeployedBuild) return; - - var options = new SentryOptions + sentrySession = SentrySdk.Init(options => { - Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2", - AutoSessionTracking = true, - IsEnvironmentUser = false, - Release = game.Version - }; + // Not setting the dsn will completely disable sentry. + if (game.IsDeployedBuild) + options.Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2"; - sentry = new SentryClient(options); - sentryScope = new Scope(options); + options.AutoSessionTracking = true; + options.IsEnvironmentUser = false; + options.Release = game.Version; + }); Logger.NewEntry += processLogEntry; } @@ -50,11 +48,11 @@ namespace osu.Game.Utils localUser = user.GetBoundCopy(); localUser.BindValueChanged(u => { - sentryScope.User = new User + SentrySdk.ConfigureScope(scope => scope.User = new User { Username = u.NewValue.Username, Id = u.NewValue.Id.ToString(), - }; + }); }, true); } @@ -73,14 +71,14 @@ namespace osu.Game.Utils lastException = exception; - sentry.CaptureEvent(new SentryEvent(exception) + SentrySdk.CaptureEvent(new SentryEvent(exception) { Message = entry.Message, Level = getSentryLevel(entry.Level), - }, sentryScope); + }); } else - sentryScope.AddBreadcrumb(DateTimeOffset.Now, entry.Message, entry.Target.ToString(), "navigation"); + SentrySdk.AddBreadcrumb(entry.Message, entry.Target.ToString(), "navigation"); } private SentryLevel? getSentryLevel(LogLevel entryLevel) @@ -143,6 +141,7 @@ namespace osu.Game.Utils protected virtual void Dispose(bool isDisposing) { Logger.NewEntry -= processLogEntry; + sentrySession?.Dispose(); } #endregion From 99e6d56508799132912238edf898df000f33033a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:45:55 +0900 Subject: [PATCH 0972/1276] Add finalizer to sentry logger for safety --- osu.Game/Utils/SentryLogger.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 02bcdd281a..c6429b6b2c 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -41,6 +41,8 @@ namespace osu.Game.Utils Logger.NewEntry += processLogEntry; } + ~SentryLogger() => Dispose(false); + public void AttachUser(IBindable user) { Debug.Assert(localUser == null); From c6112b3ae78becb6980053ae047258c103a4db8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 15:07:02 +0900 Subject: [PATCH 0973/1276] Add unhandled exception marking --- osu.Game/Utils/SentryLogger.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index c6429b6b2c..728d6ab9be 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.API.Requests.Responses; using Sentry; +using Sentry.Protocol; namespace osu.Game.Utils { @@ -73,6 +74,13 @@ namespace osu.Game.Utils lastException = exception; + // framework does some weird exception redirection which means sentry does not see unhandled exceptions using its automatic methods. + // but all unhandled exceptions still arrive via this pathway. we just need to mark them as unhandled for tagging purposes. + // easiest solution is to check the message matches what the framework logs this as. + // see https://github.com/ppy/osu-framework/blob/f932f8df053f0011d755c95ad9a2ed61b94d136b/osu.Framework/Platform/GameHost.cs#L336 + bool wasHandled = entry.Message != @"An unhandled exception has occurred."; + exception.Data[Mechanism.HandledKey] = wasHandled; + SentrySdk.CaptureEvent(new SentryEvent(exception) { Message = entry.Message, From 363643a16d64777ffd0daf681507ec0e53d003c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 15:08:49 +0900 Subject: [PATCH 0974/1276] Remove sentry logger debounce This is probably going to result in a high quantity of exceptions, but I think this is fine. We can add rules as we go to not log certain exception types. --- osu.Game/Utils/SentryLogger.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 728d6ab9be..170d8e7cb0 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -20,8 +20,6 @@ namespace osu.Game.Utils /// public class SentryLogger : IDisposable { - private Exception? lastException; - private IBindable? localUser; private readonly IDisposable? sentrySession; @@ -69,11 +67,6 @@ namespace osu.Game.Utils { if (!shouldSubmitException(exception)) return; - // since we let unhandled exceptions go ignored at times, we want to ensure they don't get submitted on subsequent reports. - if (lastException != null && lastException.Message == exception.Message && exception.StackTrace.StartsWith(lastException.StackTrace, StringComparison.Ordinal)) return; - - lastException = exception; - // framework does some weird exception redirection which means sentry does not see unhandled exceptions using its automatic methods. // but all unhandled exceptions still arrive via this pathway. we just need to mark them as unhandled for tagging purposes. // easiest solution is to check the message matches what the framework logs this as. From 216c68e6d018246a37870fa2ada898f7def2a080 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 15:19:43 +0900 Subject: [PATCH 0975/1276] Add unobserved exception hinting --- osu.Game/Utils/SentryLogger.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 170d8e7cb0..96affd85a7 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -71,8 +71,22 @@ namespace osu.Game.Utils // but all unhandled exceptions still arrive via this pathway. we just need to mark them as unhandled for tagging purposes. // easiest solution is to check the message matches what the framework logs this as. // see https://github.com/ppy/osu-framework/blob/f932f8df053f0011d755c95ad9a2ed61b94d136b/osu.Framework/Platform/GameHost.cs#L336 - bool wasHandled = entry.Message != @"An unhandled exception has occurred."; - exception.Data[Mechanism.HandledKey] = wasHandled; + bool wasUnhandled = entry.Message == @"An unhandled error has occurred."; + bool wasUnobserved = entry.Message == @"An unobserved error has occurred."; + + if (wasUnobserved) + { + // see https://github.com/getsentry/sentry-dotnet/blob/c6a660b1affc894441c63df2695a995701671744/src/Sentry/Integrations/TaskUnobservedTaskExceptionIntegration.cs#L39 + exception.Data[Mechanism.MechanismKey] = @"UnobservedTaskException"; + } + + if (wasUnhandled) + { + // see https://github.com/getsentry/sentry-dotnet/blob/main/src/Sentry/Integrations/AppDomainUnhandledExceptionIntegration.cs#L38-L39 + exception.Data[Mechanism.MechanismKey] = @"AppDomain.UnhandledException"; + } + + exception.Data[Mechanism.HandledKey] = !wasUnhandled; SentrySdk.CaptureEvent(new SentryEvent(exception) { From 6a49eb68759d89e4d001866a7ae4fcbb7addc8d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 16:14:04 +0900 Subject: [PATCH 0976/1276] Add breadcrumb level mappings --- osu.Game/Utils/SentryLogger.cs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 96affd85a7..ad4bcf6274 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -95,10 +95,31 @@ namespace osu.Game.Utils }); } else - SentrySdk.AddBreadcrumb(entry.Message, entry.Target.ToString(), "navigation"); + SentrySdk.AddBreadcrumb(entry.Message, entry.Target.ToString(), "navigation", level: getBreadcrumbLevel(entry.Level)); } - private SentryLevel? getSentryLevel(LogLevel entryLevel) + private BreadcrumbLevel getBreadcrumbLevel(LogLevel entryLevel) + { + switch (entryLevel) + { + case LogLevel.Debug: + return BreadcrumbLevel.Debug; + + case LogLevel.Verbose: + return BreadcrumbLevel.Info; + + case LogLevel.Important: + return BreadcrumbLevel.Warning; + + case LogLevel.Error: + return BreadcrumbLevel.Error; + + default: + throw new ArgumentOutOfRangeException(nameof(entryLevel), entryLevel, null); + } + } + + private SentryLevel getSentryLevel(LogLevel entryLevel) { switch (entryLevel) { From 14a21e92244030a6f1fa758237f9733776b0c6b7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 11:41:41 +0300 Subject: [PATCH 0977/1276] Workaround interface mocks in `TestSceneFirstRunSetupOverlay` breaking with hot reload --- .../TestSceneFirstRunSetupOverlay.cs | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 39298f56ba..905f53c165 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -4,9 +4,11 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using Moq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; @@ -25,9 +27,9 @@ namespace osu.Game.Tests.Visual.UserInterface { private FirstRunSetupOverlay overlay; - private readonly Mock performer = new Mock(); + private readonly Mock performer = new Mock(); - private readonly Mock notificationOverlay = new Mock(); + private readonly Mock notificationOverlay = new Mock(); private Notification lastNotification; @@ -37,8 +39,8 @@ namespace osu.Game.Tests.Visual.UserInterface private void load() { Dependencies.Cache(LocalConfig = new OsuConfigManager(LocalStorage)); - Dependencies.CacheAs(performer.Object); - Dependencies.CacheAs(notificationOverlay.Object); + Dependencies.CacheAs(performer.Object); + Dependencies.CacheAs(notificationOverlay.Object); } [SetUpSteps] @@ -196,5 +198,31 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("overlay shown", () => overlay.State.Value == Visibility.Visible); AddAssert("is resumed", () => overlay.CurrentScreen is ScreenBeatmaps); } + + // interface mocks break hot reload, mocking this stub implementation instead works around it. + // see: https://github.com/moq/moq4/issues/1252 + [UsedImplicitly] + public class TestNotificationOverlay : INotificationOverlay + { + public virtual void Post(Notification notification) + { + } + + public virtual void Hide() + { + } + + public virtual IBindable UnreadCount => null; + } + + // interface mocks break hot reload, mocking this stub implementation instead works around it. + // see: https://github.com/moq/moq4/issues/1252 + [UsedImplicitly] + public class TestPerformerFromScreenRunner : IPerformFromScreenRunner + { + public virtual void PerformFromScreen(Action action, IEnumerable validScreens = null) + { + } + } } } From 9aadc274bfe5578d2dabe2388ee9f3af4928addd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 16:43:38 +0900 Subject: [PATCH 0978/1276] Show first run dialog on first run of the game --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 1 - osu.Game/Overlays/FirstRunSetupOverlay.cs | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 905f53c165..48b5690243 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -74,7 +74,6 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - [Ignore("Enable when first run setup is being displayed on first run.")] public void TestDoesntOpenOnSecondRun() { AddStep("set first run", () => LocalConfig.SetValue(OsuSetting.ShowFirstRunSetup, true)); diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 607bef76dd..df1e094114 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -171,8 +171,7 @@ namespace osu.Game.Overlays config.BindWith(OsuSetting.ShowFirstRunSetup, showFirstRunSetup); - // TODO: uncomment when happy with the whole flow. - // if (showFirstRunSetup.Value) Show(); + if (showFirstRunSetup.Value) Show(); } public override bool OnPressed(KeyBindingPressEvent e) @@ -304,8 +303,7 @@ namespace osu.Game.Overlays } else { - // TODO: uncomment when happy with the whole flow. - // showFirstRunSetup.Value = false; + showFirstRunSetup.Value = false; currentStepIndex = null; Hide(); } From bcce9c5e6772911ff817216c4424ad0ff596e7a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:01:36 +0900 Subject: [PATCH 0979/1276] Limit the width of first run overlay content --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 71 ++++++++++++++--------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index df1e094114..befa80e839 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays typeof(ScreenBehaviour), }; - private Container stackContainer = null!; + private Container screenContent = null!; private Bindable? overlayActivationMode; @@ -86,36 +86,51 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding + Padding = new MarginPadding { Bottom = 20, }, + Child = new GridContainer() { - Horizontal = 70 * 1.2f, - Bottom = 20, - }, - Child = new InputBlockingContainer - { - Masking = true, - CornerRadius = 14, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + ColumnDimensions = new[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourProvider.Background6, - }, - stackContainer = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Vertical = 20, - Horizontal = 70, - }, - } + new Dimension(), + new Dimension(minSize: 640, maxSize: 800), + new Dimension(), }, - }, + Content = new[] + { + new[] + { + Empty(), + new InputBlockingContainer + { + Masking = true, + CornerRadius = 14, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourProvider.Background6, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Vertical = 20, + Horizontal = 20, + }, + Child = screenContent = new Container { RelativeSizeAxes = Axes.Both, }, + }, + }, + }, + Empty(), + }, + } + } }, }); @@ -268,7 +283,7 @@ namespace osu.Game.Overlays { Debug.Assert(currentStepIndex == null); - stackContainer.Child = stack = new ScreenStack + screenContent.Child = stack = new ScreenStack { RelativeSizeAxes = Axes.Both, }; From e5204e565dae9946543ca23c72119be7f26e0540 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:23:12 +0900 Subject: [PATCH 0980/1276] Move horizontal padding to content level to better align scrollbar --- .../FirstRunSetup/FirstRunSetupScreen.cs | 34 +++++++++++-------- osu.Game/Overlays/FirstRunSetupOverlay.cs | 6 +--- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index 1f18d181cb..2990948199 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -33,23 +33,29 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuScrollContainer(Direction.Vertical) { RelativeSizeAxes = Axes.Both, - ScrollbarOverlapsContent = false, - Children = new Drawable[] + Masking = false, + Child = new Container { - new OsuSpriteText + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 30 }, + Children = new Drawable[] { - Text = this.GetLocalisableDescription(), - Font = OsuFont.Default.With(size: header_size), - Colour = OverlayColourProvider.Light1, + new OsuSpriteText + { + Text = this.GetLocalisableDescription(), + Font = OsuFont.Default.With(size: header_size), + Colour = OverlayColourProvider.Light1, + }, + Content = new FillFlowContainer + { + Y = header_size + spacing, + Spacing = new Vector2(spacing), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + } }, - Content = new FillFlowContainer - { - Y = header_size + spacing, - Spacing = new Vector2(spacing), - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - } }, } }; diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index befa80e839..19a4c09473 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -118,11 +118,7 @@ namespace osu.Game.Overlays new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Vertical = 20, - Horizontal = 20, - }, + Padding = new MarginPadding { Vertical = 20 }, Child = screenContent = new Container { RelativeSizeAxes = Axes.Both, }, }, }, From 61313b69ecd40049dbc13b502e21a0e4cbeb312d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:33:37 +0900 Subject: [PATCH 0981/1276] Standardise font sizes in first run overlay screens --- .../Overlays/FirstRunSetup/FirstRunSetupScreen.cs | 9 ++++++--- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 14 +++++++------- osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs | 2 +- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 2 +- osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index 2990948199..2cfa7cd164 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -19,13 +19,16 @@ namespace osu.Game.Overlays.FirstRunSetup protected FillFlowContainer Content { get; private set; } + protected const float CONTENT_FONT_SIZE = 16; + + protected const float HEADER_FONT_SIZE = 24; + [Resolved] protected OverlayColourProvider OverlayColourProvider { get; private set; } [BackgroundDependencyLoader] private void load() { - const float header_size = 40; const float spacing = 20; InternalChildren = new Drawable[] @@ -44,12 +47,12 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuSpriteText { Text = this.GetLocalisableDescription(), - Font = OsuFont.Default.With(size: header_size), + Font = OsuFont.TorusAlternate.With(size: HEADER_FONT_SIZE), Colour = OverlayColourProvider.Light1, }, Content = new FillFlowContainer { - Y = header_size + spacing, + Y = HEADER_FONT_SIZE + spacing, Spacing = new Vector2(spacing), RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 190a0badab..66acdca8c7 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -46,11 +46,11 @@ namespace osu.Game.Overlays.FirstRunSetup [BackgroundDependencyLoader(permitNulls: true)] private void load(LegacyImportManager? legacyImportManager) { - Vector2 buttonSize = new Vector2(500, 60); + Vector2 buttonSize = new Vector2(400, 50); Content.Children = new Drawable[] { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = FirstRunSetupBeatmapScreenStrings.Description, @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.FirstRunSetup Height = 30, Children = new Drawable[] { - currentlyLoadedBeatmaps = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24, weight: FontWeight.SemiBold)) + currentlyLoadedBeatmaps = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: HEADER_FONT_SIZE, weight: FontWeight.SemiBold)) { Colour = OverlayColourProvider.Content2, TextAnchor = Anchor.Centre, @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.FirstRunSetup }, } }, - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = FirstRunSetupBeatmapScreenStrings.TutorialDescription, @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.FirstRunSetup Text = FirstRunSetupBeatmapScreenStrings.TutorialButton, Action = downloadTutorial }, - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = FirstRunSetupBeatmapScreenStrings.BundledDescription, @@ -105,7 +105,7 @@ namespace osu.Game.Overlays.FirstRunSetup Text = FirstRunSetupBeatmapScreenStrings.BundledButton, Action = downloadBundled }, - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = "If you have an existing osu! install, you can also choose to import your existing beatmap collection.", @@ -131,7 +131,7 @@ namespace osu.Game.Overlays.FirstRunSetup })); } }, - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = FirstRunSetupBeatmapScreenStrings.ObtainMoreBeatmaps, diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs index dc3d40ad95..9d426bd3fa 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.FirstRunSetup { Content.Children = new Drawable[] { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Text = FirstRunSetupOverlayStrings.BehaviourDescription, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 152d67ab27..24e113e6a9 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.FirstRunSetup { Content.Children = new Drawable[] { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Text = FirstRunSetupOverlayStrings.UIScaleDescription, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs index 10e15a7555..420d630857 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.FirstRunSetup { Content.Children = new Drawable[] { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Text = FirstRunSetupOverlayStrings.WelcomeDescription, RelativeSizeAxes = Axes.X, From 63b9e01d384eb8fda2c5439ab98807b1be7244e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:35:43 +0900 Subject: [PATCH 0982/1276] Fix behaviour screen using old style buttons --- osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs index 9d426bd3fa..1a88e6a842 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs @@ -9,7 +9,7 @@ using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.FirstRunSetup private SearchContainer searchContainer; [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { Content.Children = new Drawable[] { @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.FirstRunSetup { new[] { - new TriangleButton + new RoundedButton { Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, @@ -59,10 +59,11 @@ namespace osu.Game.Overlays.FirstRunSetup Action = applyStandard, }, Empty(), - new DangerousTriangleButton + new RoundedButton { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, + BackgroundColour = colours.Pink3, Text = FirstRunSetupOverlayStrings.ClassicDefaults, RelativeSizeAxes = Axes.X, Action = applyClassic From 493798ae5e5e4ba61e5654b2623eab4a249af750 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:44:23 +0900 Subject: [PATCH 0983/1276] Fix nested ui scale example screens no longer fitting --- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 24e113e6a9..4a44a6d391 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -35,6 +35,8 @@ namespace osu.Game.Overlays.FirstRunSetup [BackgroundDependencyLoader] private void load(OsuConfigManager config) { + const float screen_width = 640; + Content.Children = new Drawable[] { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) @@ -54,7 +56,7 @@ namespace osu.Game.Overlays.FirstRunSetup Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.None, - Size = new Vector2(960, 960 / 16f * 9 / 2), + Size = new Vector2(screen_width, screen_width / 16f * 9 / 2), Children = new Drawable[] { new GridContainer From 25c6226ca9cb7c82ef9a66e4c12f45f13ec7e732 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:47:04 +0900 Subject: [PATCH 0984/1276] Adjust transition length for a more seamless screen change --- osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index 2cfa7cd164..d1ea91e51a 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -68,7 +68,7 @@ namespace osu.Game.Overlays.FirstRunSetup { base.OnEntering(e); this - .FadeInFromZero(500) + .FadeInFromZero(100) .MoveToX(offset) .MoveToX(0, 500, Easing.OutQuint); } @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.FirstRunSetup { base.OnResuming(e); this - .FadeInFromZero(500) + .FadeInFromZero(100) .MoveToX(0, 500, Easing.OutQuint); } From a93c63b2b5b7d0119b3fb5ee97bee8232d593854 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 18:04:10 +0900 Subject: [PATCH 0985/1276] Add loading spinner when loading first run screens --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 19a4c09473..c0eed14c99 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Screens; +using osu.Framework.Threading; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -68,6 +69,9 @@ namespace osu.Game.Overlays private Container content = null!; + private LoadingSpinner loading = null!; + private ScheduledDelegate? loadingShowDelegate; + public FirstRunSetupOverlay() : base(OverlayColourScheme.Purple) { @@ -115,6 +119,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = ColourProvider.Background6, }, + loading = new LoadingSpinner(), new Container { RelativeSizeAxes = Axes.Both, @@ -310,7 +315,16 @@ namespace osu.Game.Overlays if (currentStepIndex < steps.Length) { - stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value])); + var nextScreen = (Screen)Activator.CreateInstance(steps[currentStepIndex.Value]); + + loadingShowDelegate = Scheduler.AddDelayed(() => loading.Show(), 200); + nextScreen.OnLoadComplete += _ => + { + loadingShowDelegate?.Cancel(); + loading.Hide(); + }; + + stack.Push(nextScreen); } else { From a578f7a406a049f8548af177b630c89a6ba32c8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 18:08:17 +0900 Subject: [PATCH 0986/1276] Force nested screens to load synchronously --- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 15 +++++++++------ osu.Game/Screens/OsuScreenStack.cs | 7 +++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 4a44a6d391..8452691bb5 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -125,6 +125,7 @@ namespace osu.Game.Overlays.FirstRunSetup private class SampleScreenContainer : CompositeDrawable { + private readonly OsuScreen screen; // Minimal isolation from main game. [Cached] @@ -144,6 +145,12 @@ namespace osu.Game.Overlays.FirstRunSetup public override bool PropagatePositionalInputSubTree => false; public override bool PropagateNonPositionalInputSubTree => false; + public SampleScreenContainer(OsuScreen screen) + { + this.screen = screen; + RelativeSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] private void load(AudioManager audio, TextureStore textures, RulesetStore rulesets) { @@ -151,13 +158,8 @@ namespace osu.Game.Overlays.FirstRunSetup Beatmap.Value.LoadTrack(); Ruleset.Value = rulesets.AvailableRulesets.First(); - } - public SampleScreenContainer(Screen screen) - { OsuScreenStack stack; - RelativeSizeAxes = Axes.Both; - OsuLogo logo; Padding = new MarginPadding(5); @@ -191,7 +193,8 @@ namespace osu.Game.Overlays.FirstRunSetup }, }; - stack.Push(screen); + // intentionally load synchronously so it is included in the initial load of the first run screen. + stack.PushSynchronously(screen); } } } diff --git a/osu.Game/Screens/OsuScreenStack.cs b/osu.Game/Screens/OsuScreenStack.cs index ebbcbd7650..18b16ba865 100644 --- a/osu.Game/Screens/OsuScreenStack.cs +++ b/osu.Game/Screens/OsuScreenStack.cs @@ -29,6 +29,13 @@ namespace osu.Game.Screens ScreenExited += ScreenChanged; } + public void PushSynchronously(OsuScreen screen) + { + LoadComponent(screen); + + Push(screen); + } + private void screenPushed(IScreen prev, IScreen next) { if (LoadState < LoadState.Ready) From cac6d5569c8dbd1282330ffadf5a2d20bb0d0cb5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 19:07:00 +0900 Subject: [PATCH 0987/1276] Fix incorrect variable reference in log output --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index c5b69a3637..3a35bce05c 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -524,7 +524,7 @@ namespace osu.Game { bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0; - Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {SoftHandledExceptions} more allowable exceptions" : "denied")} ."); + Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} ."); // restore the stock of allowable exceptions after a short delay. Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions)); From 4d22f262667972c4bf20cf1c117d8a7e1e5f2f45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 19:07:07 +0900 Subject: [PATCH 0988/1276] Rename property and improve xmldoc --- osu.Game.Tournament/TournamentGame.cs | 2 +- osu.Game/OsuGame.cs | 2 +- osu.Game/OsuGameBase.cs | 11 ++++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index d9db0e6d20..042e660122 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tournament private Bindable windowMode; private LoadingSpinner loadingSpinner; - protected override int SoftHandledExceptions => DebugUtils.IsDebugBuild ? 0 : 1; + protected override int ExceptionsBeforeCrash => DebugUtils.IsDebugBuild ? 0 : 1; [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig, GameHost host) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 6961dce910..0cfb3c2de8 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -176,7 +176,7 @@ namespace osu.Game private readonly List visibleBlockingOverlays = new List(); - protected override int SoftHandledExceptions => DebugUtils.IsDebugBuild ? 0 : 1; + protected override int ExceptionsBeforeCrash => DebugUtils.IsDebugBuild ? 0 : 1; public OsuGame(string[] args = null) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 3a35bce05c..b17526114a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -182,15 +182,20 @@ namespace osu.Game protected DatabaseContextFactory EFContextFactory { get; private set; } /// - /// Number of exceptions to allow before aborting execution. + /// Number of unhandled exceptions to allow before aborting execution. /// - protected virtual int SoftHandledExceptions => 0; + /// + /// When an unhandled exception is encountered, an internal count will be decremented. + /// If the count hits zero, the game will crash. + /// Each second, the count is incremented until reaching the value specified. + /// + protected virtual int ExceptionsBeforeCrash => 0; public OsuGameBase() { Name = @"osu!"; - allowableExceptions = SoftHandledExceptions; + allowableExceptions = ExceptionsBeforeCrash; } [BackgroundDependencyLoader] From b2a57c34bbea091d2e172c03a05c9481e9312bc7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 19:10:34 +0900 Subject: [PATCH 0989/1276] Move implementation to base --- osu.Game.Tournament/TournamentGame.cs | 3 --- osu.Game/OsuGame.cs | 3 --- osu.Game/OsuGameBase.cs | 4 ++-- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 042e660122..7967f54b49 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; -using osu.Framework.Development; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -40,8 +39,6 @@ namespace osu.Game.Tournament private Bindable windowMode; private LoadingSpinner loadingSpinner; - protected override int ExceptionsBeforeCrash => DebugUtils.IsDebugBuild ? 0 : 1; - [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig, GameHost host) { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0cfb3c2de8..54c4231b06 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -13,7 +13,6 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Configuration; -using osu.Framework.Development; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -176,8 +175,6 @@ namespace osu.Game private readonly List visibleBlockingOverlays = new List(); - protected override int ExceptionsBeforeCrash => DebugUtils.IsDebugBuild ? 0 : 1; - public OsuGame(string[] args = null) { this.args = args; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b17526114a..ce798d4027 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -189,13 +189,13 @@ namespace osu.Game /// If the count hits zero, the game will crash. /// Each second, the count is incremented until reaching the value specified. /// - protected virtual int ExceptionsBeforeCrash => 0; + protected virtual int UnhandledExceptionsBeforeCrash => DebugUtils.IsDebugBuild ? 0 : 1; public OsuGameBase() { Name = @"osu!"; - allowableExceptions = ExceptionsBeforeCrash; + allowableExceptions = UnhandledExceptionsBeforeCrash; } [BackgroundDependencyLoader] From 42fe7082250654233c6c78074a61cfbfec0866fb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 10 May 2022 19:56:21 +0900 Subject: [PATCH 0990/1276] Fix inspection --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index c0eed14c99..cebb2f5e3b 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -91,7 +91,7 @@ namespace osu.Game.Overlays Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Bottom = 20, }, - Child = new GridContainer() + Child = new GridContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 87f6f74795beb592dd50e5994f2f48bca590eac6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 21:03:55 +0900 Subject: [PATCH 0991/1276] Add failing test coverage of adding a file to a detached beatmap across threads --- .../Database/BeatmapImporterTests.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/osu.Game.Tests/Database/BeatmapImporterTests.cs b/osu.Game.Tests/Database/BeatmapImporterTests.cs index f9c13a8169..b7bfe14402 100644 --- a/osu.Game.Tests/Database/BeatmapImporterTests.cs +++ b/osu.Game.Tests/Database/BeatmapImporterTests.cs @@ -136,6 +136,37 @@ namespace osu.Game.Tests.Database }); } + [Test] + public void TestAddFileToAsyncImportedBeatmap() + { + RunTestWithRealm((realm, storage) => + { + BeatmapSetInfo? detachedSet = null; + + using (var importer = new BeatmapModelManager(realm, storage)) + using (new RealmRulesetStore(realm, storage)) + { + Task.Run(async () => + { + Live? beatmapSet; + + using (var reader = new ZipArchiveReader(TestResources.GetTestBeatmapStream())) + // ReSharper disable once AccessToDisposedClosure + beatmapSet = await importer.Import(reader); + + Assert.NotNull(beatmapSet); + Debug.Assert(beatmapSet != null); + + // Intentionally detach on async thread as to not trigger a refresh on the main thread. + beatmapSet.PerformRead(s => detachedSet = s.Detach()); + }).WaitSafely(); + + Debug.Assert(detachedSet != null); + importer.AddFile(detachedSet, new MemoryStream(), "test"); + } + }); + } + [Test] public void TestImportBeatmapThenCleanup() { From 33f024212ffaaa1d70c38b20ff46e42b77fd8654 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 20:47:26 +0900 Subject: [PATCH 0992/1276] Fix realm refetch operations potentially being unsafe As seen in test failure https://github.com/ppy/osu/runs/6357384721?check_suite_focus=true. --- osu.Game/Database/RealmAccess.cs | 20 +++++++++++++++++ osu.Game/Stores/RealmArchiveModelManager.cs | 25 ++++++++++++++------- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index b0a70b51d0..937876a70e 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -344,6 +344,26 @@ namespace osu.Game.Database } } + /// + /// Write changes to realm. + /// + /// The work to run. + public T Write(Func action) + { + if (ThreadSafety.IsUpdateThread) + { + total_writes_update.Value++; + return Realm.Write(action); + } + else + { + total_writes_async.Value++; + + using (var realm = getRealmInstance()) + return realm.Write(action); + } + } + /// /// Write changes to realm. /// diff --git a/osu.Game/Stores/RealmArchiveModelManager.cs b/osu.Game/Stores/RealmArchiveModelManager.cs index 57e51b79aa..e349efe5ad 100644 --- a/osu.Game/Stores/RealmArchiveModelManager.cs +++ b/osu.Game/Stores/RealmArchiveModelManager.cs @@ -45,11 +45,16 @@ namespace osu.Game.Stores // This method should be removed as soon as all the surrounding pieces support non-detached operations. if (!item.IsManaged) { - var managed = Realm.Realm.Find(item.ID); - managed.Realm.Write(() => operation(managed)); + // Importantly, begin the realm write *before* re-fetching, else the update realm may not be in a consistent state + // (ie. if an async import finished very recently). + Realm.Realm.Write(realm => + { + var managed = Realm.Realm.Find(item.ID); + operation(managed); - item.Files.Clear(); - item.Files.AddRange(managed.Files.Detach()); + item.Files.Clear(); + item.Files.AddRange(managed.Files.Detach()); + }); } else operation(item); @@ -165,7 +170,9 @@ namespace osu.Game.Stores public bool Delete(TModel item) { - return Realm.Run(realm => + // Importantly, begin the realm write *before* re-fetching, else the update realm may not be in a consistent state + // (ie. if an async import finished very recently). + return Realm.Write(realm => { if (!item.IsManaged) item = realm.Find(item.ID); @@ -173,14 +180,16 @@ namespace osu.Game.Stores if (item?.DeletePending != false) return false; - realm.Write(r => item.DeletePending = true); + item.DeletePending = true; return true; }); } public void Undelete(TModel item) { - Realm.Run(realm => + // Importantly, begin the realm write *before* re-fetching, else the update realm may not be in a consistent state + // (ie. if an async import finished very recently). + Realm.Write(realm => { if (!item.IsManaged) item = realm.Find(item.ID); @@ -188,7 +197,7 @@ namespace osu.Game.Stores if (item?.DeletePending != true) return; - realm.Write(r => item.DeletePending = false); + item.DeletePending = false; }); } From 0eb29d56f258a2febc465de501bc22ef219bbecf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 19:46:55 +0900 Subject: [PATCH 0993/1276] Rename new test method to be english --- osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 661465b484..6e8ed5ebb4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -482,7 +482,7 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestCorrectAudioAdjustmentDeapplication() + public void TestCorrectAudioAdjustmentAfterPitchAdjustChange() { createScreen(); changeRuleset(0); From bbbecbb6b7bcf080033b3236dc09918957ef5fb8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 17:54:34 +0300 Subject: [PATCH 0994/1276] Apply time-ramping adjustment using clock instead of track --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index fe6d54332c..12046d1577 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Mods public virtual void Update(Playfield playfield) { - applyRateAdjustment(track.CurrentTime); + applyRateAdjustment(playfield.Clock.CurrentTime); } /// From 4f5001704e6b45a4b9e4a4a47f1cde91eb92c8be Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 18:01:15 +0300 Subject: [PATCH 0995/1276] Change `IApplicableToTrack` to receive adjustable component instead --- osu.Game/Rulesets/Mods/IApplicableToTrack.cs | 4 ++-- osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | 6 ++---- osu.Game/Rulesets/Mods/ModDaycore.cs | 3 +-- osu.Game/Rulesets/Mods/ModMuted.cs | 3 +-- osu.Game/Rulesets/Mods/ModNightcore.cs | 2 +- osu.Game/Rulesets/Mods/ModRateAdjust.cs | 3 +-- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 5 ++--- 7 files changed, 10 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Mods/IApplicableToTrack.cs b/osu.Game/Rulesets/Mods/IApplicableToTrack.cs index 9b840cea08..deecd4bf1f 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToTrack.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToTrack.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Audio.Track; +using osu.Framework.Audio; namespace osu.Game.Rulesets.Mods { @@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Mods /// public interface IApplicableToTrack : IApplicableMod { - void ApplyToTrack(ITrack track); + void ApplyToTrack(IAdjustableAudioComponent track); } } diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs index 93251f7b2d..c9661662bf 100644 --- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs +++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Audio; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.Audio; using osu.Framework.Utils; @@ -79,7 +78,7 @@ namespace osu.Game.Rulesets.Mods // Apply a fixed rate change when missing, allowing the player to catch up when the rate is too fast. private const double rate_change_on_miss = 0.95d; - private ITrack track; + private IAdjustableAudioComponent track; private double targetRate = 1d; /// @@ -141,7 +140,7 @@ namespace osu.Game.Rulesets.Mods AdjustPitch.BindValueChanged(adjustPitchChanged); } - public void ApplyToTrack(ITrack track) + public void ApplyToTrack(IAdjustableAudioComponent track) { this.track = track; @@ -210,7 +209,6 @@ namespace osu.Game.Rulesets.Mods private void adjustPitchChanged(ValueChangedEvent adjustPitchSetting) { track?.RemoveAdjustment(adjustmentForPitchSetting(adjustPitchSetting.OldValue), SpeedChange); - track?.AddAdjustment(adjustmentForPitchSetting(adjustPitchSetting.NewValue), SpeedChange); } diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index 61ad7db706..9e8e44229e 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Audio; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; @@ -27,7 +26,7 @@ namespace osu.Game.Rulesets.Mods }, true); } - public override void ApplyToTrack(ITrack track) + public override void ApplyToTrack(IAdjustableAudioComponent track) { // base.ApplyToTrack() intentionally not called (different tempo adjustment is applied) track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 1d33b44812..a7d3114f2b 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -3,7 +3,6 @@ using System.Linq; using osu.Framework.Audio; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; @@ -71,7 +70,7 @@ namespace osu.Game.Rulesets.Mods InverseMuting.BindValueChanged(i => MuteComboCount.MinValue = i.NewValue ? 1 : 0, true); } - public void ApplyToTrack(ITrack track) + public void ApplyToTrack(IAdjustableAudioComponent track) { track.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); } diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 993efead33..7997204450 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Mods }, true); } - public override void ApplyToTrack(ITrack track) + public override void ApplyToTrack(IAdjustableAudioComponent track) { // base.ApplyToTrack() intentionally not called (different tempo adjustment is applied) track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index 05953f903f..49590c30ca 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Audio; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.Audio; @@ -15,7 +14,7 @@ namespace osu.Game.Rulesets.Mods public abstract BindableNumber SpeedChange { get; } - public virtual void ApplyToTrack(ITrack track) + public virtual void ApplyToTrack(IAdjustableAudioComponent track) { track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange); } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 12046d1577..22d6d4ca29 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using osu.Framework.Audio; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.Audio; using osu.Game.Beatmaps; @@ -46,7 +45,7 @@ namespace osu.Game.Rulesets.Mods Precision = 0.01, }; - private ITrack track; + private IAdjustableAudioComponent track; protected ModTimeRamp() { @@ -55,7 +54,7 @@ namespace osu.Game.Rulesets.Mods AdjustPitch.BindValueChanged(applyPitchAdjustment); } - public void ApplyToTrack(ITrack track) + public void ApplyToTrack(IAdjustableAudioComponent track) { this.track = track; From 82b784ce5a2b29839ea3b4177c1274bd31eec2d4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 18:01:36 +0300 Subject: [PATCH 0996/1276] Change `IApplicableToSample` to receive adjustable component instead Done for consistency with `IApplicableToTrack`. --- osu.Game/Rulesets/Mods/IApplicableToSample.cs | 4 ++-- osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | 3 +-- osu.Game/Rulesets/Mods/ModRateAdjust.cs | 3 +-- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 3 +-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Mods/IApplicableToSample.cs b/osu.Game/Rulesets/Mods/IApplicableToSample.cs index 50a6d501b6..efd88f2399 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToSample.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToSample.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.Audio; +using osu.Framework.Audio; namespace osu.Game.Rulesets.Mods { @@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Mods /// public interface IApplicableToSample : IApplicableMod { - void ApplyToSample(DrawableSample sample); + void ApplyToSample(IAdjustableAudioComponent sample); } } diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs index c9661662bf..fb291fe10f 100644 --- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs +++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Audio; using osu.Framework.Bindables; -using osu.Framework.Graphics.Audio; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -150,7 +149,7 @@ namespace osu.Game.Rulesets.Mods recentRates.AddRange(Enumerable.Repeat(InitialRate.Value, recent_rate_count)); } - public void ApplyToSample(DrawableSample sample) + public void ApplyToSample(IAdjustableAudioComponent sample) { sample.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); } diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index 49590c30ca..7b55ba4ad0 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -4,7 +4,6 @@ using System; using osu.Framework.Audio; using osu.Framework.Bindables; -using osu.Framework.Graphics.Audio; namespace osu.Game.Rulesets.Mods { @@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Mods track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange); } - public virtual void ApplyToSample(DrawableSample sample) + public virtual void ApplyToSample(IAdjustableAudioComponent sample) { sample.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 22d6d4ca29..98abda872b 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -5,7 +5,6 @@ using System; using System.Linq; using osu.Framework.Audio; using osu.Framework.Bindables; -using osu.Framework.Graphics.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Objects; @@ -62,7 +61,7 @@ namespace osu.Game.Rulesets.Mods AdjustPitch.TriggerChange(); } - public void ApplyToSample(DrawableSample sample) + public void ApplyToSample(IAdjustableAudioComponent sample) { sample.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); } From 725ff93f34e9b49cca90e1006089d512e9891f06 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 18:02:32 +0300 Subject: [PATCH 0997/1276] Define local adjustments component for mods in `MusicController` Isolates `CurrentTrack` from being directly adjusted by the mod, which could lead to issues depending on how the mod adds adjustments (i.e. `ModTimeRamp`, which adds adjustments based on changes to a setting bindable). --- osu.Game/Overlays/MusicController.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 5fc0da8891..0373856ace 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -377,6 +377,8 @@ namespace osu.Game.Overlays } } + private readonly AudioAdjustments modTrackAdjustments = new AudioAdjustments(); + /// /// Resets the adjustments currently applied on and applies the mod adjustments if is true. /// @@ -385,15 +387,27 @@ namespace osu.Game.Overlays /// public void ResetTrackAdjustments() { + // todo: we probably want a helper method rather than this. CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Balance); CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Frequency); CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Tempo); CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Volume); - if (allowTrackAdjustments) + modTrackAdjustments.RemoveAllAdjustments(AdjustableProperty.Balance); + modTrackAdjustments.RemoveAllAdjustments(AdjustableProperty.Frequency); + modTrackAdjustments.RemoveAllAdjustments(AdjustableProperty.Tempo); + modTrackAdjustments.RemoveAllAdjustments(AdjustableProperty.Volume); + + var applicableToTrack = mods.Value.OfType(); + + if (!allowTrackAdjustments || !applicableToTrack.Any()) + CurrentTrack.UnbindAdjustments(modTrackAdjustments); + else { - foreach (var mod in mods.Value.OfType()) - mod.ApplyToTrack(CurrentTrack); + CurrentTrack.BindAdjustments(modTrackAdjustments); + + foreach (var mod in applicableToTrack) + mod.ApplyToTrack(modTrackAdjustments); } } } From ec231e0f312eeeecb8229a0fe1ee0660935b455f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 00:45:17 +0900 Subject: [PATCH 0998/1276] Use more local realm reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Stores/RealmArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Stores/RealmArchiveModelManager.cs b/osu.Game/Stores/RealmArchiveModelManager.cs index e349efe5ad..cc8229b436 100644 --- a/osu.Game/Stores/RealmArchiveModelManager.cs +++ b/osu.Game/Stores/RealmArchiveModelManager.cs @@ -49,7 +49,7 @@ namespace osu.Game.Stores // (ie. if an async import finished very recently). Realm.Realm.Write(realm => { - var managed = Realm.Realm.Find(item.ID); + var managed = realm.Find(item.ID); operation(managed); item.Files.Clear(); From a0f1c48e806ff64f6c58b521b43c1d87f3d83e0b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 19:24:36 +0300 Subject: [PATCH 0999/1276] Fix `ModTimeRampTest` failing due to changes in `Update` method --- osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs b/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs index 4b9f2181dc..51163efd6a 100644 --- a/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs +++ b/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs @@ -3,9 +3,11 @@ using NUnit.Framework; using osu.Framework.Audio.Track; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; namespace osu.Game.Tests.Rulesets.Mods { @@ -16,11 +18,14 @@ namespace osu.Game.Tests.Rulesets.Mods private const double duration = 9000; private TrackVirtual track; + private OsuPlayfield playfield; [SetUp] public void SetUp() { track = new TrackVirtual(20_000); + // define a fake playfield to re-calculate the current rate by ModTimeRamp.Update(Playfield). + playfield = new OsuPlayfield { Clock = new FramedClock(track) }; } [TestCase(0, 1)] @@ -80,8 +85,8 @@ namespace osu.Game.Tests.Rulesets.Mods private void seekTrackAndUpdateMod(ModTimeRamp mod, double time) { track.Seek(time); - // update the mod via a fake playfield to re-calculate the current rate. - mod.Update(null); + playfield.Clock.ProcessFrame(); + mod.Update(playfield); } private static Beatmap createSingleSpinnerBeatmap() From 36a764416400d2d8f77793a1e258f5f7d092742a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 20:46:31 +0300 Subject: [PATCH 1000/1276] Reinstantiate mod adjustments layer for safety against previous mods --- osu.Game/Overlays/MusicController.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 0373856ace..50495d0a2c 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -377,7 +377,7 @@ namespace osu.Game.Overlays } } - private readonly AudioAdjustments modTrackAdjustments = new AudioAdjustments(); + private AudioAdjustments modTrackAdjustments; /// /// Resets the adjustments currently applied on and applies the mod adjustments if is true. @@ -393,18 +393,19 @@ namespace osu.Game.Overlays CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Tempo); CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Volume); - modTrackAdjustments.RemoveAllAdjustments(AdjustableProperty.Balance); - modTrackAdjustments.RemoveAllAdjustments(AdjustableProperty.Frequency); - modTrackAdjustments.RemoveAllAdjustments(AdjustableProperty.Tempo); - modTrackAdjustments.RemoveAllAdjustments(AdjustableProperty.Volume); - var applicableToTrack = mods.Value.OfType(); if (!allowTrackAdjustments || !applicableToTrack.Any()) - CurrentTrack.UnbindAdjustments(modTrackAdjustments); + { + if (modTrackAdjustments != null) + { + CurrentTrack.UnbindAdjustments(modTrackAdjustments); + modTrackAdjustments = null; + } + } else { - CurrentTrack.BindAdjustments(modTrackAdjustments); + CurrentTrack.BindAdjustments(modTrackAdjustments = new AudioAdjustments()); foreach (var mod in applicableToTrack) mod.ApplyToTrack(modTrackAdjustments); From 9446be251117f6b904c67b09aea0155b535738a6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 20:49:15 +0300 Subject: [PATCH 1001/1276] Remove unnecessary `UnbindAdjustments` call It is not necessary given that `CurrentTrack` already removes all adjustments first. --- osu.Game/Overlays/MusicController.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 50495d0a2c..65b06eb864 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -393,21 +393,11 @@ namespace osu.Game.Overlays CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Tempo); CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Volume); - var applicableToTrack = mods.Value.OfType(); - - if (!allowTrackAdjustments || !applicableToTrack.Any()) - { - if (modTrackAdjustments != null) - { - CurrentTrack.UnbindAdjustments(modTrackAdjustments); - modTrackAdjustments = null; - } - } - else + if (allowTrackAdjustments) { CurrentTrack.BindAdjustments(modTrackAdjustments = new AudioAdjustments()); - foreach (var mod in applicableToTrack) + foreach (var mod in mods.Value.OfType()) mod.ApplyToTrack(modTrackAdjustments); } } From 9cfe2cc310b916f2ca78ca1daefd256a777f2dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:43:57 +0200 Subject: [PATCH 1002/1276] Move `TestCustomisableModRuleset` out of `TestSceneModSettings` --- .../Mods/TestCustomisableModRuleset.cs | 80 +++++++++++++++++++ .../Gameplay/TestSceneReplayRecorder.cs | 6 +- .../Gameplay/TestSceneSpectatorPlayback.cs | 6 +- .../UserInterface/TestSceneModSettings.cs | 78 +----------------- 4 files changed, 90 insertions(+), 80 deletions(-) create mode 100644 osu.Game.Tests/Mods/TestCustomisableModRuleset.cs diff --git a/osu.Game.Tests/Mods/TestCustomisableModRuleset.cs b/osu.Game.Tests/Mods/TestCustomisableModRuleset.cs new file mode 100644 index 0000000000..3992d9abe6 --- /dev/null +++ b/osu.Game.Tests/Mods/TestCustomisableModRuleset.cs @@ -0,0 +1,80 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Tests.Mods +{ + public class TestCustomisableModRuleset : Ruleset + { + public static RulesetInfo CreateTestRulesetInfo() => new TestCustomisableModRuleset().RulesetInfo; + + public override IEnumerable GetModsFor(ModType type) + { + if (type == ModType.Conversion) + { + return new Mod[] + { + new TestModCustomisable1(), + new TestModCustomisable2() + }; + } + + return Array.Empty(); + } + + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => throw new NotImplementedException(); + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException(); + + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException(); + + public override string Description { get; } = "test"; + public override string ShortName { get; } = "tst"; + + public class TestModCustomisable1 : TestModCustomisable + { + public override string Name => "Customisable Mod 1"; + + public override string Acronym => "CM1"; + } + + public class TestModCustomisable2 : TestModCustomisable + { + public override string Name => "Customisable Mod 2"; + + public override string Acronym => "CM2"; + + public override bool RequiresConfiguration => true; + } + + public abstract class TestModCustomisable : Mod, IApplicableMod + { + public override double ScoreMultiplier => 1.0; + + public override string Description => "This is a customisable test mod."; + + public override ModType Type => ModType.Conversion; + + [SettingSource("Sample float", "Change something for a mod")] + public BindableFloat SliderBindable { get; } = new BindableFloat + { + MinValue = 0, + MaxValue = 10, + Default = 5, + Value = 7 + }; + + [SettingSource("Sample bool", "Clicking this changes a setting")] + public BindableBool TickBindable { get; } = new BindableBool(); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index 8df32c500e..81763564fa 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -24,7 +24,7 @@ using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Play; -using osu.Game.Tests.Visual.UserInterface; +using osu.Game.Tests.Mods; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Gameplay { new Drawable[] { - recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) + recordingManager = new TestRulesetInputManager(TestCustomisableModRuleset.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { Recorder = recorder = new TestReplayRecorder(new Score { @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Gameplay }, new Drawable[] { - playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) + playbackManager = new TestRulesetInputManager(TestCustomisableModRuleset.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { ReplayInputHandler = new TestFramedReplayInputHandler(replay) { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 4ec46036f6..f8748922cf 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -27,8 +27,8 @@ using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Play; +using osu.Game.Tests.Mods; using osu.Game.Tests.Visual.Spectator; -using osu.Game.Tests.Visual.UserInterface; using osuTK; using osuTK.Graphics; @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay { new Drawable[] { - recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) + recordingManager = new TestRulesetInputManager(TestCustomisableModRuleset.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { Recorder = recorder = new TestReplayRecorder { @@ -107,7 +107,7 @@ namespace osu.Game.Tests.Visual.Gameplay }, new Drawable[] { - playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) + playbackManager = new TestRulesetInputManager(TestCustomisableModRuleset.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { Clock = new FramedClock(manualClock), ReplayInputHandler = replayHandler = new TestFramedReplayInputHandler(replay) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 9a3083e8db..379735a7f5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -5,20 +5,15 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Framework.Testing; -using osu.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.UI; +using osu.Game.Tests.Mods; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface @@ -27,15 +22,15 @@ namespace osu.Game.Tests.Visual.UserInterface { private TestModSelectOverlay modSelect; - private readonly Mod testCustomisableMod = new TestModCustomisable1(); + private readonly Mod testCustomisableMod = new TestCustomisableModRuleset.TestModCustomisable1(); - private readonly Mod testCustomisableAutoOpenMod = new TestModCustomisable2(); + private readonly Mod testCustomisableAutoOpenMod = new TestCustomisableModRuleset.TestModCustomisable2(); [SetUp] public void SetUp() => Schedule(() => { SelectedMods.Value = Array.Empty(); - Ruleset.Value = CreateTestRulesetInfo(); + Ruleset.Value = TestCustomisableModRuleset.CreateTestRulesetInfo(); }); [Test] @@ -169,70 +164,5 @@ namespace osu.Game.Tests.Visual.UserInterface public void SetModSettingsWidth(float newWidth) => ModSettingsContainer.Parent.Width = newWidth; } - - public static RulesetInfo CreateTestRulesetInfo() => new TestCustomisableModRuleset().RulesetInfo; - - public class TestCustomisableModRuleset : Ruleset - { - public override IEnumerable GetModsFor(ModType type) - { - if (type == ModType.Conversion) - { - return new Mod[] - { - new TestModCustomisable1(), - new TestModCustomisable2() - }; - } - - return Array.Empty(); - } - - public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => throw new NotImplementedException(); - - public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException(); - - public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException(); - - public override string Description { get; } = "test"; - public override string ShortName { get; } = "tst"; - } - - private class TestModCustomisable1 : TestModCustomisable - { - public override string Name => "Customisable Mod 1"; - - public override string Acronym => "CM1"; - } - - private class TestModCustomisable2 : TestModCustomisable - { - public override string Name => "Customisable Mod 2"; - - public override string Acronym => "CM2"; - - public override bool RequiresConfiguration => true; - } - - private abstract class TestModCustomisable : Mod, IApplicableMod - { - public override double ScoreMultiplier => 1.0; - - public override string Description => "This is a customisable test mod."; - - public override ModType Type => ModType.Conversion; - - [SettingSource("Sample float", "Change something for a mod")] - public BindableFloat SliderBindable { get; } = new BindableFloat - { - MinValue = 0, - MaxValue = 10, - Default = 5, - Value = 7 - }; - - [SettingSource("Sample bool", "Clicking this changes a setting")] - public BindableBool TickBindable { get; } = new BindableBool(); - } } } From 4a3447f59f69af6b591f4c6c44f5b3422b229b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:45:57 +0200 Subject: [PATCH 1003/1276] Remove old free mod select overlay --- .../TestSceneFreeModSelectOverlay.cs | 21 --- .../OnlinePlay/FreeModSelectOverlay.cs | 157 ------------------ 2 files changed, 178 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs delete mode 100644 osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs deleted file mode 100644 index 26a0301d8a..0000000000 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Framework.Graphics.Containers; -using osu.Game.Screens.OnlinePlay; - -namespace osu.Game.Tests.Visual.Multiplayer -{ - public class TestSceneFreeModSelectOverlay : MultiplayerTestScene - { - [SetUp] - public new void Setup() => Schedule(() => - { - Child = new FreeModSelectOverlay - { - State = { Value = Visibility.Visible } - }; - }); - } -} diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs deleted file mode 100644 index d5abaaab4e..0000000000 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Mods; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Screens.OnlinePlay -{ - /// - /// A used for free-mod selection in online play. - /// - public class FreeModSelectOverlay : ModSelectOverlay - { - protected override bool Stacked => false; - - protected override bool AllowConfiguration => false; - - public new Func IsValidMod - { - get => base.IsValidMod; - set => base.IsValidMod = m => m.HasImplementation && m.UserPlayable && value(m); - } - - public FreeModSelectOverlay() - { - IsValidMod = m => true; - - DeselectAllButton.Alpha = 0; - - Drawable selectAllButton; - Drawable deselectAllButton; - - FooterContainer.AddRange(new[] - { - selectAllButton = new TriangleButton - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Width = 180, - Text = "Select All", - Action = selectAll, - }, - // Unlike the base mod select overlay, this button deselects mods instantaneously. - deselectAllButton = new TriangleButton - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Width = 180, - Text = "Deselect All", - Action = deselectAll, - }, - }); - - FooterContainer.SetLayoutPosition(selectAllButton, -2); - FooterContainer.SetLayoutPosition(deselectAllButton, -1); - } - - private void selectAll() - { - foreach (var section in ModSectionsContainer.Children) - section.SelectAll(); - } - - private void deselectAll() - { - foreach (var section in ModSectionsContainer.Children) - section.DeselectAll(); - } - - protected override void OnAvailableModsChanged() - { - base.OnAvailableModsChanged(); - - foreach (var section in ModSectionsContainer.Children) - ((FreeModSection)section).UpdateCheckboxState(); - } - - protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); - - private class FreeModSection : ModSection - { - private HeaderCheckbox checkbox; - - public FreeModSection(ModType type) - : base(type) - { - } - - protected override Drawable CreateHeader(string text) => new Container - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Child = checkbox = new HeaderCheckbox - { - LabelText = text, - Changed = onCheckboxChanged - } - }; - - private void onCheckboxChanged(bool value) - { - if (value) - SelectAll(); - else - DeselectAll(); - } - - protected override void ModButtonStateChanged(Mod mod) - { - base.ModButtonStateChanged(mod); - UpdateCheckboxState(); - } - - public void UpdateCheckboxState() - { - if (!SelectionAnimationRunning) - { - var validButtons = Buttons.Where(b => b.Mod.HasImplementation); - checkbox.Current.Value = validButtons.All(b => b.Selected); - } - } - } - - private class HeaderCheckbox : OsuCheckbox - { - public Action Changed; - - protected override bool PlaySoundsOnUserChange => false; - - public HeaderCheckbox() - : base(false) - - { - } - - protected override void ApplyLabelParameters(SpriteText text) - { - base.ApplyLabelParameters(text); - - text.Font = OsuFont.GetFont(weight: FontWeight.Bold); - } - - protected override void OnUserChange(bool value) - { - base.OnUserChange(value); - Changed?.Invoke(value); - } - } - } -} From 24c59e2f2fa942769843ebd8afd299c4008c3c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:48:21 +0200 Subject: [PATCH 1004/1276] Remove old user mod select overlay --- .../TestSceneModSelectOverlay.cs | 472 ------------------ .../UserInterface/TestSceneModSettings.cs | 119 ----- .../IncompatibilityDisplayingModButton.cs | 66 --- .../Overlays/Mods/UserModSelectOverlay.cs | 30 -- 4 files changed, 687 deletions(-) delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs delete mode 100644 osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs delete mode 100644 osu.Game/Overlays/Mods/UserModSelectOverlay.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs deleted file mode 100644 index b429619044..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Testing; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Mods; -using osu.Game.Overlays.Settings; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mania; -using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.Play.HUD; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Tests.Visual.UserInterface -{ - [Description("mod select and icon display")] - public class TestSceneModSelectOverlay : OsuTestScene - { - private RulesetStore rulesets; - private ModDisplay modDisplay; - private TestModSelectOverlay modSelect; - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - - [SetUp] - public void SetUp() => Schedule(() => - { - SelectedMods.Value = Array.Empty(); - createDisplay(() => new TestModSelectOverlay()); - }); - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("show", () => modSelect.Show()); - } - - /// - /// Ensure that two mod overlays are not cross polluting via central settings instances. - /// - [Test] - public void TestSettingsNotCrossPolluting() - { - Bindable> selectedMods2 = null; - - AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() }); - - AddStep("set setting", () => modSelect.ChildrenOfType>().First().Current.Value = 8); - - AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); - - AddStep("create second bindable", () => selectedMods2 = new Bindable>(new Mod[] { new OsuModDifficultyAdjust() })); - - AddStep("create second overlay", () => - { - Add(modSelect = new TestModSelectOverlay().With(d => - { - d.Origin = Anchor.TopCentre; - d.Anchor = Anchor.TopCentre; - d.SelectedMods.BindTarget = selectedMods2; - })); - }); - - AddStep("show", () => modSelect.Show()); - - AddAssert("ensure first is unchanged", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); - AddAssert("ensure second is default", () => selectedMods2.Value.OfType().Single().CircleSize.Value == null); - } - - [Test] - public void TestSettingsResetOnDeselection() - { - var osuModDoubleTime = new OsuModDoubleTime { SpeedChange = { Value = 1.2 } }; - - changeRuleset(0); - - AddStep("set dt mod with custom rate", () => { SelectedMods.Value = new[] { osuModDoubleTime }; }); - - AddAssert("selected mod matches", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.Value == 1.2); - - AddStep("deselect", () => modSelect.DeselectAllButton.TriggerClick()); - AddAssert("selected mods empty", () => SelectedMods.Value.Count == 0); - - AddStep("reselect", () => modSelect.GetModButton(osuModDoubleTime).TriggerClick()); - AddAssert("selected mod has default value", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.IsDefault == true); - } - - [Test] - public void TestAnimationFlushOnClose() - { - changeRuleset(0); - - AddStep("Select all fun mods", () => - { - modSelect.ModSectionsContainer - .Single(c => c.ModType == ModType.DifficultyIncrease) - .SelectAll(); - }); - - AddUntilStep("many mods selected", () => modDisplay.Current.Value.Count >= 5); - - AddStep("trigger deselect and close overlay", () => - { - modSelect.ModSectionsContainer - .Single(c => c.ModType == ModType.DifficultyIncrease) - .DeselectAll(); - - modSelect.Hide(); - }); - - AddAssert("all mods deselected", () => modDisplay.Current.Value.Count == 0); - } - - [Test] - public void TestOsuMods() - { - changeRuleset(0); - - var osu = new OsuRuleset(); - - var easierMods = osu.GetModsFor(ModType.DifficultyReduction); - var harderMods = osu.GetModsFor(ModType.DifficultyIncrease); - - var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail); - - var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime)); - - var easy = easierMods.FirstOrDefault(m => m is OsuModEasy); - var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock); - - testSingleMod(noFailMod); - testMultiMod(doubleTimeMod); - testIncompatibleMods(easy, hardRock); - testDeselectAll(easierMods.Where(m => !(m is MultiMod))); - } - - [Test] - public void TestManiaMods() - { - changeRuleset(3); - - var mania = new ManiaRuleset(); - - testModsWithSameBaseType( - mania.CreateMod(), - mania.CreateMod()); - } - - [Test] - public void TestRulesetChanges() - { - changeRuleset(0); - - var noFailMod = new OsuRuleset().GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail); - - AddStep("set mods externally", () => { SelectedMods.Value = new[] { noFailMod }; }); - - changeRuleset(0); - - AddAssert("ensure mods still selected", () => modDisplay.Current.Value.SingleOrDefault(m => m is OsuModNoFail) != null); - - changeRuleset(3); - - AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0); - - changeRuleset(0); - - AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0); - } - - [Test] - public void TestExternallySetCustomizedMod() - { - changeRuleset(0); - - 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; - }); - } - - [Test] - public void TestSettingsAreRetainedOnReload() - { - changeRuleset(0); - - AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }); - - AddAssert("setting remains", () => (SelectedMods.Value.SingleOrDefault() as OsuModDoubleTime)?.SpeedChange.Value == 1.01); - - AddStep("create overlay", () => createDisplay(() => new TestNonStackedModSelectOverlay())); - - AddAssert("setting remains", () => (SelectedMods.Value.SingleOrDefault() as OsuModDoubleTime)?.SpeedChange.Value == 1.01); - } - - [Test] - public void TestExternallySetModIsReplacedByOverlayInstance() - { - Mod external = new OsuModDoubleTime(); - Mod overlayButtonMod = null; - - changeRuleset(0); - - AddStep("set mod externally", () => { SelectedMods.Value = new[] { external }; }); - - AddAssert("ensure button is selected", () => - { - var button = modSelect.GetModButton(SelectedMods.Value.Single()); - overlayButtonMod = button.SelectedMod; - return overlayButtonMod.GetType() == external.GetType(); - }); - - // Right now, when an external change occurs, the ModSelectOverlay will replace the global instance with its own - AddAssert("mod instance doesn't match", () => external != overlayButtonMod); - - AddAssert("one mod present in global selected", () => SelectedMods.Value.Count == 1); - AddAssert("globally selected matches button's mod instance", () => SelectedMods.Value.Contains(overlayButtonMod)); - AddAssert("globally selected doesn't contain original external change", () => !SelectedMods.Value.Contains(external)); - } - - [Test] - public void TestNonStacked() - { - changeRuleset(0); - - AddStep("create overlay", () => createDisplay(() => new TestNonStackedModSelectOverlay())); - - AddStep("show", () => modSelect.Show()); - - AddAssert("ensure all buttons are spread out", () => modSelect.ChildrenOfType().All(m => m.Mods.Length <= 1)); - } - - [Test] - public void TestChangeIsValidChangesButtonVisibility() - { - changeRuleset(0); - - AddAssert("double time visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModDoubleTime))); - - AddStep("make double time invalid", () => modSelect.IsValidMod = m => !(m is OsuModDoubleTime)); - AddUntilStep("double time not visible", () => modSelect.ChildrenOfType().All(b => !b.Mods.Any(m => m is OsuModDoubleTime))); - AddAssert("nightcore still visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModNightcore))); - - AddStep("make double time valid again", () => modSelect.IsValidMod = m => true); - AddUntilStep("double time visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModDoubleTime))); - AddAssert("nightcore still visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModNightcore))); - } - - [Test] - public void TestChangeIsValidPreservesSelection() - { - changeRuleset(0); - - AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); - AddAssert("DT + HD selected", () => modSelect.ChildrenOfType().Count(b => b.Selected) == 2); - - AddStep("make NF invalid", () => modSelect.IsValidMod = m => !(m is ModNoFail)); - AddAssert("DT + HD still selected", () => modSelect.ChildrenOfType().Count(b => b.Selected) == 2); - } - - [Test] - public void TestUnimplementedModIsUnselectable() - { - var testRuleset = new TestUnimplementedModOsuRuleset(); - changeTestRuleset(testRuleset.RulesetInfo); - - var conversionMods = testRuleset.GetModsFor(ModType.Conversion); - - var unimplementedMod = conversionMods.FirstOrDefault(m => m is TestUnimplementedMod); - - testUnimplementedMod(unimplementedMod); - } - - private void testSingleMod(Mod mod) - { - selectNext(mod); - checkSelected(mod); - - selectPrevious(mod); - checkNotSelected(mod); - - selectNext(mod); - selectNext(mod); - checkNotSelected(mod); - - selectPrevious(mod); - selectPrevious(mod); - checkNotSelected(mod); - } - - private void testMultiMod(MultiMod multiMod) - { - foreach (var mod in multiMod.Mods) - { - selectNext(mod); - checkSelected(mod); - } - - for (int index = multiMod.Mods.Length - 1; index >= 0; index--) - selectPrevious(multiMod.Mods[index]); - - foreach (var mod in multiMod.Mods) - checkNotSelected(mod); - } - - private void testUnimplementedMod(Mod mod) - { - selectNext(mod); - checkNotSelected(mod); - } - - private void testIncompatibleMods(Mod modA, Mod modB) - { - selectNext(modA); - checkSelected(modA); - checkNotSelected(modB); - - selectNext(modB); - checkSelected(modB); - checkNotSelected(modA); - - selectPrevious(modB); - checkNotSelected(modA); - checkNotSelected(modB); - } - - private void testDeselectAll(IEnumerable mods) - { - foreach (var mod in mods) - selectNext(mod); - - AddAssert("check for any selection", () => modSelect.SelectedMods.Value.Any()); - AddStep("deselect all", () => modSelect.DeselectAllButton.Action.Invoke()); - AddAssert("check for no selection", () => !modSelect.SelectedMods.Value.Any()); - } - - private void testModsWithSameBaseType(Mod modA, Mod modB) - { - selectNext(modA); - checkSelected(modA); - selectNext(modB); - checkSelected(modB); - - // Backwards - selectPrevious(modA); - checkSelected(modA); - } - - private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1)); - - private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(-1)); - - private void checkSelected(Mod mod) - { - AddAssert($"check {mod.Name} is selected", () => - { - var button = modSelect.GetModButton(mod); - return modSelect.SelectedMods.Value.SingleOrDefault(m => m.Name == mod.Name) != null && button.SelectedMod.GetType() == mod.GetType() && button.Selected; - }); - } - - private void changeRuleset(int? onlineId) - { - AddStep($"change ruleset to {(onlineId?.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.OnlineID == onlineId); }); - waitForLoad(); - } - - private void changeTestRuleset(RulesetInfo rulesetInfo) - { - AddStep($"change ruleset to {rulesetInfo.Name}", () => { Ruleset.Value = rulesetInfo; }); - waitForLoad(); - } - - private void waitForLoad() => - AddUntilStep("wait for icons to load", () => modSelect.AllLoaded); - - private void checkNotSelected(Mod mod) - { - AddAssert($"check {mod.Name} is not selected", () => - { - var button = modSelect.GetModButton(mod); - return modSelect.SelectedMods.Value.All(m => m.GetType() != mod.GetType()) && button.SelectedMod?.GetType() != mod.GetType(); - }); - } - - private void createDisplay(Func createOverlayFunc) - { - Children = new Drawable[] - { - modSelect = createOverlayFunc().With(d => - { - d.Origin = Anchor.BottomCentre; - d.Anchor = Anchor.BottomCentre; - d.SelectedMods.BindTarget = SelectedMods; - }), - modDisplay = new ModDisplay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Position = new Vector2(-5, 25), - Current = { BindTarget = modSelect.SelectedMods } - } - }; - } - - private class TestModSelectOverlay : UserModSelectOverlay - { - public new Bindable> SelectedMods => base.SelectedMods; - - public bool AllLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded); - - public new FillFlowContainer ModSectionsContainer => - base.ModSectionsContainer; - - public ModButton GetModButton(Mod mod) - { - var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type); - return section.ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())); - } - - public new TriangleButton DeselectAllButton => base.DeselectAllButton; - - public new Color4 LowMultiplierColour => base.LowMultiplierColour; - public new Color4 HighMultiplierColour => base.HighMultiplierColour; - } - - private class TestNonStackedModSelectOverlay : TestModSelectOverlay - { - protected override bool Stacked => false; - } - - private class TestUnimplementedMod : Mod - { - public override string Name => "Unimplemented mod"; - public override string Acronym => "UM"; - public override string Description => "A mod that is not implemented."; - public override double ScoreMultiplier => 1; - public override ModType Type => ModType.Conversion; - } - - private class TestUnimplementedModOsuRuleset : OsuRuleset - { - public override string ShortName => "unimplemented"; - - public override IEnumerable GetModsFor(ModType type) - { - if (type == ModType.Conversion) return base.GetModsFor(type).Concat(new[] { new TestUnimplementedMod() }); - - return base.GetModsFor(type); - } - } - } -} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 379735a7f5..1cf3b2651c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -1,79 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; -using System.Linq; using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Utils; -using osu.Framework.Testing; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Tests.Mods; -using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneModSettings : OsuManualInputManagerTestScene { - private TestModSelectOverlay modSelect; - - private readonly Mod testCustomisableMod = new TestCustomisableModRuleset.TestModCustomisable1(); - - private readonly Mod testCustomisableAutoOpenMod = new TestCustomisableModRuleset.TestModCustomisable2(); - - [SetUp] - public void SetUp() => Schedule(() => - { - SelectedMods.Value = Array.Empty(); - Ruleset.Value = TestCustomisableModRuleset.CreateTestRulesetInfo(); - }); - - [Test] - public void TestButtonShowsOnCustomisableMod() - { - createModSelect(); - openModSelect(); - - AddAssert("button disabled", () => !modSelect.CustomiseButton.Enabled.Value); - AddUntilStep("wait for button load", () => modSelect.ButtonsLoaded); - AddStep("select mod", () => modSelect.SelectMod(testCustomisableMod)); - AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); - AddStep("open Customisation", () => modSelect.CustomiseButton.TriggerClick()); - AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableMod)); - AddAssert("controls hidden", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden); - } - - [Test] - public void TestButtonShowsOnModAlreadyAdded() - { - AddStep("set active mods", () => SelectedMods.Value = new List { testCustomisableMod }); - - createModSelect(); - - AddAssert("mods still active", () => SelectedMods.Value.Count == 1); - - openModSelect(); - AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); - } - - [Test] - public void TestCustomisationMenuVisibility() - { - createModSelect(); - openModSelect(); - - AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden); - AddStep("select mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod)); - AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.State.Value == Visibility.Visible); - AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod)); - AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden); - } - [Test] public void TestModSettingsUnboundWhenCopied() { @@ -109,60 +45,5 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("original has new value", () => Precision.AlmostEquals(2.0, ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value)); AddAssert("copy has original value", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)copy.Mods[0]).SpeedChange.Value)); } - - [Test] - public void TestCustomisationMenuNoClickthrough() - { - createModSelect(); - openModSelect(); - - AddStep("change mod settings menu width to full screen", () => modSelect.SetModSettingsWidth(1.0f)); - AddStep("select cm2", () => modSelect.SelectMod(testCustomisableAutoOpenMod)); - AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.State.Value == Visibility.Visible); - AddStep("hover over mod behind settings menu", () => InputManager.MoveMouseTo(modSelect.GetModButton(testCustomisableMod))); - AddAssert("Mod is not considered hovered over", () => !modSelect.GetModButton(testCustomisableMod).IsHovered); - AddStep("left click mod", () => InputManager.Click(MouseButton.Left)); - AddAssert("only cm2 is active", () => SelectedMods.Value.Count == 1); - AddStep("right click mod", () => InputManager.Click(MouseButton.Right)); - AddAssert("only cm2 is active", () => SelectedMods.Value.Count == 1); - } - - private void createModSelect() - { - AddStep("create mod select", () => - { - Child = modSelect = new TestModSelectOverlay - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - SelectedMods = { BindTarget = SelectedMods } - }; - }); - } - - private void openModSelect() - { - AddStep("open", () => modSelect.Show()); - AddUntilStep("wait for ready", () => modSelect.State.Value == Visibility.Visible && modSelect.ButtonsLoaded); - } - - private class TestModSelectOverlay : UserModSelectOverlay - { - public new VisibilityContainer ModSettingsContainer => base.ModSettingsContainer; - public new TriangleButton CustomiseButton => base.CustomiseButton; - - public bool ButtonsLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded); - - public ModButton GetModButton(Mod mod) - { - return ModSectionsContainer.ChildrenOfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())); - } - - public void SelectMod(Mod mod) => - GetModButton(mod).SelectNext(1); - - public void SetModSettingsWidth(float newWidth) => - ModSettingsContainer.Parent.Width = newWidth; - } } } diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs deleted file mode 100644 index 6e2cb40596..0000000000 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// 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.Cursor; -using osu.Game.Rulesets.Mods; -using osu.Game.Utils; -using osuTK; - -namespace osu.Game.Overlays.Mods -{ - public class IncompatibilityDisplayingModButton : ModButton - { - private readonly CompositeDrawable incompatibleIcon; - - [Resolved] - private Bindable> selectedMods { get; set; } - - public IncompatibilityDisplayingModButton(Mod mod) - : base(mod) - { - ButtonContent.Add(incompatibleIcon = new IncompatibleIcon - { - Anchor = Anchor.BottomRight, - Origin = Anchor.Centre, - Position = new Vector2(-13), - }); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - selectedMods.BindValueChanged(_ => Scheduler.AddOnce(updateCompatibility), true); - } - - protected override void DisplayMod(Mod mod) - { - base.DisplayMod(mod); - - Scheduler.AddOnce(updateCompatibility); - } - - private void updateCompatibility() - { - var m = SelectedMod ?? Mods.First(); - - bool isIncompatible = false; - - if (selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(m)) - isIncompatible = !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(m)); - - if (isIncompatible) - incompatibleIcon.Show(); - else - incompatibleIcon.Hide(); - } - - public override ITooltip GetCustomTooltip() => new IncompatibilityDisplayingTooltip(); - } -} diff --git a/osu.Game/Overlays/Mods/UserModSelectOverlay.cs b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs deleted file mode 100644 index 161f89c2eb..0000000000 --- a/osu.Game/Overlays/Mods/UserModSelectOverlay.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Overlays.Mods -{ - public class UserModSelectOverlay : ModSelectOverlay - { - protected override void OnModSelected(Mod mod) - { - base.OnModSelected(mod); - - foreach (var section in ModSectionsContainer.Children) - section.DeselectTypes(mod.IncompatibleMods, true, mod); - } - - protected override ModSection CreateModSection(ModType type) => new UserModSection(type); - - private class UserModSection : ModSection - { - public UserModSection(ModType type) - : base(type) - { - } - - protected override ModButton CreateModButton(Mod mod) => new IncompatibilityDisplayingModButton(mod); - } - } -} From 128468e13d87aa8f2ed54ddb4264732b96c2b1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:52:30 +0200 Subject: [PATCH 1005/1276] Remove old base mod select overlay --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 532 --------------------- 1 file changed, 532 deletions(-) delete mode 100644 osu.Game/Overlays/Mods/ModSelectOverlay.cs diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs deleted file mode 100644 index cf57322594..0000000000 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using JetBrains.Annotations; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Input.Bindings; -using osu.Game.Resources.Localisation.Web; -using osu.Game.Rulesets.Mods; -using osu.Game.Screens; -using osu.Game.Utils; -using osuTK; -using osuTK.Graphics; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods -{ - public abstract class ModSelectOverlay : WaveOverlayContainer - { - public const float HEIGHT = 510; - - protected readonly TriangleButton DeselectAllButton; - protected readonly TriangleButton CustomiseButton; - protected readonly TriangleButton CloseButton; - - protected readonly FillFlowContainer FooterContainer; - - protected override bool BlockNonPositionalInput => false; - - protected override bool DimMainContent => false; - - /// - /// Whether s underneath the same instance should appear as stacked buttons. - /// - protected virtual bool Stacked => true; - - /// - /// Whether configurable s can be configured by the local user. - /// - protected virtual bool AllowConfiguration => true; - - [NotNull] - private Func isValidMod = m => true; - - /// - /// A function that checks whether a given mod is selectable. - /// - [NotNull] - public Func IsValidMod - { - get => isValidMod; - set - { - isValidMod = value ?? throw new ArgumentNullException(nameof(value)); - updateAvailableMods(); - } - } - - protected readonly FillFlowContainer ModSectionsContainer; - - protected readonly ModSettingsContainer ModSettingsContainer; - - [Cached] - public readonly Bindable> SelectedMods = new Bindable>(Array.Empty()); - - private Bindable>> availableMods; - - protected Color4 LowMultiplierColour; - protected Color4 HighMultiplierColour; - - private const float content_width = 0.8f; - private const float footer_button_spacing = 20; - - private Sample sampleOn, sampleOff; - - protected ModSelectOverlay() - { - Waves.FirstWaveColour = Color4Extensions.FromHex(@"19b0e2"); - Waves.SecondWaveColour = Color4Extensions.FromHex(@"2280a2"); - Waves.ThirdWaveColour = Color4Extensions.FromHex(@"005774"); - Waves.FourthWaveColour = Color4Extensions.FromHex(@"003a4e"); - - RelativeSizeAxes = Axes.X; - Height = HEIGHT; - - Padding = new MarginPadding { Horizontal = -OsuScreen.HORIZONTAL_OVERFLOW_PADDING }; - - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(36, 50, 68, 255) - }, - new Triangles - { - TriangleScale = 5, - RelativeSizeAxes = Axes.Both, - ColourLight = new Color4(53, 66, 82, 255), - ColourDark = new Color4(41, 54, 70, 255), - }, - }, - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RowDimensions = new[] - { - new Dimension(GridSizeMode.Absolute, 90), - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(10).Opacity(100), - }, - new FillFlowContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Width = content_width, - Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = @"Gameplay Mods", - Font = OsuFont.GetFont(size: 22, weight: FontWeight.Bold), - Shadow = true, - Margin = new MarginPadding - { - Bottom = 4, - }, - }, - new OsuTextFlowContainer(text => - { - text.Font = text.Font.With(size: 18); - text.Shadow = true; - }) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Text = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play.\nOthers are just for fun.", - }, - }, - }, - }, - }, - }, - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - // Body - new OsuScrollContainer - { - ScrollbarVisible = false, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Vertical = 10, - Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING - }, - Children = new Drawable[] - { - ModSectionsContainer = new FillFlowContainer - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0f, 10f), - Width = content_width, - LayoutDuration = 200, - LayoutEasing = Easing.OutQuint, - Children = new[] - { - CreateModSection(ModType.DifficultyReduction).With(s => - { - s.ToggleKeys = new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }; - s.Action = modButtonPressed; - }), - CreateModSection(ModType.DifficultyIncrease).With(s => - { - s.ToggleKeys = new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }; - s.Action = modButtonPressed; - }), - CreateModSection(ModType.Automation).With(s => - { - s.ToggleKeys = new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }; - s.Action = modButtonPressed; - }), - CreateModSection(ModType.Conversion).With(s => - { - s.Action = modButtonPressed; - }), - CreateModSection(ModType.Fun).With(s => - { - s.Action = modButtonPressed; - }), - } - }, - } - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Padding = new MarginPadding(30), - Width = 0.3f, - Children = new Drawable[] - { - ModSettingsContainer = new ModSettingsContainer - { - Alpha = 0, - SelectedMods = { BindTarget = SelectedMods }, - }, - } - }, - } - }, - }, - new Drawable[] - { - new Container - { - Name = "Footer content", - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(172, 20, 116, 255), - Alpha = 0.5f, - }, - FooterContainer = new FillFlowContainer - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - RelativePositionAxes = Axes.X, - Width = content_width, - Spacing = new Vector2(footer_button_spacing, footer_button_spacing / 2), - Padding = new MarginPadding - { - Vertical = 15, - Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING - }, - Children = new[] - { - DeselectAllButton = new TriangleButton - { - Width = 180, - Text = "Deselect All", - Action = deselectAll, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - CustomiseButton = new TriangleButton - { - Width = 180, - Text = "Customisation", - Action = () => ModSettingsContainer.ToggleVisibility(), - Enabled = { Value = false }, - Alpha = AllowConfiguration ? 1 : 0, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - CloseButton = new TriangleButton - { - Width = 180, - Text = CommonStrings.ButtonsClose, - Action = Hide, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - } - } - }, - } - }, - }, - }, - }; - - ((IBindable)CustomiseButton.Enabled).BindTo(ModSettingsContainer.HasSettingsForSelection); - } - - [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, OsuGameBase osu) - { - availableMods = osu.AvailableMods.GetBoundCopy(); - - sampleOn = audio.Samples.Get(@"UI/check-on"); - sampleOff = audio.Samples.Get(@"UI/check-off"); - } - - private void deselectAll() - { - foreach (var section in ModSectionsContainer.Children) - section.DeselectAll(); - - refreshSelectedMods(); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - availableMods.BindValueChanged(_ => updateAvailableMods(), true); - - // intentionally bound after the above line to avoid a potential update feedback cycle. - // i haven't actually observed this happening but as updateAvailableMods() changes the selection it is plausible. - SelectedMods.BindValueChanged(_ => updateSelectedButtons()); - } - - protected override void PopOut() - { - base.PopOut(); - - foreach (var section in ModSectionsContainer) - { - section.FlushPendingSelections(); - } - - FooterContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - FooterContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - - foreach (var section in ModSectionsContainer.Children) - { - section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - } - } - - protected override void PopIn() - { - base.PopIn(); - - FooterContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); - FooterContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - - foreach (var section in ModSectionsContainer.Children) - { - section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - } - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - // don't absorb control as ToolbarRulesetSelector uses control + number to navigate - if (e.ControlPressed) return false; - - switch (e.Key) - { - case Key.Number1: - DeselectAllButton.TriggerClick(); - return true; - - case Key.Number2: - CloseButton.TriggerClick(); - return true; - } - - return base.OnKeyDown(e); - } - - public override bool OnPressed(KeyBindingPressEvent e) => false; // handled by back button - - private void updateAvailableMods() - { - if (availableMods?.Value == null) - return; - - foreach (var section in ModSectionsContainer.Children) - { - IEnumerable modEnumeration = availableMods.Value[section.ModType]; - - if (!Stacked) - modEnumeration = ModUtils.FlattenMods(modEnumeration); - - section.Mods = modEnumeration.Select(getValidModOrNull).Where(m => m != null).Select(m => m.DeepClone()); - } - - updateSelectedButtons(); - OnAvailableModsChanged(); - } - - /// - /// Returns a valid form of a given if possible, or null otherwise. - /// - /// - /// This is a recursive process during which any invalid mods are culled while preserving structures where possible. - /// - /// The to check. - /// A valid form of if exists, or null otherwise. - [CanBeNull] - private Mod getValidModOrNull([NotNull] Mod mod) - { - if (!(mod is MultiMod multi)) - return IsValidMod(mod) ? mod : null; - - var validSubset = multi.Mods.Select(getValidModOrNull).Where(m => m != null).ToArray(); - - if (validSubset.Length == 0) - return null; - - return validSubset.Length == 1 ? validSubset[0] : new MultiMod(validSubset); - } - - private void updateSelectedButtons() - { - // Enumeration below may update the bindable list. - var selectedMods = SelectedMods.Value.ToList(); - - foreach (var section in ModSectionsContainer.Children) - section.UpdateSelectedButtons(selectedMods); - } - - private void modButtonPressed(Mod selectedMod) - { - if (selectedMod != null) - { - if (State.Value == Visibility.Visible) - Scheduler.AddOnce(playSelectedSound); - - OnModSelected(selectedMod); - - if (selectedMod.RequiresConfiguration && AllowConfiguration) - ModSettingsContainer.Show(); - } - else - { - if (State.Value == Visibility.Visible) - Scheduler.AddOnce(playDeselectedSound); - } - - refreshSelectedMods(); - } - - private void playSelectedSound() => sampleOn?.Play(); - private void playDeselectedSound() => sampleOff?.Play(); - - /// - /// Invoked after has changed. - /// - protected virtual void OnAvailableModsChanged() - { - } - - /// - /// Invoked when a new has been selected. - /// - /// The that has been selected. - protected virtual void OnModSelected(Mod mod) - { - } - - private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); - - /// - /// Creates a that groups s with the same . - /// - /// The of s in the section. - /// The . - protected virtual ModSection CreateModSection(ModType type) => new ModSection(type); - - #region Disposal - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - availableMods?.UnbindAll(); - SelectedMods?.UnbindAll(); - } - - #endregion - } -} From dfd97701d7d17ca559f0d681d8a669cbe2cafb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:53:42 +0200 Subject: [PATCH 1006/1276] Remove old mod settings container & related controls --- osu.Game/Overlays/Mods/ModControlSection.cs | 54 --------- .../Overlays/Mods/ModSettingsContainer.cs | 111 ------------------ 2 files changed, 165 deletions(-) delete mode 100644 osu.Game/Overlays/Mods/ModControlSection.cs delete mode 100644 osu.Game/Overlays/Mods/ModSettingsContainer.cs diff --git a/osu.Game/Overlays/Mods/ModControlSection.cs b/osu.Game/Overlays/Mods/ModControlSection.cs deleted file mode 100644 index 10b3bc7c2b..0000000000 --- a/osu.Game/Overlays/Mods/ModControlSection.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using osuTK; - -namespace osu.Game.Overlays.Mods -{ - public class ModControlSection : CompositeDrawable - { - protected FillFlowContainer FlowContent; - - public readonly Mod Mod; - - public ModControlSection(Mod mod, IEnumerable modControls) - { - Mod = mod; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - FlowContent = new FillFlowContainer - { - Margin = new MarginPadding { Top = 30 }, - Spacing = new Vector2(0, 5), - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - ChildrenEnumerable = modControls - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AddRangeInternal(new Drawable[] - { - new OsuSpriteText - { - Text = Mod.Name, - Font = OsuFont.GetFont(weight: FontWeight.Bold), - Colour = colours.Yellow, - }, - FlowContent - }); - } - } -} diff --git a/osu.Game/Overlays/Mods/ModSettingsContainer.cs b/osu.Game/Overlays/Mods/ModSettingsContainer.cs deleted file mode 100644 index 64d65cab3b..0000000000 --- a/osu.Game/Overlays/Mods/ModSettingsContainer.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// 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; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Game.Configuration; -using osu.Game.Graphics.Containers; -using osu.Game.Rulesets.Mods; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Mods -{ - public class ModSettingsContainer : VisibilityContainer - { - public readonly IBindable> SelectedMods = new Bindable>(Array.Empty()); - - public IBindable HasSettingsForSelection => hasSettingsForSelection; - - private readonly Bindable hasSettingsForSelection = new Bindable(); - - private readonly FillFlowContainer modSettingsContent; - - private readonly Container content; - - private const double transition_duration = 400; - - public ModSettingsContainer() - { - RelativeSizeAxes = Axes.Both; - - Child = content = new Container - { - Masking = true, - CornerRadius = 10, - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Both, - X = 1, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(0, 0, 0, 192) - }, - new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - ScrollbarVisible = false, - Child = modSettingsContent = new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0f, 10f), - Padding = new MarginPadding(20), - } - } - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - SelectedMods.BindValueChanged(modsChanged, true); - } - - private void modsChanged(ValueChangedEvent> mods) - { - modSettingsContent.Clear(); - - foreach (var mod in mods.NewValue) - { - var settings = mod.CreateSettingsControls().ToList(); - if (settings.Count > 0) - modSettingsContent.Add(new ModControlSection(mod, settings)); - } - - bool hasSettings = modSettingsContent.Count > 0; - - if (!hasSettings) - Hide(); - - hasSettingsForSelection.Value = hasSettings; - } - - protected override bool OnMouseDown(MouseDownEvent e) => true; - protected override bool OnHover(HoverEvent e) => true; - - protected override void PopIn() - { - this.FadeIn(transition_duration, Easing.OutQuint); - content.MoveToX(0, transition_duration, Easing.OutQuint); - } - - protected override void PopOut() - { - this.FadeOut(transition_duration, Easing.OutQuint); - content.MoveToX(1, transition_duration, Easing.OutQuint); - } - } -} From 8b0ece1c09df40506f813a125bbd9c2f78546eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:55:54 +0200 Subject: [PATCH 1007/1276] Remove old mod section --- osu.Game/Overlays/Mods/ModSection.cs | 261 --------------------------- 1 file changed, 261 deletions(-) delete mode 100644 osu.Game/Overlays/Mods/ModSection.cs diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs deleted file mode 100644 index a70191a864..0000000000 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osuTK; -using osuTK.Input; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using System; -using System.Linq; -using System.Collections.Generic; -using System.Threading; -using Humanizer; -using osu.Framework.Input.Events; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Mods -{ - public class ModSection : CompositeDrawable - { - private readonly Drawable header; - - public FillFlowContainer ButtonsContainer { get; } - - protected IReadOnlyList Buttons { get; private set; } = Array.Empty(); - - public Action Action; - - public Key[] ToggleKeys; - - public readonly ModType ModType; - - public IEnumerable SelectedMods => Buttons.Select(b => b.SelectedMod).Where(m => m != null); - - private CancellationTokenSource modsLoadCts; - - protected bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0; - - /// - /// True when all mod icons have completed loading. - /// - public bool ModIconsLoaded { get; private set; } = true; - - public IEnumerable Mods - { - set - { - var modContainers = value.Select(m => - { - if (m == null) - return new ModButtonEmpty(); - - return CreateModButton(m).With(b => - { - b.SelectionChanged = mod => - { - ModButtonStateChanged(mod); - Action?.Invoke(mod); - }; - }); - }).ToArray(); - - modsLoadCts?.Cancel(); - - if (modContainers.Length == 0) - { - ModIconsLoaded = true; - header.Hide(); - Hide(); - return; - } - - ModIconsLoaded = false; - - LoadComponentsAsync(modContainers, c => - { - ModIconsLoaded = true; - ButtonsContainer.ChildrenEnumerable = c; - }, (modsLoadCts = new CancellationTokenSource()).Token); - - Buttons = modContainers.OfType().ToArray(); - - header.FadeIn(200); - this.FadeIn(200); - } - } - - protected virtual void ModButtonStateChanged(Mod mod) - { - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - if (e.ControlPressed) return false; - - if (ToggleKeys != null) - { - int index = Array.IndexOf(ToggleKeys, e.Key); - if (index > -1 && index < Buttons.Count) - Buttons[index].SelectNext(e.ShiftPressed ? -1 : 1); - } - - return base.OnKeyDown(e); - } - - private const double initial_multiple_selection_delay = 120; - - private double selectionDelay = initial_multiple_selection_delay; - private double lastSelection; - - private readonly Queue pendingSelectionOperations = new Queue(); - - protected override void Update() - { - base.Update(); - - if (selectionDelay == initial_multiple_selection_delay || Time.Current - lastSelection >= selectionDelay) - { - if (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) - { - dequeuedAction(); - - // each time we play an animation, we decrease the time until the next animation (to ramp the visual and audible elements). - selectionDelay = Math.Max(30, selectionDelay * 0.8f); - lastSelection = Time.Current; - } - else - { - // reset the selection delay after all animations have been completed. - // this will cause the next action to be immediately performed. - selectionDelay = initial_multiple_selection_delay; - } - } - } - - /// - /// Selects all mods. - /// - public void SelectAll() - { - pendingSelectionOperations.Clear(); - - foreach (var button in Buttons.Where(b => !b.Selected)) - pendingSelectionOperations.Enqueue(() => button.SelectAt(0)); - } - - /// - /// Deselects all mods. - /// - public void DeselectAll() - { - pendingSelectionOperations.Clear(); - DeselectTypes(Buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); - } - - /// - /// Deselect one or more mods in this section. - /// - /// The types of s which should be deselected. - /// Whether the deselection should happen immediately. Should only be used when required to ensure correct selection flow. - /// If this deselection is triggered by a user selection, this should contain the newly selected type. This type will never be deselected, even if it matches one provided in . - public void DeselectTypes(IEnumerable modTypes, bool immediate = false, Mod newSelection = null) - { - foreach (var button in Buttons) - { - if (button.SelectedMod == null) continue; - - if (button.SelectedMod == newSelection) - continue; - - foreach (var type in modTypes) - { - if (type.IsInstanceOfType(button.SelectedMod)) - { - if (immediate) - button.Deselect(); - else - pendingSelectionOperations.Enqueue(button.Deselect); - } - } - } - } - - /// - /// Updates all buttons with the given list of selected mods. - /// - /// The new list of selected mods to select. - public void UpdateSelectedButtons(IReadOnlyList newSelectedMods) - { - foreach (var button in Buttons) - updateButtonSelection(button, newSelectedMods); - } - - private void updateButtonSelection(ModButton button, IReadOnlyList newSelectedMods) - { - foreach (var mod in newSelectedMods) - { - int index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); - if (index < 0) - continue; - - var buttonMod = button.Mods[index]; - - // as this is likely coming from an external change, ensure the settings of the mod are in sync. - buttonMod.CopyFrom(mod); - - button.SelectAt(index, false); - return; - } - - button.Deselect(); - } - - public ModSection(ModType type) - { - ModType = type; - - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; - - Origin = Anchor.TopCentre; - Anchor = Anchor.TopCentre; - - InternalChildren = new[] - { - header = CreateHeader(type.Humanize(LetterCasing.Title)), - ButtonsContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Spacing = new Vector2(50f, 0f), - Margin = new MarginPadding - { - Top = 20, - }, - AlwaysPresent = true - }, - }; - } - - protected virtual Drawable CreateHeader(string text) => new OsuSpriteText - { - Font = OsuFont.GetFont(weight: FontWeight.Bold), - Text = text - }; - - protected virtual ModButton CreateModButton(Mod mod) => new ModButton(mod); - - /// - /// Run any delayed selections (due to animation) immediately to leave mods in a good (final) state. - /// - public void FlushPendingSelections() - { - while (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) - dequeuedAction(); - } - } -} From 33634cba1eb7badf4821c04a7a21954b55d7f11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:58:22 +0200 Subject: [PATCH 1008/1276] Remove old mod buttons --- .../UserInterface/TestSceneModButton.cs | 64 ---- osu.Game/Overlays/Mods/IncompatibleIcon.cs | 64 ---- osu.Game/Overlays/Mods/ModButton.cs | 319 ------------------ osu.Game/Overlays/Mods/ModButtonEmpty.cs | 20 -- 4 files changed, 467 deletions(-) delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs delete mode 100644 osu.Game/Overlays/Mods/IncompatibleIcon.cs delete mode 100644 osu.Game/Overlays/Mods/ModButton.cs delete mode 100644 osu.Game/Overlays/Mods/ModButtonEmpty.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs deleted file mode 100644 index fdc21d80ff..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Game.Overlays.Mods; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Tests.Visual.UserInterface -{ - public class TestSceneModButton : OsuTestScene - { - public TestSceneModButton() - { - Children = new Drawable[] - { - new ModButton(new MultiMod(new TestMod1(), new TestMod2(), new TestMod3(), new TestMod4())) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - } - }; - } - - private class TestMod1 : TestMod - { - public override string Name => "Test mod 1"; - - public override string Acronym => "M1"; - } - - private class TestMod2 : TestMod - { - public override string Name => "Test mod 2"; - - public override string Acronym => "M2"; - - public override IconUsage? Icon => FontAwesome.Solid.Exclamation; - } - - private class TestMod3 : TestMod - { - public override string Name => "Test mod 3"; - - public override string Acronym => "M3"; - - public override IconUsage? Icon => FontAwesome.Solid.ArrowRight; - } - - private class TestMod4 : TestMod - { - public override string Name => "Test mod 4"; - - public override string Acronym => "M4"; - } - - private abstract class TestMod : Mod, IApplicableMod - { - public override double ScoreMultiplier => 1.0; - - public override string Description => "This is a test mod."; - } - } -} diff --git a/osu.Game/Overlays/Mods/IncompatibleIcon.cs b/osu.Game/Overlays/Mods/IncompatibleIcon.cs deleted file mode 100644 index df134fe4a4..0000000000 --- a/osu.Game/Overlays/Mods/IncompatibleIcon.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Localisation; -using osu.Game.Graphics; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Mods -{ - public class IncompatibleIcon : VisibilityContainer, IHasTooltip - { - private Circle circle; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Size = new Vector2(20); - - State.Value = Visibility.Hidden; - Alpha = 0; - - InternalChildren = new Drawable[] - { - circle = new Circle - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray4, - }, - new SpriteIcon - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(0.6f), - Icon = FontAwesome.Solid.Slash, - Colour = Color4.White, - Shadow = true, - } - }; - } - - protected override void PopIn() - { - this.FadeIn(200, Easing.OutQuint); - circle.FlashColour(Color4.Red, 500, Easing.OutQuint); - this.ScaleTo(1.8f).Then().ScaleTo(1, 500, Easing.OutQuint); - } - - protected override void PopOut() - { - this.FadeOut(200, Easing.OutQuint); - this.ScaleTo(0.8f, 200, Easing.In); - } - - public LocalisableString TooltipText => "Incompatible with current selected mods"; - } -} diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs deleted file mode 100644 index 979e2c8da3..0000000000 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osuTK; -using osuTK.Graphics; -using osuTK.Input; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; -using System; -using System.Linq; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Input.Events; -using osu.Framework.Localisation; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Mods -{ - /// - /// Represents a clickable button which can cycle through one of more mods. - /// - public class ModButton : ModButtonEmpty, IHasCustomTooltip - { - private ModIcon foregroundIcon; - private ModIcon backgroundIcon; - private readonly SpriteText text; - private readonly Container iconsContainer; - - /// - /// Fired when the selection changes. - /// - public Action SelectionChanged; - - public LocalisableString TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty; - - private const Easing mod_switch_easing = Easing.InOutSine; - private const double mod_switch_duration = 120; - - // A selected index of -1 means not selected. - private int selectedIndex = -1; - - /// - /// Change the selected mod index of this button. - /// - /// The new index. - /// Whether any settings applied to the mod should be reset on selection. - /// Whether the selection changed. - private bool changeSelectedIndex(int newIndex, bool resetSettings = true) - { - if (newIndex == selectedIndex) return false; - - int direction = newIndex < selectedIndex ? -1 : 1; - - bool beforeSelected = Selected; - - Mod previousSelection = SelectedMod ?? Mods[0]; - - if (newIndex >= Mods.Length) - newIndex = -1; - else if (newIndex < -1) - newIndex = Mods.Length - 1; - - if (newIndex >= 0 && !Mods[newIndex].HasImplementation) - return false; - - selectedIndex = newIndex; - - Mod newSelection = SelectedMod ?? Mods[0]; - - if (resetSettings) - newSelection.ResetSettingsToDefaults(); - - Schedule(() => - { - if (beforeSelected != Selected) - { - iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); - iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); - } - - if (previousSelection != newSelection) - { - const float rotate_angle = 16; - - foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing); - backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); - - backgroundIcon.Mod = newSelection; - - using (BeginDelayedSequence(mod_switch_duration)) - { - foregroundIcon - .RotateTo(-rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - backgroundIcon - .RotateTo(rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - Schedule(() => DisplayMod(newSelection)); - } - } - - foregroundIcon.Selected.Value = Selected; - }); - - SelectionChanged?.Invoke(SelectedMod); - - return true; - } - - public bool Selected => selectedIndex != -1; - - private Color4 selectedColour; - - public Color4 SelectedColour - { - get => selectedColour; - set - { - if (value == selectedColour) return; - - selectedColour = value; - if (Selected) foregroundIcon.Colour = value; - } - } - - private Mod mod; - - protected readonly Container ButtonContent; - - public Mod Mod - { - get => mod; - set - { - mod = value; - - if (mod == null) - { - Mods = Array.Empty(); - Alpha = 0; - } - else - { - Mods = (mod as MultiMod)?.Mods ?? new[] { mod }; - Alpha = 1; - } - - createIcons(); - - if (Mods.Length > 0) - { - DisplayMod(Mods[0]); - } - } - } - - public Mod[] Mods { get; private set; } - - public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex); - - protected override bool OnMouseDown(MouseDownEvent e) - { - ButtonContent.ScaleTo(0.9f, 800, Easing.Out); - return base.OnMouseDown(e); - } - - protected override void OnMouseUp(MouseUpEvent e) - { - ButtonContent.ScaleTo(1, 500, Easing.OutElastic); - - // only trigger the event if we are inside the area of the button - if (Contains(e.ScreenSpaceMousePosition)) - { - switch (e.Button) - { - case MouseButton.Right: - SelectNext(-1); - break; - } - } - } - - protected override bool OnClick(ClickEvent e) - { - SelectNext(1); - - return true; - } - - /// - /// Select the next available mod in a specified direction. - /// - /// 1 for forwards, -1 for backwards. - public void SelectNext(int direction) - { - int start = selectedIndex + direction; - // wrap around if we are at an extremity. - if (start >= Mods.Length) - start = -1; - else if (start < -1) - start = Mods.Length - 1; - - for (int i = start; i < Mods.Length && i >= 0; i += direction) - { - if (SelectAt(i)) - return; - } - - Deselect(); - } - - /// - /// Select the mod at the provided index. - /// - /// The index to select. - /// Whether any settings applied to the mod should be reset on selection. - /// Whether the selection changed. - public bool SelectAt(int index, bool resetSettings = true) - { - if (!Mods[index].HasImplementation) return false; - - changeSelectedIndex(index, resetSettings); - return true; - } - - public void Deselect() => changeSelectedIndex(-1); - - protected virtual void DisplayMod(Mod mod) - { - if (backgroundIcon != null) - backgroundIcon.Mod = foregroundIcon.Mod; - foregroundIcon.Mod = mod; - text.Text = mod.Name; - Colour = mod.HasImplementation ? Color4.White : Color4.Gray; - } - - private void createIcons() - { - iconsContainer.Clear(); - - if (Mods.Length > 1) - { - iconsContainer.AddRange(new[] - { - backgroundIcon = new ModIcon(Mods[1], false) - { - Origin = Anchor.BottomRight, - Anchor = Anchor.BottomRight, - Position = new Vector2(1.5f), - }, - foregroundIcon = new ModIcon(Mods[0], false) - { - Origin = Anchor.BottomRight, - Anchor = Anchor.BottomRight, - Position = new Vector2(-1.5f), - }, - }); - } - else - { - iconsContainer.Add(foregroundIcon = new ModIcon(Mod, false) - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - }); - } - } - - public ModButton(Mod mod) - { - Children = new Drawable[] - { - new Container - { - Size = new Vector2(77f, 80f), - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Children = new Drawable[] - { - ButtonContent = new Container - { - Children = new Drawable[] - { - iconsContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - }, - }, - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - } - } - }, - text = new OsuSpriteText - { - Y = 75, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Font = OsuFont.GetFont(size: 18) - }, - new HoverSounds() - }; - Mod = mod; - } - - public virtual ITooltip GetCustomTooltip() => new ModButtonTooltip(); - - public Mod TooltipContent => SelectedMod ?? Mods.FirstOrDefault(); - } -} diff --git a/osu.Game/Overlays/Mods/ModButtonEmpty.cs b/osu.Game/Overlays/Mods/ModButtonEmpty.cs deleted file mode 100644 index 03afe5adba..0000000000 --- a/osu.Game/Overlays/Mods/ModButtonEmpty.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osuTK; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Overlays.Mods -{ - /// - /// A mod button used exclusively for providing an empty space the size of a mod button. - /// - public class ModButtonEmpty : Container - { - public ModButtonEmpty() - { - Size = new Vector2(100f); - AlwaysPresent = true; - } - } -} From c4c7556fb24407b7c97067729a6bd6c894a71eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 22:07:24 +0200 Subject: [PATCH 1009/1276] Make remains of `TestSceneModSettings` non-visual As they're not really testing anything visual. --- osu.Game.Tests/Mods/ModSettingsTest.cs | 36 ++++++++++++++ .../UserInterface/TestSceneModSettings.cs | 49 ------------------- 2 files changed, 36 insertions(+), 49 deletions(-) create mode 100644 osu.Game.Tests/Mods/ModSettingsTest.cs delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs diff --git a/osu.Game.Tests/Mods/ModSettingsTest.cs b/osu.Game.Tests/Mods/ModSettingsTest.cs new file mode 100644 index 0000000000..b9ea1f2567 --- /dev/null +++ b/osu.Game.Tests/Mods/ModSettingsTest.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Tests.Mods +{ + public class ModSettingsTest + { + [Test] + public void TestModSettingsUnboundWhenCopied() + { + var original = new OsuModDoubleTime(); + var copy = (OsuModDoubleTime)original.DeepClone(); + + original.SpeedChange.Value = 2; + + Assert.That(original.SpeedChange.Value, Is.EqualTo(2.0)); + Assert.That(copy.SpeedChange.Value, Is.EqualTo(1.5)); + } + + [Test] + public void TestMultiModSettingsUnboundWhenCopied() + { + var original = new MultiMod(new OsuModDoubleTime()); + var copy = (MultiMod)original.DeepClone(); + + ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value = 2; + + Assert.That(((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value, Is.EqualTo(2.0)); + Assert.That(((OsuModDoubleTime)copy.Mods[0]).SpeedChange.Value, Is.EqualTo(1.5)); + } + } +} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs deleted file mode 100644 index 1cf3b2651c..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Framework.Utils; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; - -namespace osu.Game.Tests.Visual.UserInterface -{ - public class TestSceneModSettings : OsuManualInputManagerTestScene - { - [Test] - public void TestModSettingsUnboundWhenCopied() - { - OsuModDoubleTime original = null; - OsuModDoubleTime copy = null; - - AddStep("create mods", () => - { - original = new OsuModDoubleTime(); - copy = (OsuModDoubleTime)original.DeepClone(); - }); - - AddStep("change property", () => original.SpeedChange.Value = 2); - - AddAssert("original has new value", () => Precision.AlmostEquals(2.0, original.SpeedChange.Value)); - AddAssert("copy has original value", () => Precision.AlmostEquals(1.5, copy.SpeedChange.Value)); - } - - [Test] - public void TestMultiModSettingsUnboundWhenCopied() - { - MultiMod original = null; - MultiMod copy = null; - - AddStep("create mods", () => - { - original = new MultiMod(new OsuModDoubleTime()); - copy = (MultiMod)original.DeepClone(); - }); - - AddStep("change property", () => ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value = 2); - - AddAssert("original has new value", () => Precision.AlmostEquals(2.0, ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value)); - AddAssert("copy has original value", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)copy.Mods[0]).SpeedChange.Value)); - } - } -} From 76c63f1d0a0a2bda6eb56642c0f417ba8656297c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 22:29:57 +0200 Subject: [PATCH 1010/1276] Rename `ModSelect{Screen -> Overlay}` in place of removed old design --- .../TestSceneFreeModSelectScreen.cs | 18 ++-- .../Multiplayer/TestSceneMultiplayer.cs | 2 +- .../TestSceneMultiplayerMatchSongSelect.cs | 2 +- .../TestSceneMultiplayerMatchSubScreen.cs | 2 +- .../Navigation/TestSceneScreenNavigation.cs | 2 +- .../SongSelect/TestScenePlaySongSelect.cs | 2 +- .../UserInterface/TestSceneModSelectScreen.cs | 96 +++++++++---------- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- ...ModSelectScreen.cs => ModSelectOverlay.cs} | 4 +- ...electScreen.cs => UserModSelectOverlay.cs} | 4 +- ...electScreen.cs => FreeModSelectOverlay.cs} | 4 +- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 +- .../OnlinePlay/OnlinePlaySongSelect.cs | 6 +- osu.Game/Screens/Select/SongSelect.cs | 4 +- 14 files changed, 76 insertions(+), 76 deletions(-) rename osu.Game/Overlays/Mods/{ModSelectScreen.cs => ModSelectOverlay.cs} (99%) rename osu.Game/Overlays/Mods/{UserModSelectScreen.cs => UserModSelectOverlay.cs} (92%) rename osu.Game/Screens/OnlinePlay/{FreeModSelectScreen.cs => FreeModSelectOverlay.cs} (94%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs index 4eb14542ba..9a24a1c646 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneFreeModSelectScreen : MultiplayerTestScene { - private FreeModSelectScreen freeModSelectScreen; + private FreeModSelectOverlay freeModSelectOverlay; private readonly Bindable>> availableMods = new Bindable>>(); [BackgroundDependencyLoader] @@ -40,8 +40,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddToggleStep("toggle visibility", visible => { - if (freeModSelectScreen != null) - freeModSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden; + if (freeModSelectOverlay != null) + freeModSelectOverlay.State.Value = visible ? Visibility.Visible : Visibility.Hidden; }); } @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createFreeModSelect(); - AddStep("select difficulty adjust", () => freeModSelectScreen.SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); + AddStep("select difficulty adjust", () => freeModSelectOverlay.SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); AddWaitStep("wait some", 3); AddAssert("customisation area not expanded", () => this.ChildrenOfType().Single().Height == 0); } @@ -72,18 +72,18 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.MoveMouseTo(this.ChildrenOfType().Last()); InputManager.Click(MouseButton.Left); }); - AddUntilStep("all mods deselected", () => !freeModSelectScreen.SelectedMods.Value.Any()); + AddUntilStep("all mods deselected", () => !freeModSelectOverlay.SelectedMods.Value.Any()); } private void createFreeModSelect() { - AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen + AddStep("create free mod select screen", () => Child = freeModSelectOverlay = new FreeModSelectOverlay { State = { Value = Visibility.Visible } }); AddUntilStep("all column content loaded", - () => freeModSelectScreen.ChildrenOfType().Any() - && freeModSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); + () => freeModSelectOverlay.ChildrenOfType().Any() + && freeModSelectOverlay.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); } private bool assertAllAvailableModsSelected() @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var availableMod in allAvailableMods) { - if (freeModSelectScreen.SelectedMods.Value.All(selectedMod => selectedMod.GetType() != availableMod.GetType())) + if (freeModSelectOverlay.SelectedMods.Value.All(selectedMod => selectedMod.GetType() != availableMod.GetType())) return false; } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 97cb08e3c1..8e45d99eae 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -627,7 +627,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("invoke on back button", () => multiplayerComponents.OnBackButton()); - AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); + AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 061fe5715b..eacd80925d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void assertHasFreeModButton(Type type, bool hasButton = true) { AddAssert($"{type.ReadableName()} {(hasButton ? "displayed" : "not displayed")} in freemod overlay", - () => this.ChildrenOfType() + () => this.ChildrenOfType() .Single() .ChildrenOfType() .Where(panel => !panel.Filtered.Value) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 6173580f0b..ca79fa9cb8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -172,7 +172,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("mod select contents loaded", () => this.ChildrenOfType().Any() && this.ChildrenOfType().All(col => col.IsLoaded && col.ItemsLoaded)); AddUntilStep("mod select contains only double time mod", - () => this.ChildrenOfType() + () => this.ChildrenOfType() .SingleOrDefault()? .ChildrenOfType() .SingleOrDefault(panel => !panel.Filtered.Value)?.Mod is OsuModDoubleTime); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 9674ef7ae1..a3e0caedb9 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -568,7 +568,7 @@ namespace osu.Game.Tests.Visual.Navigation public class TestPlaySongSelect : PlaySongSelect { - public ModSelectScreen ModSelectOverlay => ModSelect; + public ModSelectOverlay ModSelectOverlay => ModSelect; public BeatmapOptionsOverlay BeatmapOptionsOverlay => BeatmapOptions; diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 083e24be7b..aad7f6b301 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -1008,7 +1008,7 @@ namespace osu.Game.Tests.Visual.SongSelect public WorkingBeatmap CurrentBeatmap => Beatmap.Value; public IWorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public new BeatmapCarousel Carousel => base.Carousel; - public new ModSelectScreen ModSelect => base.ModSelect; + public new ModSelectOverlay ModSelect => base.ModSelect; public new void PresentScore(ScoreInfo score) => base.PresentScore(score); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index fa7758df59..2e2ce6edd7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Resolved] private RulesetStore rulesetStore { get; set; } - private UserModSelectScreen modSelectScreen; + private UserModSelectOverlay modSelectOverlay; [SetUpSteps] public void SetUpSteps() @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void createScreen() { - AddStep("create screen", () => Child = modSelectScreen = new UserModSelectScreen + AddStep("create screen", () => Child = modSelectOverlay = new UserModSelectOverlay { RelativeSizeAxes = Axes.Both, State = { Value = Visibility.Visible }, @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestStateChange() { createScreen(); - AddStep("toggle state", () => modSelectScreen.ToggleVisibility()); + AddStep("toggle state", () => modSelectOverlay.ToggleVisibility()); } [Test] @@ -62,14 +62,14 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModAlternate(), new OsuModDaycore() }); createScreen(); - AddUntilStep("two panels active", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddUntilStep("two panels active", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); AddAssert("mod multiplier correct", () => { double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); - return Precision.AlmostEquals(multiplier, modSelectScreen.ChildrenOfType().Single().Current.Value); + return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); - AddAssert("setting items created", () => modSelectScreen.ChildrenOfType().Any()); + AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType().Any()); } [Test] @@ -77,14 +77,14 @@ namespace osu.Game.Tests.Visual.UserInterface { createScreen(); AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModAlternate(), new OsuModDaycore() }); - AddUntilStep("two panels active", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddUntilStep("two panels active", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); AddAssert("mod multiplier correct", () => { double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); - return Precision.AlmostEquals(multiplier, modSelectScreen.ChildrenOfType().Single().Current.Value); + return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); - AddAssert("setting items created", () => modSelectScreen.ChildrenOfType().Any()); + AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType().Any()); } [Test] @@ -139,7 +139,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("last column dimmed", () => !this.ChildrenOfType().Last().Active.Value); AddStep("request scroll to last column", () => { - var lastDimContainer = this.ChildrenOfType().Last(); + var lastDimContainer = this.ChildrenOfType().Last(); lastColumn = lastDimContainer.Column; lastDimContainer.RequestScroll?.Invoke(lastDimContainer); }); @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("dismiss mod customisation via toggle", () => { - InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType().Single()); + InputManager.MoveMouseTo(modSelectOverlay.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); assertCustomisationToggleState(disabled: false, active: false); @@ -205,14 +205,14 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("move mouse to dimmed area", () => { InputManager.MoveMouseTo(new Vector2( - modSelectScreen.ScreenSpaceDrawQuad.TopLeft.X, - (modSelectScreen.ScreenSpaceDrawQuad.TopLeft.Y + modSelectScreen.ScreenSpaceDrawQuad.BottomLeft.Y) / 2)); + modSelectOverlay.ScreenSpaceDrawQuad.TopLeft.X, + (modSelectOverlay.ScreenSpaceDrawQuad.TopLeft.Y + modSelectOverlay.ScreenSpaceDrawQuad.BottomLeft.Y) / 2)); }); AddStep("click", () => InputManager.Click(MouseButton.Left)); assertCustomisationToggleState(disabled: false, active: false); - AddStep("move mouse to first mod panel", () => InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType().First())); - AddAssert("first mod panel is hovered", () => modSelectScreen.ChildrenOfType().First().IsHovered); + AddStep("move mouse to first mod panel", () => InputManager.MoveMouseTo(modSelectOverlay.ChildrenOfType().First())); + AddAssert("first mod panel is hovered", () => modSelectOverlay.ChildrenOfType().First().IsHovered); } /// @@ -222,12 +222,12 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestSettingsNotCrossPolluting() { Bindable> selectedMods2 = null; - ModSelectScreen modSelectScreen2 = null; + ModSelectOverlay modSelectScreen2 = null; createScreen(); AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() }); - AddStep("set setting", () => modSelectScreen.ChildrenOfType>().First().Current.Value = 8); + AddStep("set setting", () => modSelectOverlay.ChildrenOfType>().First().Current.Value = 8); AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); @@ -235,7 +235,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create second overlay", () => { - Add(modSelectScreen2 = new UserModSelectScreen().With(d => + Add(modSelectScreen2 = new UserModSelectOverlay().With(d => { d.Origin = Anchor.TopCentre; d.Anchor = Anchor.TopCentre; @@ -276,20 +276,20 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Select all fun mods", () => { - modSelectScreen.ChildrenOfType() - .Single(c => c.ModType == ModType.DifficultyIncrease) - .SelectAll(); + modSelectOverlay.ChildrenOfType() + .Single(c => c.ModType == ModType.DifficultyIncrease) + .SelectAll(); }); AddUntilStep("many mods selected", () => SelectedMods.Value.Count >= 5); AddStep("trigger deselect and close overlay", () => { - modSelectScreen.ChildrenOfType() - .Single(c => c.ModType == ModType.DifficultyIncrease) - .DeselectAll(); + modSelectOverlay.ChildrenOfType() + .Single(c => c.ModType == ModType.DifficultyIncrease) + .DeselectAll(); - modSelectScreen.Hide(); + modSelectOverlay.Hide(); }); AddAssert("all mods deselected", () => SelectedMods.Value.Count == 0); @@ -378,15 +378,15 @@ namespace osu.Game.Tests.Visual.UserInterface createScreen(); changeRuleset(0); - AddAssert("double time visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).Any(panel => !panel.Filtered.Value)); + AddAssert("double time visible", () => modSelectOverlay.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).Any(panel => !panel.Filtered.Value)); - AddStep("make double time invalid", () => modSelectScreen.IsValidMod = m => !(m is OsuModDoubleTime)); - AddUntilStep("double time not visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).All(panel => panel.Filtered.Value)); - AddAssert("nightcore still visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModNightcore).Any(panel => !panel.Filtered.Value)); + AddStep("make double time invalid", () => modSelectOverlay.IsValidMod = m => !(m is OsuModDoubleTime)); + AddUntilStep("double time not visible", () => modSelectOverlay.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).All(panel => panel.Filtered.Value)); + AddAssert("nightcore still visible", () => modSelectOverlay.ChildrenOfType().Where(panel => panel.Mod is OsuModNightcore).Any(panel => !panel.Filtered.Value)); - AddStep("make double time valid again", () => modSelectScreen.IsValidMod = m => true); - AddUntilStep("double time visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).Any(panel => !panel.Filtered.Value)); - AddAssert("nightcore still visible", () => modSelectScreen.ChildrenOfType().Where(b => b.Mod is OsuModNightcore).Any(panel => !panel.Filtered.Value)); + AddStep("make double time valid again", () => modSelectOverlay.IsValidMod = m => true); + AddUntilStep("double time visible", () => modSelectOverlay.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).Any(panel => !panel.Filtered.Value)); + AddAssert("nightcore still visible", () => modSelectOverlay.ChildrenOfType().Where(b => b.Mod is OsuModNightcore).Any(panel => !panel.Filtered.Value)); } [Test] @@ -396,10 +396,10 @@ namespace osu.Game.Tests.Visual.UserInterface changeRuleset(0); AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); - AddAssert("DT + HD selected", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); - AddStep("make NF invalid", () => modSelectScreen.IsValidMod = m => !(m is ModNoFail)); - AddAssert("DT + HD still selected", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddStep("make NF invalid", () => modSelectOverlay.IsValidMod = m => !(m is ModNoFail)); + AddAssert("DT + HD still selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); } [Test] @@ -422,7 +422,7 @@ namespace osu.Game.Tests.Visual.UserInterface changeRuleset(0); AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); - AddAssert("DT + HD selected", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); AddStep("click deselect all button", () => { @@ -448,13 +448,13 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.MoveMouseTo(this.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); - AddAssert("mod select hidden", () => modSelectScreen.State.Value == Visibility.Hidden); + AddAssert("mod select hidden", () => modSelectOverlay.State.Value == Visibility.Hidden); } [Test] public void TestColumnHiding() { - AddStep("create screen", () => Child = modSelectScreen = new UserModSelectScreen + AddStep("create screen", () => Child = modSelectOverlay = new UserModSelectOverlay { RelativeSizeAxes = Axes.Both, State = { Value = Visibility.Visible }, @@ -466,23 +466,23 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("two columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 2); - AddStep("unset filter", () => modSelectScreen.IsValidMod = _ => true); + AddStep("unset filter", () => modSelectOverlay.IsValidMod = _ => true); AddAssert("all columns visible", () => this.ChildrenOfType().All(col => col.IsPresent)); - AddStep("filter out everything", () => modSelectScreen.IsValidMod = _ => false); + AddStep("filter out everything", () => modSelectOverlay.IsValidMod = _ => false); AddAssert("no columns visible", () => this.ChildrenOfType().All(col => !col.IsPresent)); - AddStep("hide", () => modSelectScreen.Hide()); - AddStep("set filter for 3 columns", () => modSelectScreen.IsValidMod = mod => mod.Type == ModType.DifficultyReduction - || mod.Type == ModType.Automation - || mod.Type == ModType.Conversion); + AddStep("hide", () => modSelectOverlay.Hide()); + AddStep("set filter for 3 columns", () => modSelectOverlay.IsValidMod = mod => mod.Type == ModType.DifficultyReduction + || mod.Type == ModType.Automation + || mod.Type == ModType.Conversion); - AddStep("show", () => modSelectScreen.Show()); + AddStep("show", () => modSelectOverlay.Show()); AddUntilStep("3 columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 3); } private void waitForColumnLoad() => AddUntilStep("all column content loaded", - () => modSelectScreen.ChildrenOfType().Any() && modSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); + () => modSelectOverlay.ChildrenOfType().Any() && modSelectOverlay.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); private void changeRuleset(int id) { @@ -492,14 +492,14 @@ namespace osu.Game.Tests.Visual.UserInterface private void assertCustomisationToggleState(bool disabled, bool active) { - ShearedToggleButton getToggle() => modSelectScreen.ChildrenOfType().Single(); + ShearedToggleButton getToggle() => modSelectOverlay.ChildrenOfType().Single(); AddAssert($"customisation toggle is {(disabled ? "" : "not ")}disabled", () => getToggle().Active.Disabled == disabled); AddAssert($"customisation toggle is {(active ? "" : "not ")}active", () => getToggle().Active.Value == active); } private ModPanel getPanelForMod(Type modType) - => modSelectScreen.ChildrenOfType().Single(panel => panel.Mod.GetType() == modType); + => modSelectOverlay.ChildrenOfType().Single(panel => panel.Mod.GetType() == modType); private class TestUnimplementedMod : Mod { diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 3a2fda0bb0..b32ebb4a5c 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -384,7 +384,7 @@ namespace osu.Game.Overlays.Mods /// /// /// This method exists to be able to receive mod instances that come from potentially-external sources and to copy the changes across to this column's state. - /// uses this to substitute any external mod references in + /// uses this to substitute any external mod references in /// to references that are owned by this column. /// internal void SetSelection(IReadOnlyList mods) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs similarity index 99% rename from osu.Game/Overlays/Mods/ModSelectScreen.cs rename to osu.Game/Overlays/Mods/ModSelectOverlay.cs index 912c09f05c..b589b2e7e1 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -27,7 +27,7 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public abstract class ModSelectScreen : ShearedOverlayContainer, ISamplePlaybackDisabler + public abstract class ModSelectOverlay : ShearedOverlayContainer, ISamplePlaybackDisabler { protected const int BUTTON_WIDTH = 200; @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Mods private ShearedToggleButton? customisationButton; - protected ModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green) + protected ModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green) : base(colourScheme) { } diff --git a/osu.Game/Overlays/Mods/UserModSelectScreen.cs b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs similarity index 92% rename from osu.Game/Overlays/Mods/UserModSelectScreen.cs rename to osu.Game/Overlays/Mods/UserModSelectOverlay.cs index a018797cba..8ff5e28c8f 100644 --- a/osu.Game/Overlays/Mods/UserModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs @@ -10,9 +10,9 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public class UserModSelectScreen : ModSelectScreen + public class UserModSelectOverlay : ModSelectOverlay { - public UserModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green) + public UserModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green) : base(colourScheme) { } diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs similarity index 94% rename from osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs rename to osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index e92de5e083..6e1c9b7a59 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -13,7 +13,7 @@ using osu.Game.Localisation; namespace osu.Game.Screens.OnlinePlay { - public class FreeModSelectScreen : ModSelectScreen + public class FreeModSelectOverlay : ModSelectOverlay { protected override bool ShowTotalMultiplier => false; @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m); } - public FreeModSelectScreen() + public FreeModSelectOverlay() : base(OverlayColourScheme.Plum) { IsValidMod = _ => true; diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index ec4e329f7a..13b2c37ded 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens.OnlinePlay.Match public readonly Room Room; private readonly bool allowEdit; - private ModSelectScreen userModsSelectOverlay; + private ModSelectOverlay userModsSelectOverlay; [CanBeNull] private IDisposable userModsSelectOverlayRegistration; @@ -231,7 +231,7 @@ namespace osu.Game.Screens.OnlinePlay.Match } }; - LoadComponent(userModsSelectOverlay = new UserModSelectScreen(OverlayColourScheme.Plum) + LoadComponent(userModsSelectOverlay = new UserModSelectOverlay(OverlayColourScheme.Plum) { SelectedMods = { BindTarget = UserMods }, IsValidMod = _ => false diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index c4503773ad..fb18a33d66 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.OnlinePlay private IReadOnlyList initialMods; private bool itemSelected; - private readonly FreeModSelectScreen freeModSelectOverlay; + private readonly FreeModSelectOverlay freeModSelectOverlay; private IDisposable freeModSelectOverlayRegistration; protected OnlinePlaySongSelect(Room room) @@ -62,7 +62,7 @@ namespace osu.Game.Screens.OnlinePlay Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; - freeModSelectOverlay = new FreeModSelectScreen + freeModSelectOverlay = new FreeModSelectOverlay { SelectedMods = { BindTarget = FreeMods }, IsValidMod = IsValidFreeMod, @@ -160,7 +160,7 @@ namespace osu.Game.Screens.OnlinePlay return base.OnExiting(e); } - protected override ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(OverlayColourScheme.Plum) + protected override ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay(OverlayColourScheme.Plum) { IsValidMod = IsValidMod }; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index eb5e996972..8870239485 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -102,7 +102,7 @@ namespace osu.Game.Screens.Select [Resolved(CanBeNull = true)] private LegacyImportManager legacyImportManager { get; set; } - protected ModSelectScreen ModSelect { get; private set; } + protected ModSelectOverlay ModSelect { get; private set; } protected Sample SampleConfirm { get; private set; } @@ -333,7 +333,7 @@ namespace osu.Game.Screens.Select (new FooterButtonOptions(), BeatmapOptions) }; - protected virtual ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(); + protected virtual ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay(); protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { From 9dce329e998e88c415c7d23576d75a29be3ca47f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 10:38:35 +0900 Subject: [PATCH 1011/1276] Remove build suffix from version when reporting to sentry --- osu.Game/OsuGameBase.cs | 4 +++- osu.Game/Utils/SentryLogger.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index ce798d4027..2e4758a134 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -86,6 +86,8 @@ namespace osu.Game public bool IsDeployedBuild => AssemblyVersion.Major > 0; + internal const string BUILD_SUFFIX = "lazer"; + public virtual string Version { get @@ -94,7 +96,7 @@ namespace osu.Game return @"local " + (DebugUtils.IsDebugBuild ? @"debug" : @"release"); var version = AssemblyVersion; - return $@"{version.Major}.{version.Minor}.{version.Build}-lazer"; + return $@"{version.Major}.{version.Minor}.{version.Build}-{BUILD_SUFFIX}"; } } diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index ad4bcf6274..218d10345f 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -34,7 +34,7 @@ namespace osu.Game.Utils options.AutoSessionTracking = true; options.IsEnvironmentUser = false; - options.Release = game.Version; + options.Release = game.Version.Replace($@"-{OsuGameBase.BUILD_SUFFIX}", string.Empty); }); Logger.NewEntry += processLogEntry; From 533f4b298f5f0a34fa690e33c11ac858cae783ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 10:40:40 +0900 Subject: [PATCH 1012/1276] Add explanation inline for future visitors --- osu.Game/Utils/SentryLogger.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 218d10345f..16f1a8b039 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -34,6 +34,8 @@ namespace osu.Game.Utils options.AutoSessionTracking = true; options.IsEnvironmentUser = false; + // The reported release needs to match release tags on github in order for sentry + // to automatically associate and track against releases. options.Release = game.Version.Replace($@"-{OsuGameBase.BUILD_SUFFIX}", string.Empty); }); From 843e13a471a0b763ca5cd2f6047478c1fb0241de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 12:55:15 +0900 Subject: [PATCH 1013/1276] Add screen stack context to sentry --- osu.Game/OsuGame.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b8abef38a8..9edcf90132 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -56,6 +56,8 @@ using osu.Game.Updater; using osu.Game.Users; using osu.Game.Utils; using osuTK.Graphics; +using Sentry; +using Logger = osu.Framework.Logging.Logger; namespace osu.Game { @@ -1197,6 +1199,15 @@ namespace osu.Game private void screenChanged(IScreen current, IScreen newScreen) { + SentrySdk.ConfigureScope(scope => + { + scope.Contexts[@"screen stack"] = new + { + Current = newScreen.GetType().Name, + Previous = current.GetType().Name, + }; + }); + switch (newScreen) { case IntroScreen intro: From b136677bb002565e7736f7947a44fd456685d5df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 14:03:16 +0900 Subject: [PATCH 1014/1276] Add config and clock time contexts to sentry --- osu.Game/Configuration/OsuConfigManager.cs | 16 ++++++++++++++++ osu.Game/Utils/SentryLogger.cs | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 64e77384a2..026a83cceb 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -2,8 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Linq; using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; using osu.Framework.Extensions; @@ -164,6 +166,20 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.EditorHitAnimations, false); } + public IDictionary GetLoggableState() => + new Dictionary(ConfigStore.Where(kvp => !keyContainsPrivateInformation(kvp.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString())); + + private static bool keyContainsPrivateInformation(OsuSetting argKey) + { + switch (argKey) + { + case OsuSetting.Token: + return true; + } + + return false; + } + public OsuConfigManager(Storage storage) : base(storage) { diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index ad4bcf6274..e019e4bb12 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -7,9 +7,12 @@ using System; using System.Diagnostics; using System.IO; using System.Net; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Game.Configuration; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; using Sentry; using Sentry.Protocol; @@ -24,8 +27,11 @@ namespace osu.Game.Utils private readonly IDisposable? sentrySession; + private readonly OsuGame game; + public SentryLogger(OsuGame game) { + this.game = game; sentrySession = SentrySdk.Init(options => { // Not setting the dsn will completely disable sentry. @@ -92,6 +98,18 @@ namespace osu.Game.Utils { Message = entry.Message, Level = getSentryLevel(entry.Level), + }, scope => + { + scope.Contexts[@"config"] = new + { + Game = game.Dependencies.Get().GetLoggableState() + // TODO: add framework config here. needs some consideration on how to expose. + }; + scope.Contexts[@"clocks"] = new + { + Audio = game.Dependencies.Get().CurrentTrack.CurrentTime, + Game = game.Clock.CurrentTime, + }; }); } else From 977a0453cc3e18bbc38ba75d77288b0af72834c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 14:11:20 +0900 Subject: [PATCH 1015/1276] Add beatmap context to sentry --- osu.Game/Utils/SentryLogger.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index e019e4bb12..d02ffcb013 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -10,6 +10,7 @@ using System.Net; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; @@ -105,6 +106,15 @@ namespace osu.Game.Utils Game = game.Dependencies.Get().GetLoggableState() // TODO: add framework config here. needs some consideration on how to expose. }; + + var beatmap = game.Dependencies.Get>().Value.BeatmapInfo; + + scope.Contexts[@"beatmap"] = new + { + Name = beatmap.ToString(), + beatmap.OnlineID, + }; + scope.Contexts[@"clocks"] = new { Audio = game.Dependencies.Get().CurrentTrack.CurrentTime, From f53d42d31ff51e6c43878e49f2db6b3da73a5841 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 14:51:56 +0900 Subject: [PATCH 1016/1276] Add realm context to sentry --- osu.Game/Utils/SentryLogger.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index d02ffcb013..a291cee9ae 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -6,14 +6,18 @@ using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Database; +using osu.Game.Models; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; +using osu.Game.Skinning; using Sentry; using Sentry.Protocol; @@ -101,13 +105,27 @@ namespace osu.Game.Utils Level = getSentryLevel(entry.Level), }, scope => { + var beatmap = game.Dependencies.Get>().Value.BeatmapInfo; + scope.Contexts[@"config"] = new { Game = game.Dependencies.Get().GetLoggableState() // TODO: add framework config here. needs some consideration on how to expose. }; - var beatmap = game.Dependencies.Get>().Value.BeatmapInfo; + game.Dependencies.Get().Run(realm => + { + scope.Contexts[@"realm"] = new + { + Counts = new + { + BeatmapSets = realm.All().Count(), + Beatmaps = realm.All().Count(), + Files = realm.All().Count(), + Skins = realm.All().Count(), + } + }; + }); scope.Contexts[@"beatmap"] = new { From be09ec833e0b5e271f6fe8d50dd3d0e4d965de30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 14:52:08 +0900 Subject: [PATCH 1017/1276] Add global statistics context to sentry --- osu.Game/Utils/SentryLogger.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index a291cee9ae..d68f43e88a 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -11,6 +11,7 @@ using System.Net; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Framework.Statistics; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; @@ -127,6 +128,10 @@ namespace osu.Game.Utils }; }); + scope.Contexts[@"global statistics"] = GlobalStatistics.GetStatistics() + .GroupBy(s => s.Group) + .ToDictionary(g => g.Key, items => items.ToDictionary(i => i.Name, g => g.DisplayValue)); + scope.Contexts[@"beatmap"] = new { Name = beatmap.ToString(), From 0b597e712ed72de537fc9e8e413bc06a7c477111 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 09:39:27 +0300 Subject: [PATCH 1018/1276] Fix timeline not handling mouse down events --- .../Screens/Edit/Compose/Components/Timeline/Timeline.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 51b8792d87..7e66c57917 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -270,12 +270,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override bool OnMouseDown(MouseDownEvent e) { if (base.OnMouseDown(e)) - { beginUserDrag(); - return true; - } - return false; + return true; } protected override void OnMouseUp(MouseUpEvent e) From e43ce28ada3a21a1b7c978da2db16ea9fee0f994 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 09:51:00 +0300 Subject: [PATCH 1019/1276] Remove references of "difficulty point" in sample point test scene Fat-fingered. --- .../Editing/TestSceneHitObjectSamplePointAdjustments.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index dca30a6fc0..f0a2347c31 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -173,11 +173,11 @@ namespace osu.Game.Tests.Visual.Editing samplePopoverHasSingleBank("normal"); } - private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} difficulty piece", () => + private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () => { - var difficultyPiece = this.ChildrenOfType().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex)); + var samplePiece = this.ChildrenOfType().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex)); - InputManager.MoveMouseTo(difficultyPiece); + InputManager.MoveMouseTo(samplePiece); InputManager.Click(MouseButton.Left); }); @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.Editing private void dismissPopover() { AddStep("dismiss popover", () => InputManager.Key(Key.Escape)); - AddUntilStep("wait for dismiss", () => !this.ChildrenOfType().Any(popover => popover.IsPresent)); + AddUntilStep("wait for dismiss", () => !this.ChildrenOfType().Any(popover => popover.IsPresent)); } private void setVolumeViaPopover(int volume) => AddStep($"set volume {volume} via popover", () => From f797514bce5aafb5df020e7fa62c2dd24b0e3139 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 09:51:59 +0300 Subject: [PATCH 1020/1276] Add failing test cases --- ...tSceneHitObjectDifficultyPointAdjustments.cs | 17 +++++++++++++++++ .../TestSceneHitObjectSamplePointAdjustments.cs | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs index 4012a672ed..7f82d5966e 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; @@ -66,6 +67,13 @@ namespace osu.Game.Tests.Visual.Editing }); } + [Test] + public void TestPopoverHasFocus() + { + clickDifficultyPiece(0); + velocityPopoverHasFocus(); + } + [Test] public void TestSingleSelection() { @@ -133,6 +141,15 @@ namespace osu.Game.Tests.Visual.Editing InputManager.Click(MouseButton.Left); }); + private void velocityPopoverHasFocus() => AddUntilStep("velocity popover textbox focused", () => + { + var popover = this.ChildrenOfType().SingleOrDefault(); + var slider = popover?.ChildrenOfType>().Single(); + var textbox = slider?.ChildrenOfType().Single(); + + return textbox?.HasFocus == true; + }); + private void velocityPopoverHasSingleValue(double velocity) => AddUntilStep($"velocity popover has {velocity}", () => { var popover = this.ChildrenOfType().SingleOrDefault(); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index f0a2347c31..fcdf4a6e33 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -57,6 +57,13 @@ namespace osu.Game.Tests.Visual.Editing }); } + [Test] + public void TestPopoverHasFocus() + { + clickSamplePiece(0); + samplePopoverHasFocus(); + } + [Test] public void TestSingleSelection() { @@ -181,6 +188,15 @@ namespace osu.Game.Tests.Visual.Editing InputManager.Click(MouseButton.Left); }); + private void samplePopoverHasFocus() => AddUntilStep("sample popover textbox focused", () => + { + var popover = this.ChildrenOfType().SingleOrDefault(); + var slider = popover?.ChildrenOfType>().Single(); + var textbox = slider?.ChildrenOfType().Single(); + + return textbox?.HasFocus == true; + }); + private void samplePopoverHasSingleVolume(int volume) => AddUntilStep($"sample popover has volume {volume}", () => { var popover = this.ChildrenOfType().SingleOrDefault(); From 24432dffc4622135e1a296638d1e765ca9801477 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 09:53:04 +0300 Subject: [PATCH 1021/1276] Add support for focusing intermediate slider-textbox component --- .../Edit/Timing/IndeterminateSliderWithTextBoxInput.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs index 0cf2cf6c54..16a04982f5 100644 --- a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays.Settings; @@ -107,6 +108,14 @@ namespace osu.Game.Screens.Edit.Timing Current.BindValueChanged(_ => updateState(), true); } + public override bool AcceptsFocus => true; + + protected override void OnFocus(FocusEvent e) + { + base.OnFocus(e); + GetContainingInputManager().ChangeFocus(textBox); + } + private void updateState() { if (Current.Value is T nonNullValue) From 96db530de63626afa8ed51f3f8f24de669a7faa2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 09:55:34 +0300 Subject: [PATCH 1022/1276] Focus textbox on control point popovers --- .../Compose/Components/Timeline/DifficultyPointPiece.cs | 6 ++++++ .../Edit/Compose/Components/Timeline/SamplePointPiece.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs index b230bab0c2..eaaa663fe7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs @@ -122,6 +122,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline beatmap.EndChange(); }); } + + protected override void LoadComplete() + { + base.LoadComplete(); + ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(sliderVelocitySlider)); + } } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs index fc0952d4f0..ab21a83c43 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs @@ -126,6 +126,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline volume.Current.BindValueChanged(val => updateVolumeFor(relevantObjects, val.NewValue)); } + protected override void LoadComplete() + { + base.LoadComplete(); + ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(volume)); + } + private static string? getCommonBank(SampleControlPoint[] relevantControlPoints) => relevantControlPoints.Select(point => point.SampleBank).Distinct().Count() == 1 ? relevantControlPoints.First().SampleBank : null; private static int? getCommonVolume(SampleControlPoint[] relevantControlPoints) => relevantControlPoints.Select(point => point.SampleVolume).Distinct().Count() == 1 ? (int?)relevantControlPoints.First().SampleVolume : null; From 1c369956066c28ffe2333d03ae9353e1cf9a09f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 16:09:16 +0900 Subject: [PATCH 1023/1276] Expose `HoldingForHUD` state from `HUDOverlay` as bindable --- osu.Game/Screens/Play/HUDOverlay.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index abfed1acd0..f6087e0958 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -68,7 +68,9 @@ namespace osu.Game.Screens.Play internal readonly IBindable IsPlaying = new Bindable(); - private bool holdingForHUD; + public IBindable HoldingForHUD => holdingForHUD; + + private readonly BindableBool holdingForHUD = new BindableBool(); private readonly SkinnableTargetContainer mainComponents; @@ -144,7 +146,8 @@ namespace osu.Game.Screens.Play hideTargets.ForEach(d => d.Hide()); } - public override void Hide() => throw new InvalidOperationException($"{nameof(HUDOverlay)} should not be hidden as it will remove the ability of a user to quit. Use {nameof(ShowHud)} instead."); + public override void Hide() => + throw new InvalidOperationException($"{nameof(HUDOverlay)} should not be hidden as it will remove the ability of a user to quit. Use {nameof(ShowHud)} instead."); protected override void LoadComplete() { @@ -152,6 +155,7 @@ namespace osu.Game.Screens.Play ShowHud.BindValueChanged(visible => hideTargets.ForEach(d => d.FadeTo(visible.NewValue ? 1 : 0, FADE_DURATION, FADE_EASING))); + holdingForHUD.BindValueChanged(_ => updateVisibility()); IsPlaying.BindValueChanged(_ => updateVisibility()); configVisibilityMode.BindValueChanged(_ => updateVisibility(), true); @@ -204,7 +208,7 @@ namespace osu.Game.Screens.Play if (ShowHud.Disabled) return; - if (holdingForHUD) + if (holdingForHUD.Value) { ShowHud.Value = true; return; @@ -287,8 +291,7 @@ namespace osu.Game.Screens.Play switch (e.Action) { case GlobalAction.HoldForHUD: - holdingForHUD = true; - updateVisibility(); + holdingForHUD.Value = true; return true; case GlobalAction.ToggleInGameInterface: @@ -318,8 +321,7 @@ namespace osu.Game.Screens.Play switch (e.Action) { case GlobalAction.HoldForHUD: - holdingForHUD = false; - updateVisibility(); + holdingForHUD.Value = false; break; } } From d05cd6908763cd47ca66281f70d6540b2080ea39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 16:12:54 +0900 Subject: [PATCH 1024/1276] Change multiplayer leaderboard to always hide during gameplay unless holding-for-HUD --- .../OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 02ff040a94..5dab845999 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -43,6 +43,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private readonly MultiplayerRoomUser[] users; + private readonly Bindable leaderboardExpanded = new BindableBool(); + private LoadingLayer loadingDisplay; private FillFlowContainer leaderboardFlow; @@ -76,13 +78,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Spacing = new Vector2(5) }); + HUDOverlay.HoldingForHUD.BindValueChanged(_ => updateLeaderboardExpandedState()); + LocalUserPlaying.BindValueChanged(_ => updateLeaderboardExpandedState(), true); + // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(GameplayState.Ruleset.RulesetInfo, ScoreProcessor, users), l => { if (!LoadedBeatmapSuccessfully) return; - ((IBindable)leaderboard.Expanded).BindTo(HUDOverlay.ShowHud); + leaderboard.Expanded.BindTo(leaderboardExpanded); leaderboardFlow.Insert(0, l); @@ -99,7 +104,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer LoadComponentAsync(new GameplayChatDisplay(Room) { - Expanded = { BindTarget = HUDOverlay.ShowHud }, + Expanded = { BindTarget = leaderboardExpanded }, }, chat => leaderboardFlow.Insert(2, chat)); HUDOverlay.Add(loadingDisplay = new LoadingLayer(true) { Depth = float.MaxValue }); @@ -152,6 +157,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } } + private void updateLeaderboardExpandedState() => + leaderboardExpanded.Value = !LocalUserPlaying.Value || HUDOverlay.HoldingForHUD.Value; + private void failAndBail(string message = null) { if (!string.IsNullOrEmpty(message)) From d51689e9ae38a0f0030ac436701d22d57465b51d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 11:25:41 +0300 Subject: [PATCH 1025/1276] Unfocus textbox when dismissing popover in test scene --- .../Editing/TestSceneHitObjectDifficultyPointAdjustments.cs | 1 + .../Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs index 7f82d5966e..7c05abc2cd 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs @@ -168,6 +168,7 @@ namespace osu.Game.Tests.Visual.Editing private void dismissPopover() { + AddStep("unfocus textbox", () => InputManager.Key(Key.Escape)); AddStep("dismiss popover", () => InputManager.Key(Key.Escape)); AddUntilStep("wait for dismiss", () => !this.ChildrenOfType().Any(popover => popover.IsPresent)); } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index fcdf4a6e33..4501eea88e 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -231,6 +231,7 @@ namespace osu.Game.Tests.Visual.Editing private void dismissPopover() { + AddStep("unfocus textbox", () => InputManager.Key(Key.Escape)); AddStep("dismiss popover", () => InputManager.Key(Key.Escape)); AddUntilStep("wait for dismiss", () => !this.ChildrenOfType().Any(popover => popover.IsPresent)); } From 551370d27b1a9c4e941335e706efc9bc19896cfe Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 11 May 2022 18:26:08 +0900 Subject: [PATCH 1026/1276] Update ReSharper package --- .config/dotnet-tools.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 1132396608..65ac05261a 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2022.1.0-eap10", + "version": "2022.1.1", "commands": [ "jb" ] @@ -27,4 +27,4 @@ ] } } -} +} \ No newline at end of file From a46894b613df38cb0d4d159f6e86a87dd2a0e8fb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 11 May 2022 18:27:57 +0900 Subject: [PATCH 1027/1276] Bust CI cache on more files --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 514acef525..729f2f266d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: uses: actions/cache@v3 with: path: ${{ github.workspace }}/inspectcode - key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }} + key: inspectcode-${{ hashFiles('.config/dotnet-tools.json', '.github/workflows/ci.yml', 'osu.sln*', '.editorconfig', '.globalconfig') }} - name: Dotnet code style run: dotnet build -c Debug -warnaserror osu.Desktop.slnf -p:EnforceCodeStyleInBuild=true From fecf92e16f926b2d5e2a8f51b2e3c03bf8736d0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 19:51:22 +0900 Subject: [PATCH 1028/1276] Update libraries to latest versions --- .../osu.Game.Rulesets.EmptyFreeform.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.EmptyScrolling.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- osu.Android.props | 2 +- osu.Desktop/osu.Desktop.csproj | 2 +- osu.Game.Benchmarks/osu.Game.Benchmarks.csproj | 2 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- .../osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- .../osu.Game.Tournament.Tests.csproj | 2 +- osu.Game/osu.Game.csproj | 14 +++++++------- osu.iOS.props | 8 ++++---- 15 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj index cb922c5a58..bc285dbe11 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 5ecd9cc675..718ada1905 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj index 33ad0ac4f7..6b9c3f4d63 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 5ecd9cc675..718ada1905 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/osu.Android.props b/osu.Android.props index 97d9dbc380..af4a93d191 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -56,6 +56,6 @@ - + diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index a4f309c6ac..a4f9e2671b 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index 434c0e0367..36ffd3b5b6 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -8,7 +8,7 @@ - + diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index fc6d900567..b957ade952 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index ddad2adfea..d3b4b378c0 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 4ce29ab5c7..2c0d3fd937 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index a6b8eb8651..ce468d399b 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 0bcf533653..a1eef4ce47 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index c7314a4969..6fd53d923b 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2f32c843c0..89cb882981 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,9 +23,9 @@ - - - + + + @@ -34,12 +34,12 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - - + + + diff --git a/osu.iOS.props b/osu.iOS.props index b483267696..46580705f7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -85,10 +85,10 @@ - - + + - - + + From 3cbd19a9ce09c800005e6b51d263e4d4593362d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 21:17:16 +0900 Subject: [PATCH 1029/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index af4a93d191..98dc28d915 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 89cb882981..772a78c8fe 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 46580705f7..af8f9b617c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 9d30b490ade53ada8771a704a03163c04f15028f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 18:26:04 +0300 Subject: [PATCH 1030/1276] Fix intermittent test failures in results screen --- .../Visual/Ranking/TestSceneResultsScreen.cs | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index a1d51683e4..2a5fc050d3 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -103,7 +103,7 @@ namespace osu.Game.Tests.Visual.Ranking score.Accuracy = accuracy; score.Rank = rank; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen(score))); + loadResultsScreen(() => screen = createResultsScreen(score)); AddUntilStep("wait for loaded", () => screen.IsLoaded); AddAssert("retry overlay present", () => screen.RetryOverlay != null); } @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.Ranking { UnrankedSoloResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createUnrankedSoloResultsScreen())); + loadResultsScreen(() => screen = createUnrankedSoloResultsScreen()); AddUntilStep("wait for loaded", () => screen.IsLoaded); AddAssert("retry overlay present", () => screen.RetryOverlay != null); } @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.Ranking { TestResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + loadResultsScreen(() => screen = createResultsScreen()); AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddStep("click expanded panel", () => @@ -162,7 +162,7 @@ namespace osu.Game.Tests.Visual.Ranking { TestResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + loadResultsScreen(() => screen = createResultsScreen()); AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddStep("click expanded panel", () => @@ -201,7 +201,7 @@ namespace osu.Game.Tests.Visual.Ranking { TestResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + loadResultsScreen(() => screen = createResultsScreen()); AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); ScorePanel expandedPanel = null; @@ -231,7 +231,7 @@ namespace osu.Game.Tests.Visual.Ranking var tcs = new TaskCompletionSource(); - AddStep("load results", () => Child = new TestResultsContainer(screen = new DelayedFetchResultsScreen(TestResources.CreateTestScoreInfo(), tcs.Task))); + loadResultsScreen(() => screen = new DelayedFetchResultsScreen(TestResources.CreateTestScoreInfo(), tcs.Task)); AddUntilStep("wait for loaded", () => screen.IsLoaded); @@ -255,7 +255,7 @@ namespace osu.Game.Tests.Visual.Ranking { TestResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + loadResultsScreen(() => screen = createResultsScreen()); AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddAssert("download button is disabled", () => !screen.ChildrenOfType().Last().Enabled.Value); @@ -276,7 +276,7 @@ namespace osu.Game.Tests.Visual.Ranking var ruleset = new RulesetWithNoPerformanceCalculator(); var score = TestResources.CreateTestScoreInfo(ruleset.RulesetInfo); - AddStep("load results", () => Child = new TestResultsContainer(createResultsScreen(score))); + loadResultsScreen(() => createResultsScreen(score)); AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddAssert("PP displayed as 0", () => @@ -287,6 +287,22 @@ namespace osu.Game.Tests.Visual.Ranking }); } + private void loadResultsScreen(Func createResults) + { + ResultsScreen results = null; + + AddStep("load results", () => Child = new TestResultsContainer(results = createResults())); + + // expanded panel should be centered the moment results screen is loaded + // but can potentially be scrolled away on certain specific load scenarios. + // see: https://github.com/ppy/osu/issues/18226 + AddUntilStep("expanded panel in centre of screen", () => + { + var expandedPanel = this.ChildrenOfType().Single(p => p.State == PanelState.Expanded); + return Precision.AlmostEquals(expandedPanel.ScreenSpaceDrawQuad.Centre.X, results.ScreenSpaceDrawQuad.Centre.X, 1); + }); + } + private TestResultsScreen createResultsScreen(ScoreInfo score = null) => new TestResultsScreen(score ?? TestResources.CreateTestScoreInfo()); private UnrankedSoloResultsScreen createUnrankedSoloResultsScreen() => new UnrankedSoloResultsScreen(TestResources.CreateTestScoreInfo()); From a104277e7fbd1315fa230fa67d6be9ea317f92e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:01:33 +0200 Subject: [PATCH 1031/1276] Rename `ModSelect{Screen -> Overlay}Strings` --- ...ModSelectScreenStrings.cs => ModSelectOverlayStrings.cs} | 6 +++--- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game/Localisation/{ModSelectScreenStrings.cs => ModSelectOverlayStrings.cs} (94%) diff --git a/osu.Game/Localisation/ModSelectScreenStrings.cs b/osu.Game/Localisation/ModSelectOverlayStrings.cs similarity index 94% rename from osu.Game/Localisation/ModSelectScreenStrings.cs rename to osu.Game/Localisation/ModSelectOverlayStrings.cs index 0c113fd381..e9af7147e3 100644 --- a/osu.Game/Localisation/ModSelectScreenStrings.cs +++ b/osu.Game/Localisation/ModSelectOverlayStrings.cs @@ -5,9 +5,9 @@ using osu.Framework.Localisation; namespace osu.Game.Localisation { - public static class ModSelectScreenStrings + public static class ModSelectOverlayStrings { - private const string prefix = @"osu.Game.Resources.Localisation.ModSelectScreen"; + private const string prefix = @"osu.Game.Resources.Localisation.ModSelectOverlay"; /// /// "Mod Select" @@ -26,4 +26,4 @@ namespace osu.Game.Localisation private static string getKey(string key) => $@"{prefix}:{key}"; } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b589b2e7e1..7a8e89584a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -84,8 +84,8 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load(OsuColour colours) { - Header.Title = ModSelectScreenStrings.ModSelectTitle; - Header.Description = ModSelectScreenStrings.ModSelectDescription; + Header.Title = ModSelectOverlayStrings.ModSelectTitle; + Header.Description = ModSelectOverlayStrings.ModSelectDescription; AddRange(new Drawable[] { @@ -262,7 +262,7 @@ namespace osu.Game.Overlays.Mods { customisationButton = new ShearedToggleButton(BUTTON_WIDTH) { - Text = ModSelectScreenStrings.ModCustomisation, + Text = ModSelectOverlayStrings.ModCustomisation, Active = { BindTarget = customisationVisible } }, new ShearedButton(BUTTON_WIDTH) From 62f6caf76dcc836b08a7db054d7f77d149c551c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:04:39 +0200 Subject: [PATCH 1032/1276] Rename `ModSelect{Screen -> Overlay}` test scenes --- ...eFreeModSelectScreen.cs => TestSceneFreeModSelectOverlay.cs} | 2 +- ...TestSceneModSelectScreen.cs => TestSceneModSelectOverlay.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneFreeModSelectScreen.cs => TestSceneFreeModSelectOverlay.cs} (98%) rename osu.Game.Tests/Visual/UserInterface/{TestSceneModSelectScreen.cs => TestSceneModSelectOverlay.cs} (99%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs similarity index 98% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index 9a24a1c646..f40c31b07f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -17,7 +17,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneFreeModSelectScreen : MultiplayerTestScene + public class TestSceneFreeModSelectOverlay : MultiplayerTestScene { private FreeModSelectOverlay freeModSelectOverlay; private readonly Bindable>> availableMods = new Bindable>>(); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs similarity index 99% rename from osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 2e2ce6edd7..08ad030b39 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -24,7 +24,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneModSelectScreen : OsuManualInputManagerTestScene + public class TestSceneModSelectOverlay : OsuManualInputManagerTestScene { [Resolved] private RulesetStore rulesetStore { get; set; } From 9b7ff9f2ee3d9aee2eb443c1e354024e8eae07ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:05:09 +0200 Subject: [PATCH 1033/1276] Rename `modSelect{Screen -> Overlay}2` variable --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 08ad030b39..fc543d9db7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -222,7 +222,7 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestSettingsNotCrossPolluting() { Bindable> selectedMods2 = null; - ModSelectOverlay modSelectScreen2 = null; + ModSelectOverlay modSelectOverlay2 = null; createScreen(); AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() }); @@ -235,7 +235,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create second overlay", () => { - Add(modSelectScreen2 = new UserModSelectOverlay().With(d => + Add(modSelectOverlay2 = new UserModSelectOverlay().With(d => { d.Origin = Anchor.TopCentre; d.Anchor = Anchor.TopCentre; @@ -243,7 +243,7 @@ namespace osu.Game.Tests.Visual.UserInterface })); }); - AddStep("show", () => modSelectScreen2.Show()); + AddStep("show", () => modSelectOverlay2.Show()); AddAssert("ensure first is unchanged", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); AddAssert("ensure second is default", () => selectedMods2.Value.OfType().Single().CircleSize.Value == null); From ddb2d4eef51220e0f41835ddfe4ec593482279fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:06:09 +0200 Subject: [PATCH 1034/1276] Rename `FreeModSelect{Screen -> Overlay}` reference in inline comment --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 7a8e89584a..b3c3eee15a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -193,7 +193,7 @@ namespace osu.Game.Overlays.Mods State.BindValueChanged(_ => samplePlaybackDisabled.Value = State.Value == Visibility.Hidden, true); // This is an optimisation to prevent refreshing the available settings controls when it can be - // reasonably assumed that the settings panel is never to be displayed (e.g. FreeModSelectScreen). + // reasonably assumed that the settings panel is never to be displayed (e.g. FreeModSelectOverlay). if (customisationButton != null) ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); From 315c67a316343e8935c61e640aa502c418e887cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 19:59:30 +0200 Subject: [PATCH 1035/1276] Add failing test case for ruleset without all mod types --- .../UserInterface/TestSceneModSelectOverlay.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index fc543d9db7..0b037a10cd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Tests.Mods; using osuTK; using osuTK.Input; @@ -481,6 +482,21 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("3 columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 3); } + [Test] + public void TestColumnHidingOnRulesetChange() + { + createScreen(); + + changeRuleset(0); + AddAssert("5 columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 5); + + AddStep("change to ruleset without all mod types", () => Ruleset.Value = TestCustomisableModRuleset.CreateTestRulesetInfo()); + AddUntilStep("1 column visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 1); + + changeRuleset(0); + AddAssert("5 columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 5); + } + private void waitForColumnLoad() => AddUntilStep("all column content loaded", () => modSelectOverlay.ChildrenOfType().Any() && modSelectOverlay.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); From 478cfc0b87ac3a6062e5948fe8df496df0fac991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:26:52 +0200 Subject: [PATCH 1036/1276] Split model class for mod state --- osu.Game/Overlays/Mods/ModState.cs | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 osu.Game/Overlays/Mods/ModState.cs diff --git a/osu.Game/Overlays/Mods/ModState.cs b/osu.Game/Overlays/Mods/ModState.cs new file mode 100644 index 0000000000..8fdd5db00b --- /dev/null +++ b/osu.Game/Overlays/Mods/ModState.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Overlays.Mods +{ + /// + /// Wrapper class used to store the current state of a mod shown on the . + /// Used primarily to decouple data from drawable logic. + /// + public class ModState + { + /// + /// The mod that whose state this instance describes. + /// + public Mod Mod { get; } + + /// + /// Whether the mod is currently selected. + /// + public BindableBool Active { get; } = new BindableBool(); + + /// + /// Whether the mod is currently filtered out due to not matching imposed criteria. + /// + public BindableBool Filtered { get; } = new BindableBool(); + + public ModState(Mod mod) + { + Mod = mod; + } + } +} From 74599c9c625ae9902e16dfd80d2132d0fc0051e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:29:14 +0200 Subject: [PATCH 1037/1276] Use `ModState` in mod panels --- osu.Game/Overlays/Mods/ModPanel.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index 4c4951307d..02b8c195ec 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -28,9 +28,11 @@ namespace osu.Game.Overlays.Mods { public class ModPanel : OsuClickableContainer { - public Mod Mod { get; } - public BindableBool Active { get; } = new BindableBool(); - public BindableBool Filtered { get; } = new BindableBool(); + public Mod Mod => modState.Mod; + public BindableBool Active => modState.Active; + public BindableBool Filtered => modState.Filtered; + + private readonly ModState modState; protected readonly Box Background; protected readonly Container SwitchContainer; @@ -57,7 +59,7 @@ namespace osu.Game.Overlays.Mods public ModPanel(Mod mod) { - Mod = mod; + modState = new ModState(mod); RelativeSizeAxes = Axes.X; Height = 42; From e86444c4bf433a34adce2a62f3f0eae9fc9a12ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:37:31 +0200 Subject: [PATCH 1038/1276] Hoist `ModState` to column level --- .../Mods/IncompatibilityDisplayingModPanel.cs | 5 +++++ osu.Game/Overlays/Mods/ModColumn.cs | 21 ++++++++++--------- osu.Game/Overlays/Mods/ModPanel.cs | 15 ++++++++----- .../Overlays/Mods/UserModSelectOverlay.cs | 2 +- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs index aeb983d352..34c4458a21 100644 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs @@ -19,6 +19,11 @@ namespace osu.Game.Overlays.Mods [Resolved] private Bindable> selectedMods { get; set; } + public IncompatibilityDisplayingModPanel(ModState modState) + : base(modState) + { + } + public IncompatibilityDisplayingModPanel(Mod mod) : base(mod) { diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index b32ebb4a5c..c5364fb403 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -20,6 +20,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Framework.Lists; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -83,20 +84,20 @@ namespace osu.Game.Overlays.Mods protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => base.ReceivePositionalInputAtSubTree(screenSpacePos) && Active.Value; - protected virtual ModPanel CreateModPanel(Mod mod) => new ModPanel(mod); + protected virtual ModPanel CreateModPanel(ModState mod) => new ModPanel(mod); private readonly Key[]? toggleKeys; private readonly Bindable>> availableMods = new Bindable>>(); /// - /// All mods that are available for the current ruleset in this particular column. + /// Contains information about state of all mods that are available for the current ruleset in this particular column. /// /// /// Note that the mod instances in this list are owned solely by this column /// (as in, they are locally-managed clones, to ensure proper isolation from any other external instances). /// - private IReadOnlyList localAvailableMods = Array.Empty(); + private IReadOnlyList localAvailableMods = Array.Empty(); private readonly TextFlowContainer headerText; private readonly Box headerBackground; @@ -291,10 +292,10 @@ namespace osu.Game.Overlays.Mods private void updateLocalAvailableMods(bool asyncLoadContent) { var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty()) - .Select(m => m.DeepClone()) + .Select(m => new ModState(m.DeepClone())) .ToList(); - if (newMods.SequenceEqual(localAvailableMods)) + if (newMods.SequenceEqual(localAvailableMods, new FuncEqualityComparer((x, y) => ReferenceEquals(x.Mod, y.Mod)))) return; localAvailableMods = newMods; @@ -393,18 +394,18 @@ namespace osu.Game.Overlays.Mods var newSelection = new List(); - foreach (var mod in localAvailableMods) + foreach (var modState in localAvailableMods) { - var matchingSelectedMod = mods.SingleOrDefault(selected => selected.GetType() == mod.GetType()); + var matchingSelectedMod = mods.SingleOrDefault(selected => selected.GetType() == modState.GetType()); if (matchingSelectedMod != null) { - mod.CopyFrom(matchingSelectedMod); - newSelection.Add(mod); + modState.Mod.CopyFrom(matchingSelectedMod); + newSelection.Add(modState.Mod); } else { - mod.ResetSettingsToDefaults(); + modState.Mod.ResetSettingsToDefaults(); } } diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index 02b8c195ec..7010342bd8 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -57,9 +57,9 @@ namespace osu.Game.Overlays.Mods private Sample? sampleOff; private Sample? sampleOn; - public ModPanel(Mod mod) + public ModPanel(ModState modState) { - modState = new ModState(mod); + this.modState = modState; RelativeSizeAxes = Axes.X; Height = 42; @@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Mods SwitchContainer = new Container { RelativeSizeAxes = Axes.Y, - Child = new ModSwitchSmall(mod) + Child = new ModSwitchSmall(Mod) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -117,7 +117,7 @@ namespace osu.Game.Overlays.Mods { new OsuSpriteText { - Text = mod.Name, + Text = Mod.Name, Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold), Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Margin = new MarginPadding @@ -127,7 +127,7 @@ namespace osu.Game.Overlays.Mods }, new OsuSpriteText { - Text = mod.Description, + Text = Mod.Description, Font = OsuFont.Default.With(size: 12), RelativeSizeAxes = Axes.X, Truncate = true, @@ -143,6 +143,11 @@ namespace osu.Game.Overlays.Mods Action = Active.Toggle; } + public ModPanel(Mod mod) + : this(new ModState(mod)) + { + } + [BackgroundDependencyLoader(true)] private void load(AudioManager audio, OsuColour colours, ISamplePlaybackDisabler? samplePlaybackDisabler) { diff --git a/osu.Game/Overlays/Mods/UserModSelectOverlay.cs b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs index 8ff5e28c8f..7100446730 100644 --- a/osu.Game/Overlays/Mods/UserModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Mods { } - protected override ModPanel CreateModPanel(Mod mod) => new IncompatibilityDisplayingModPanel(mod); + protected override ModPanel CreateModPanel(ModState modState) => new IncompatibilityDisplayingModPanel(modState); } } } From 05a21fbbe0563605fe002dfe49ff1bb7a87632da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 19:02:45 +0200 Subject: [PATCH 1039/1276] Hoist `ModState` to overlay level --- osu.Game/Overlays/Mods/ModColumn.cs | 69 ++++++++-------------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 47 +++++++++++---- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index c5364fb403..8b3896a88c 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -20,13 +20,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Framework.Lists; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; -using osu.Game.Utils; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -39,6 +37,23 @@ namespace osu.Game.Overlays.Mods public readonly ModType ModType; + private IReadOnlyList availableMods = Array.Empty(); + + /// + /// Sets the list of mods to show in this column. + /// + public IReadOnlyList AvailableMods + { + get => availableMods; + set + { + Debug.Assert(value.All(mod => mod.Mod.Type == ModType)); + + availableMods = value; + asyncLoadPanels(); + } + } + private Func? filter; /// @@ -88,17 +103,6 @@ namespace osu.Game.Overlays.Mods private readonly Key[]? toggleKeys; - private readonly Bindable>> availableMods = new Bindable>>(); - - /// - /// Contains information about state of all mods that are available for the current ruleset in this particular column. - /// - /// - /// Note that the mod instances in this list are owned solely by this column - /// (as in, they are locally-managed clones, to ensure proper isolation from any other external instances). - /// - private IReadOnlyList localAvailableMods = Array.Empty(); - private readonly TextFlowContainer headerText; private readonly Box headerBackground; private readonly Container contentContainer; @@ -258,12 +262,8 @@ namespace osu.Game.Overlays.Mods } [BackgroundDependencyLoader] - private void load(OsuGameBase game, OverlayColourProvider colourProvider, OsuColour colours) + private void load(OverlayColourProvider colourProvider, OsuColour colours) { - availableMods.BindTo(game.AvailableMods); - updateLocalAvailableMods(asyncLoadContent: false); - availableMods.BindValueChanged(_ => updateLocalAvailableMods(asyncLoadContent: true)); - headerBackground.Colour = accentColour = colours.ForModType(ModType); if (toggleAllCheckbox != null) @@ -289,30 +289,13 @@ namespace osu.Game.Overlays.Mods toggleAllCheckbox.LabelText = toggleAllCheckbox.Current.Value ? CommonStrings.DeselectAll : CommonStrings.SelectAll; } - private void updateLocalAvailableMods(bool asyncLoadContent) - { - var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty()) - .Select(m => new ModState(m.DeepClone())) - .ToList(); - - if (newMods.SequenceEqual(localAvailableMods, new FuncEqualityComparer((x, y) => ReferenceEquals(x.Mod, y.Mod)))) - return; - - localAvailableMods = newMods; - - if (asyncLoadContent) - asyncLoadPanels(); - else - onPanelsLoaded(createPanels()); - } - private CancellationTokenSource? cancellationTokenSource; private void asyncLoadPanels() { cancellationTokenSource?.Cancel(); - var panels = createPanels(); + var panels = availableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0))); Task? loadTask; @@ -324,12 +307,6 @@ namespace osu.Game.Overlays.Mods }); } - private IEnumerable createPanels() - { - var panels = localAvailableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0))); - return panels; - } - private void onPanelsLoaded(IEnumerable loaded) { panelFlow.ChildrenEnumerable = loaded; @@ -394,7 +371,7 @@ namespace osu.Game.Overlays.Mods var newSelection = new List(); - foreach (var modState in localAvailableMods) + foreach (var modState in this.availableMods) { var matchingSelectedMod = mods.SingleOrDefault(selected => selected.GetType() == modState.GetType()); @@ -554,10 +531,10 @@ namespace osu.Game.Overlays.Mods int index = Array.IndexOf(toggleKeys, e.Key); if (index < 0) return false; - var panel = panelFlow.ElementAtOrDefault(index); - if (panel == null || panel.Filtered.Value) return false; + var modState = availableMods.ElementAtOrDefault(index); + if (modState == null || modState.Filtered.Value) return false; - panel.Active.Toggle(); + modState.Active.Toggle(); return true; } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b3c3eee15a..ace0576b96 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -22,6 +22,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; +using osu.Game.Utils; using osuTK; using osuTK.Input; @@ -47,9 +48,7 @@ namespace osu.Game.Overlays.Mods set { isValidMod = value ?? throw new ArgumentNullException(nameof(value)); - - if (IsLoaded) - updateAvailableMods(); + filterMods(); } } @@ -64,6 +63,9 @@ namespace osu.Game.Overlays.Mods protected virtual IEnumerable CreateFooterButtons() => createDefaultFooterButtons(); + private readonly Bindable>> availableMods = new Bindable>>(); + private readonly Dictionary> localAvailableMods = new Dictionary>(); + private readonly BindableBool customisationVisible = new BindableBool(); private ModSettingsArea modSettingsArea = null!; @@ -82,7 +84,7 @@ namespace osu.Game.Overlays.Mods } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuGameBase game, OsuColour colours) { Header.Title = ModSelectOverlayStrings.ModSelectTitle; Header.Description = ModSelectOverlayStrings.ModSelectDescription; @@ -184,12 +186,16 @@ namespace osu.Game.Overlays.Mods LighterColour = colours.Pink1 }) }; + + availableMods.BindTo(game.AvailableMods); } protected override void LoadComplete() { base.LoadComplete(); + availableMods.BindValueChanged(_ => createLocalMods(), true); + State.BindValueChanged(_ => samplePlaybackDisabled.Value = State.Value == Visibility.Hidden, true); // This is an optimisation to prevent refreshing the available settings controls when it can be @@ -211,8 +217,6 @@ namespace osu.Game.Overlays.Mods customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); - updateAvailableMods(); - // Start scrolled slightly to the right to give the user a sense that // there is more horizontal content available. ScheduleAfterChildren(() => @@ -272,6 +276,31 @@ namespace osu.Game.Overlays.Mods } }; + private void createLocalMods() + { + localAvailableMods.Clear(); + + foreach (var (modType, mods) in availableMods.Value) + { + var modStates = mods.SelectMany(ModUtils.FlattenMod) + .Select(mod => new ModState(mod.DeepClone())) + .ToArray(); + + localAvailableMods[modType] = modStates; + } + + filterMods(); + + foreach (var column in columnFlow.Columns) + column.AvailableMods = localAvailableMods.GetValueOrDefault(column.ModType, Array.Empty()); + } + + private void filterMods() + { + foreach (var modState in localAvailableMods.Values.SelectMany(m => m)) + modState.Filtered.Value = !modState.Mod.HasImplementation || !IsValidMod.Invoke(modState.Mod); + } + private void updateMultiplier() { if (multiplierDisplay == null) @@ -285,12 +314,6 @@ namespace osu.Game.Overlays.Mods multiplierDisplay.Current.Value = multiplier; } - private void updateAvailableMods() - { - foreach (var column in columnFlow.Columns) - column.Filter = m => m.HasImplementation && isValidMod.Invoke(m); - } - private void updateCustomisation(ValueChangedEvent> valueChangedEvent) { if (customisationButton == null) From 11ae1da65a092266786f4eae988bd0713584dbc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 19:30:06 +0200 Subject: [PATCH 1040/1276] Hoist reference replacement logic to overlay level --- osu.Game/Overlays/Mods/ModColumn.cs | 116 ++------------------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 85 ++++++++++----- 2 files changed, 64 insertions(+), 137 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 8b3896a88c..deaaacd775 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -13,7 +13,6 @@ using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -54,49 +53,18 @@ namespace osu.Game.Overlays.Mods } } - private Func? filter; - /// /// A function determining whether each mod in the column should be displayed. /// A return value of means that the mod is not filtered and therefore its corresponding panel should be displayed. /// A return value of means that the mod is filtered out and therefore its corresponding panel should be hidden. /// - public Func? Filter - { - get => filter; - set - { - filter = value; - updateState(); - } - } + public Func? Filter { get; set; } // TODO: remove later /// /// Determines whether this column should accept user input. /// public Bindable Active = new BindableBool(true); - private readonly Bindable allFiltered = new BindableBool(); - - /// - /// True if all of the panels in this column have been filtered out by the current . - /// - public IBindable AllFiltered => allFiltered; - - /// - /// List of mods marked as selected in this column. - /// - /// - /// Note that the mod instances returned by this property are owned solely by this column - /// (as in, they are locally-managed clones, to ensure proper isolation from any other external instances). - /// - public IReadOnlyList SelectedMods { get; private set; } = Array.Empty(); - - /// - /// Invoked when a mod panel has been selected interactively by the user. - /// - public event Action? SelectionChangedByUser; - protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => base.ReceivePositionalInputAtSubTree(screenSpacePos) && Active.Value; protected virtual ModPanel CreateModPanel(ModState mod) => new ModPanel(mod); @@ -299,7 +267,11 @@ namespace osu.Game.Overlays.Mods Task? loadTask; - latestLoadTask = loadTask = LoadComponentsAsync(panels, onPanelsLoaded, (cancellationTokenSource = new CancellationTokenSource()).Token); + latestLoadTask = loadTask = LoadComponentsAsync(panels, loaded => + { + panelFlow.ChildrenEnumerable = loaded; + updateState(); + }, (cancellationTokenSource = new CancellationTokenSource()).Token); loadTask.ContinueWith(_ => { if (loadTask == latestLoadTask) @@ -307,27 +279,9 @@ namespace osu.Game.Overlays.Mods }); } - private void onPanelsLoaded(IEnumerable loaded) - { - panelFlow.ChildrenEnumerable = loaded; - - updateState(); - - foreach (var panel in panelFlow) - { - panel.Active.BindValueChanged(_ => panelStateChanged(panel)); - } - } - private void updateState() { - foreach (var panel in panelFlow) - { - panel.Active.Value = SelectedMods.Contains(panel.Mod); - panel.ApplyFilter(Filter); - } - - allFiltered.Value = panelFlow.All(panel => panel.Filtered.Value); + Alpha = availableMods.All(mod => mod.Filtered.Value) ? 0 : 1; if (toggleAllCheckbox != null && !SelectionAnimationRunning) { @@ -336,62 +290,6 @@ namespace osu.Game.Overlays.Mods } } - /// - /// This flag helps to determine the source of changes to . - /// If the value is false, then are changing due to a user selection on the UI. - /// If the value is true, then are changing due to an external call. - /// - private bool externalSelectionUpdateInProgress; - - private void panelStateChanged(ModPanel panel) - { - if (externalSelectionUpdateInProgress) - return; - - var newSelectedMods = panel.Active.Value - ? SelectedMods.Append(panel.Mod) - : SelectedMods.Except(panel.Mod.Yield()); - - SelectedMods = newSelectedMods.ToArray(); - updateState(); - SelectionChangedByUser?.Invoke(); - } - - /// - /// Adjusts the set of selected mods in this column to match the passed in . - /// - /// - /// This method exists to be able to receive mod instances that come from potentially-external sources and to copy the changes across to this column's state. - /// uses this to substitute any external mod references in - /// to references that are owned by this column. - /// - internal void SetSelection(IReadOnlyList mods) - { - externalSelectionUpdateInProgress = true; - - var newSelection = new List(); - - foreach (var modState in this.availableMods) - { - var matchingSelectedMod = mods.SingleOrDefault(selected => selected.GetType() == modState.GetType()); - - if (matchingSelectedMod != null) - { - modState.Mod.CopyFrom(matchingSelectedMod); - newSelection.Add(modState.Mod); - } - else - { - modState.Mod.ResetSettingsToDefaults(); - } - } - - SelectedMods = newSelection; - updateState(); - - externalSelectionUpdateInProgress = false; - } - #region Bulk select / deselect private const double initial_multiple_selection_delay = 120; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ace0576b96..567e91e3fa 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -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); + /// + /// This flag helps to determine the source of changes to . + /// If the value is false, then are changing due to a user selection on the UI. + /// If the value is true, then are changing due to an external change. + /// + 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(); + + 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 From 83ba06e7afc42d59273515a1a8b277daeca64af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 19:33:52 +0200 Subject: [PATCH 1041/1276] Extract helper property for accessing all mods --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 567e91e3fa..7e28ab4b1f 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -65,6 +65,7 @@ namespace osu.Game.Overlays.Mods private readonly Bindable>> availableMods = new Bindable>>(); private readonly Dictionary> localAvailableMods = new Dictionary>(); + private IEnumerable allLocalAvailableMods => localAvailableMods.SelectMany(pair => pair.Value); private readonly BindableBool customisationVisible = new BindableBool(); @@ -294,7 +295,7 @@ namespace osu.Game.Overlays.Mods private void filterMods() { - foreach (var modState in localAvailableMods.Values.SelectMany(m => m)) + foreach (var modState in allLocalAvailableMods) modState.Filtered.Value = !modState.Mod.HasImplementation || !IsValidMod.Invoke(modState.Mod); } @@ -372,7 +373,7 @@ namespace osu.Game.Overlays.Mods var newSelection = new List(); - foreach (var modState in localAvailableMods.SelectMany(pair => pair.Value)) + foreach (var modState in allLocalAvailableMods) { var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == modState.Mod.GetType()); @@ -399,10 +400,9 @@ namespace osu.Game.Overlays.Mods if (externalSelectionUpdateInProgress) return; - var candidateSelection = localAvailableMods.SelectMany(pair => pair.Value) - .Where(modState => modState.Active.Value) - .Select(modState => modState.Mod) - .ToArray(); + var candidateSelection = allLocalAvailableMods.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 From fc24a564782aa31381b8eada9e547f871f5c0978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 19:40:56 +0200 Subject: [PATCH 1042/1276] Add protection from recursive updates from external selection --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 7e28ab4b1f..e478b2afcd 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -369,6 +369,9 @@ namespace osu.Game.Overlays.Mods private void updateFromExternalSelection() { + if (externalSelectionUpdateInProgress) + return; + externalSelectionUpdateInProgress = true; var newSelection = new List(); From 7ac6073f13cf31bf6e15489c9f54f31ed87503c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 20:23:24 +0200 Subject: [PATCH 1043/1276] Fix column test scene to work --- .../UserInterface/TestSceneModColumn.cs | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs index e47ae860c6..331509e10f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Linq; using NUnit.Framework; @@ -12,11 +14,9 @@ using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Mods; -using osu.Game.Rulesets.Catch; -using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Taiko; +using osu.Game.Utils; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface @@ -41,20 +41,16 @@ namespace osu.Game.Tests.Visual.UserInterface Child = new ModColumn(modType, false) { Anchor = Anchor.Centre, - Origin = Anchor.Centre + Origin = Anchor.Centre, + AvailableMods = getExampleModsFor(modType) } }); - - AddStep("change ruleset to osu!", () => Ruleset.Value = new OsuRuleset().RulesetInfo); - AddStep("change ruleset to taiko", () => Ruleset.Value = new TaikoRuleset().RulesetInfo); - AddStep("change ruleset to catch", () => Ruleset.Value = new CatchRuleset().RulesetInfo); - AddStep("change ruleset to mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo); } [Test] public void TestMultiSelection() { - ModColumn column = null; + ModColumn column = null!; AddStep("create content", () => Child = new Container { RelativeSizeAxes = Axes.Both, @@ -62,7 +58,8 @@ namespace osu.Game.Tests.Visual.UserInterface Child = column = new ModColumn(ModType.DifficultyIncrease, true) { Anchor = Anchor.Centre, - Origin = Anchor.Centre + Origin = Anchor.Centre, + AvailableMods = getExampleModsFor(ModType.DifficultyIncrease) } }); @@ -91,7 +88,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestFiltering() { - TestModColumn column = null; + TestModColumn column = null!; AddStep("create content", () => Child = new Container { @@ -100,30 +97,31 @@ namespace osu.Game.Tests.Visual.UserInterface Child = column = new TestModColumn(ModType.Fun, true) { Anchor = Anchor.Centre, - Origin = Anchor.Centre + Origin = Anchor.Centre, + AvailableMods = getExampleModsFor(ModType.Fun) } }); - AddStep("set filter", () => column.Filter = mod => mod.Name.Contains("Wind", StringComparison.CurrentCultureIgnoreCase)); + AddStep("set filter", () => setFilter(mod => mod.Name.Contains("Wind", StringComparison.CurrentCultureIgnoreCase))); AddUntilStep("two panels visible", () => column.ChildrenOfType().Count(panel => !panel.Filtered.Value) == 2); clickToggle(); AddUntilStep("wait for animation", () => !column.SelectionAnimationRunning); AddAssert("only visible items selected", () => column.ChildrenOfType().Where(panel => panel.Active.Value).All(panel => !panel.Filtered.Value)); - AddStep("unset filter", () => column.Filter = null); + AddStep("unset filter", () => setFilter(null)); AddUntilStep("all panels visible", () => column.ChildrenOfType().All(panel => !panel.Filtered.Value)); AddAssert("checkbox not selected", () => !column.ChildrenOfType().Single().Current.Value); - AddStep("set filter", () => column.Filter = mod => mod.Name.Contains("Wind", StringComparison.CurrentCultureIgnoreCase)); + AddStep("set filter", () => setFilter(mod => mod.Name.Contains("Wind", StringComparison.CurrentCultureIgnoreCase))); AddUntilStep("two panels visible", () => column.ChildrenOfType().Count(panel => !panel.Filtered.Value) == 2); AddAssert("checkbox selected", () => column.ChildrenOfType().Single().Current.Value); - AddStep("filter out everything", () => column.Filter = _ => false); + AddStep("filter out everything", () => setFilter(_ => false)); AddUntilStep("no panels visible", () => column.ChildrenOfType().All(panel => panel.Filtered.Value)); AddUntilStep("checkbox hidden", () => !column.ChildrenOfType().Single().IsPresent); - AddStep("inset filter", () => column.Filter = null); + AddStep("inset filter", () => setFilter(null)); AddUntilStep("all panels visible", () => column.ChildrenOfType().All(panel => !panel.Filtered.Value)); AddUntilStep("checkbox visible", () => column.ChildrenOfType().Single().IsPresent); @@ -138,7 +136,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestKeyboardSelection() { - ModColumn column = null; + ModColumn column = null!; AddStep("create content", () => Child = new Container { RelativeSizeAxes = Axes.Both, @@ -146,7 +144,8 @@ namespace osu.Game.Tests.Visual.UserInterface Child = column = new ModColumn(ModType.DifficultyReduction, true, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }) { Anchor = Anchor.Centre, - Origin = Anchor.Centre + Origin = Anchor.Centre, + AvailableMods = getExampleModsFor(ModType.DifficultyReduction) } }); @@ -158,7 +157,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("press W again", () => InputManager.Key(Key.W)); AddAssert("NF panel deselected", () => !this.ChildrenOfType().Single(panel => panel.Mod.Acronym == "NF").Active.Value); - AddStep("set filter to NF", () => column.Filter = mod => mod.Acronym == "NF"); + AddStep("set filter to NF", () => setFilter(mod => mod.Acronym == "NF")); AddStep("press W", () => InputManager.Key(Key.W)); AddAssert("NF panel selected", () => this.ChildrenOfType().Single(panel => panel.Mod.Acronym == "NF").Active.Value); @@ -166,12 +165,18 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("press W again", () => InputManager.Key(Key.W)); AddAssert("NF panel deselected", () => !this.ChildrenOfType().Single(panel => panel.Mod.Acronym == "NF").Active.Value); - AddStep("filter out everything", () => column.Filter = _ => false); + AddStep("filter out everything", () => setFilter(_ => false)); AddStep("press W", () => InputManager.Key(Key.W)); AddAssert("NF panel not selected", () => !this.ChildrenOfType().Single(panel => panel.Mod.Acronym == "NF").Active.Value); - AddStep("clear filter", () => column.Filter = null); + AddStep("clear filter", () => setFilter(null)); + } + + private void setFilter(Func? filter) + { + foreach (var modState in this.ChildrenOfType().Single().AvailableMods) + modState.Filtered.Value = filter?.Invoke(modState.Mod) == false; } private class TestModColumn : ModColumn @@ -183,5 +188,13 @@ namespace osu.Game.Tests.Visual.UserInterface { } } + + private static ModState[] getExampleModsFor(ModType modType) + { + return new OsuRuleset().GetModsFor(modType) + .SelectMany(ModUtils.FlattenMod) + .Select(mod => new ModState(mod)) + .ToArray(); + } } } From 52bbce12f16d3ba769c8da6fa6ee6a51a8a65d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 20:23:56 +0200 Subject: [PATCH 1044/1276] Fix not being able to set `AvailableMods` before loaded --- osu.Game/Overlays/Mods/ModColumn.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index deaaacd775..06e96afdca 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -49,7 +49,9 @@ namespace osu.Game.Overlays.Mods Debug.Assert(value.All(mod => mod.Mod.Type == ModType)); availableMods = value; - asyncLoadPanels(); + + if (IsLoaded) + asyncLoadPanels(); } } @@ -249,6 +251,7 @@ namespace osu.Game.Overlays.Mods base.LoadComplete(); toggleAllCheckbox?.Current.BindValueChanged(_ => updateToggleAllText(), true); + asyncLoadPanels(); } private void updateToggleAllText() From b5a9f1310a706491c6019b2a2d159583620f4972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 20:28:11 +0200 Subject: [PATCH 1045/1276] Fix select/deselect all toggle not working correctly after changes --- osu.Game/Overlays/Mods/ModColumn.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 06e96afdca..c1904a0bc0 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -50,6 +50,14 @@ namespace osu.Game.Overlays.Mods availableMods = value; + foreach (var mod in availableMods) + { + mod.Active.BindValueChanged(_ => updateState()); + mod.Filtered.BindValueChanged(_ => updateState()); + } + + updateState(); + if (IsLoaded) asyncLoadPanels(); } From 1c0166367d1f12cfaf5194f467d7c64220e1409b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 20:29:28 +0200 Subject: [PATCH 1046/1276] Fix remaining column operations being coupled to drawables --- osu.Game/Overlays/Mods/ModColumn.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index c1904a0bc0..d7ce08e124 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -296,8 +296,8 @@ namespace osu.Game.Overlays.Mods if (toggleAllCheckbox != null && !SelectionAnimationRunning) { - toggleAllCheckbox.Alpha = panelFlow.Any(panel => !panel.Filtered.Value) ? 1 : 0; - toggleAllCheckbox.Current.Value = panelFlow.Where(panel => !panel.Filtered.Value).All(panel => panel.Active.Value); + toggleAllCheckbox.Alpha = availableMods.Any(panel => !panel.Filtered.Value) ? 1 : 0; + toggleAllCheckbox.Current.Value = availableMods.Where(panel => !panel.Filtered.Value).All(panel => panel.Active.Value); } } @@ -342,7 +342,7 @@ namespace osu.Game.Overlays.Mods { pendingSelectionOperations.Clear(); - foreach (var button in panelFlow.Where(b => !b.Active.Value && !b.Filtered.Value)) + foreach (var button in availableMods.Where(b => !b.Active.Value && !b.Filtered.Value)) pendingSelectionOperations.Enqueue(() => button.Active.Value = true); } @@ -353,7 +353,7 @@ namespace osu.Game.Overlays.Mods { pendingSelectionOperations.Clear(); - foreach (var button in panelFlow.Where(b => b.Active.Value && !b.Filtered.Value)) + foreach (var button in availableMods.Where(b => b.Active.Value && !b.Filtered.Value)) pendingSelectionOperations.Enqueue(() => button.Active.Value = false); } From 2266a5c9a0588beb5e188197a0dd03c67457dd46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 20:38:53 +0200 Subject: [PATCH 1047/1276] Remove no-longer-necessary `ModColumn.Filter` --- osu.Game/Overlays/Mods/ModColumn.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index d7ce08e124..9bb3f8bd9e 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -63,13 +63,6 @@ namespace osu.Game.Overlays.Mods } } - /// - /// A function determining whether each mod in the column should be displayed. - /// A return value of means that the mod is not filtered and therefore its corresponding panel should be displayed. - /// A return value of means that the mod is filtered out and therefore its corresponding panel should be hidden. - /// - public Func? Filter { get; set; } // TODO: remove later - /// /// Determines whether this column should accept user input. /// From 93539160adb48a6be40d69ed33c216d46f52327e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 21:57:57 +0200 Subject: [PATCH 1048/1276] Remove no-longer-necessary guard --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index e478b2afcd..d94f663962 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -12,7 +12,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; -using osu.Framework.Lists; using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Configuration; @@ -407,11 +406,6 @@ namespace osu.Game.Overlays.Mods .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 - if (candidateSelection.SequenceEqual(SelectedMods.Value, new FuncEqualityComparer(ReferenceEquals))) - return; - SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection); } From 981ead68bf8a54ece314e00bf0d68a16aa331622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 22:20:00 +0200 Subject: [PATCH 1049/1276] Ensure local mods are constructed in time for `Pop{In,Out}()` --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d94f663962..d068839ab0 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -192,10 +192,11 @@ namespace osu.Game.Overlays.Mods protected override void LoadComplete() { - base.LoadComplete(); - + // this is called before base call so that the mod state is populated early, and the transition in `PopIn()` can play out properly. availableMods.BindValueChanged(_ => createLocalMods(), true); + base.LoadComplete(); + State.BindValueChanged(_ => samplePlaybackDisabled.Value = State.Value == Visibility.Hidden, true); // This is an optimisation to prevent refreshing the available settings controls when it can be From cced8609f6d2190156d24c75d5a048166761eb74 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 11 May 2022 22:51:15 +0100 Subject: [PATCH 1050/1276] Add `setCurrent` param to `ChannelManager.JoinChannel` --- osu.Game/Online/Chat/ChannelManager.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 47e45e67d1..1fe784f68b 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -420,10 +420,11 @@ namespace osu.Game.Online.Chat /// Joins a channel if it has not already been joined. Must be called from the update thread. /// /// The channel to join. + /// Set the channel to join as the current channel if the current channel is null. /// The joined channel. Note that this may not match the parameter channel as it is a backed object. - public Channel JoinChannel(Channel channel) => joinChannel(channel, true); + public Channel JoinChannel(Channel channel, bool setCurrent = true) => joinChannel(channel, true, setCurrent); - private Channel joinChannel(Channel channel, bool fetchInitialMessages = false) + private Channel joinChannel(Channel channel, bool fetchInitialMessages = false, bool setCurrent = true) { if (channel == null) return null; @@ -439,7 +440,7 @@ namespace osu.Game.Online.Chat case ChannelType.Multiplayer: // join is implicit. happens when you join a multiplayer game. // this will probably change in the future. - joinChannel(channel, fetchInitialMessages); + joinChannel(channel, fetchInitialMessages, setCurrent); return channel; case ChannelType.PM: @@ -460,7 +461,7 @@ namespace osu.Game.Online.Chat default: var req = new JoinChannelRequest(channel); - req.Success += () => joinChannel(channel, fetchInitialMessages); + req.Success += () => joinChannel(channel, fetchInitialMessages, setCurrent); req.Failure += ex => LeaveChannel(channel); api.Queue(req); return channel; @@ -472,7 +473,8 @@ namespace osu.Game.Online.Chat this.fetchInitialMessages(channel); } - CurrentChannel.Value ??= channel; + if (setCurrent) + CurrentChannel.Value ??= channel; return channel; } From db371ab0685621d868aa9bb036e963f487ee1a6a Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 11 May 2022 22:52:15 +0100 Subject: [PATCH 1051/1276] Use `CurrentChannel == null` to show the channel selector --- .../Overlays/Chat/ChannelList/ChannelList.cs | 8 ++--- .../Chat/ChannelList/ChannelListItem.cs | 5 +--- .../Chat/ChannelList/ChannelListSelector.cs | 12 ++++---- osu.Game/Overlays/ChatOverlayV2.cs | 29 ++----------------- 4 files changed, 12 insertions(+), 42 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 076dc5719e..6bdf5ee084 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -19,11 +18,9 @@ namespace osu.Game.Overlays.Chat.ChannelList { public class ChannelList : Container { - public Action? OnRequestSelect; + public Action? OnRequestSelect; public Action? OnRequestLeave; - public readonly BindableBool SelectorActive = new BindableBool(); - private readonly Dictionary channelMap = new Dictionary(); private ChannelListItemFlow publicChannelFlow = null!; @@ -56,7 +53,7 @@ namespace osu.Game.Overlays.Chat.ChannelList new ChannelListSelector { Margin = new MarginPadding { Bottom = 10 }, - SelectorActive = { BindTarget = SelectorActive }, + Action = () => OnRequestSelect?.Invoke(null), }, privateChannelFlow = new ChannelListItemFlow("DIRECT MESSAGES"), }, @@ -73,7 +70,6 @@ namespace osu.Game.Overlays.Chat.ChannelList ChannelListItem item = new ChannelListItem(channel); item.OnRequestSelect += chan => OnRequestSelect?.Invoke(chan); item.OnRequestLeave += chan => OnRequestLeave?.Invoke(chan); - item.SelectorActive.BindTarget = SelectorActive; ChannelListItemFlow flow = getFlowForChannel(channel); channelMap.Add(channel, item); diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index c5ac87f527..dd571c9ad9 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -31,8 +31,6 @@ namespace osu.Game.Overlays.Chat.ChannelList public readonly BindableBool Unread = new BindableBool(); - public readonly BindableBool SelectorActive = new BindableBool(); - private Box hoverBox = null!; private Box selectBox = null!; private OsuSpriteText text = null!; @@ -127,7 +125,6 @@ namespace osu.Game.Overlays.Chat.ChannelList base.LoadComplete(); selectedChannel.BindValueChanged(_ => updateState(), true); - SelectorActive.BindValueChanged(_ => updateState(), true); Unread.BindValueChanged(_ => updateState(), true); } @@ -163,7 +160,7 @@ namespace osu.Game.Overlays.Chat.ChannelList private void updateState() { - bool selected = selectedChannel.Value == Channel && !SelectorActive.Value; + bool selected = selectedChannel.Value == Channel; if (selected) selectBox.FadeIn(300, Easing.OutQuint); diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs index 9cba93ffa5..7beef7b16c 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs @@ -12,17 +12,19 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; namespace osu.Game.Overlays.Chat.ChannelList { public class ChannelListSelector : OsuClickableContainer { - public readonly BindableBool SelectorActive = new BindableBool(); - private Box hoverBox = null!; private Box selectBox = null!; private OsuSpriteText text = null!; + [Resolved] + private Bindable currentChannel { get; set; } = null!; + [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -69,9 +71,9 @@ namespace osu.Game.Overlays.Chat.ChannelList { base.LoadComplete(); - SelectorActive.BindValueChanged(selector => + currentChannel.BindValueChanged(channel => { - if (selector.NewValue) + if (channel.NewValue == null) { text.FadeColour(colourProvider.Content1, 300, Easing.OutQuint); selectBox.FadeIn(300, Easing.OutQuint); @@ -82,8 +84,6 @@ namespace osu.Game.Overlays.Chat.ChannelList selectBox.FadeOut(200, Easing.OutQuint); } }, true); - - Action = () => SelectorActive.Value = true; } protected override bool OnHover(HoverEvent e) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index e59bee7977..19609f0702 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -100,7 +100,6 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Y, Width = side_bar_width, Padding = new MarginPadding { Top = top_bar_height }, - SelectorActive = { BindTarget = selectorActive }, }, new Container { @@ -157,20 +156,10 @@ namespace osu.Game.Overlays channelManager.JoinedChannels.BindCollectionChanged(joinedChannelsChanged, true); channelManager.AvailableChannels.BindCollectionChanged(availableChannelsChanged, true); - channelList.OnRequestSelect += channel => - { - // Manually selecting a channel should dismiss the selector - selectorActive.Value = false; - channelManager.CurrentChannel.Value = channel; - }; + channelList.OnRequestSelect += channel => channelManager.CurrentChannel.Value = channel; channelList.OnRequestLeave += channel => channelManager.LeaveChannel(channel); - channelListing.OnRequestJoin += channel => - { - channelManager.JoinChannel(channel); - // Manually joining a channel should keep the selector open - selectorActive.Value = true; - }; + channelListing.OnRequestJoin += channel => channelManager.JoinChannel(channel, false); channelListing.OnRequestLeave += channel => channelManager.LeaveChannel(channel); textBar.OnSearchTermsChanged += searchTerms => channelListing.SearchTerm = searchTerms; @@ -257,21 +246,9 @@ namespace osu.Game.Overlays loading.Show(); - // Channel is null when leaving the currently selected channel if (newChannel == null) { - // Don't need to autoswitch if the selector is visible - if (selectorActive.Value) - return; - - // Find another channel to switch to - newChannel = channelManager.JoinedChannels.FirstOrDefault(c => c != channel.OldValue); - - if (newChannel == null) - selectorActive.Value = true; - else - currentChannel.Value = newChannel; - + selectorActive.Value = true; return; } From c45e8f619b286b2af42a421f9d0337073b7bf5d4 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 11 May 2022 22:52:25 +0100 Subject: [PATCH 1052/1276] Update `ChannelList` test scene --- .../Visual/Online/TestSceneChannelList.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs index a3bfbd47a3..9929642539 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -25,7 +25,6 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly Bindable selected = new Bindable(); - private OsuSpriteText selectorText; private OsuSpriteText selectedText; private OsuSpriteText leaveText; private ChannelList channelList; @@ -43,21 +42,12 @@ namespace osu.Game.Tests.Visual.Online Height = 0.7f, RowDimensions = new[] { - new Dimension(GridSizeMode.Absolute, 20), new Dimension(GridSizeMode.Absolute, 20), new Dimension(GridSizeMode.Absolute, 20), new Dimension(), }, Content = new[] { - new Drawable[] - { - selectorText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - }, new Drawable[] { selectedText = new OsuSpriteText @@ -89,7 +79,6 @@ namespace osu.Game.Tests.Visual.Online channelList.OnRequestSelect += channel => { - channelList.SelectorActive.Value = false; selected.Value = channel; }; @@ -101,12 +90,6 @@ namespace osu.Game.Tests.Visual.Online channelList.RemoveChannel(channel); }; - channelList.SelectorActive.BindValueChanged(change => - { - selectorText.Text = $"Channel Selector Active: {change.NewValue}"; - selected.Value = null; - }, true); - selected.BindValueChanged(change => { selectedText.Text = $"Selected Channel: {change.NewValue?.Name ?? "[null]"}"; From b794deb5c544e5612745e1d33c32fa3122edd7e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 12:06:51 +0900 Subject: [PATCH 1053/1276] Add null checks to screen context insertion --- osu.Game/OsuGame.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 9edcf90132..3d56d33689 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -14,6 +14,7 @@ using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -1203,8 +1204,8 @@ namespace osu.Game { scope.Contexts[@"screen stack"] = new { - Current = newScreen.GetType().Name, - Previous = current.GetType().Name, + Current = newScreen?.GetType().ReadableName(), + Previous = current?.GetType().ReadableName(), }; }); From fda61943b074eea3500b32422b032aacf5cac8a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 13:04:17 +0900 Subject: [PATCH 1054/1276] Update distance snap test when cursor at centre of grid to be in line with expectations --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 3c3c5cb939..c50aec40a5 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -113,7 +113,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public void TestCursorInCentre() { AddStep("move mouse to centre", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position))); - assertSnappedDistance(0); + assertSnappedDistance(beat_length); + } + + [Test] + public void TestCursorAlmostInCentre() + { + AddStep("move mouse to almost centre", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position) + new Vector2(1))); + assertSnappedDistance(beat_length); } [Test] From f51607521cef17d7ff4ef62082febe76e7252204 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 13:11:51 +0900 Subject: [PATCH 1055/1276] Fix distance snap providing zero-distance snaps incorrectly --- .../Edit/Compose/Components/CircularDistanceSnapGrid.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 2c6bb766ad..cb50ea0d4b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Utils; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osuTK; @@ -76,14 +77,19 @@ namespace osu.Game.Screens.Edit.Compose.Components Vector2 travelVector = (position - StartPosition); + // We need a non-zero travel vector in order to find a valid direction. if (travelVector == Vector2.Zero) - return (StartPosition, StartTime); + travelVector = new Vector2(0, -1); float travelLength = travelVector.Length; // FindSnappedDistance will always round down, but we want to potentially round upwards. travelLength += DistanceBetweenTicks / 2; + // We never want to snap towards zero. + if (travelLength < DistanceBetweenTicks) + travelLength = DistanceBetweenTicks; + // When interacting with the resolved snap provider, the distance spacing multiplier should first be removed // to allow for snapping at a non-multiplied ratio. float snappedDistance = SnapProvider.FindSnappedDistance(ReferenceObject, travelLength / distanceSpacingMultiplier); From 2dfed6eda17ac3747fe6f3ac6d1406e25ef0a54e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 13:53:57 +0900 Subject: [PATCH 1056/1276] Remove `selectorActive` bindable completely --- osu.Game/Overlays/ChatOverlayV2.cs | 34 +++++++++++++----------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 19609f0702..60b25ef52d 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -50,8 +50,6 @@ namespace osu.Game.Overlays private const float side_bar_width = 190; private const float chat_bar_height = 60; - private readonly BindableBool selectorActive = new BindableBool(); - [Resolved] private OsuConfigManager config { get; set; } = null!; @@ -136,7 +134,6 @@ namespace osu.Game.Overlays Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, Padding = new MarginPadding { Left = side_bar_width }, - ShowSearch = { BindTarget = selectorActive }, }, }; } @@ -145,8 +142,6 @@ namespace osu.Game.Overlays { base.LoadComplete(); - loading.Show(); - config.BindWith(OsuSetting.ChatDisplayHeight, chatHeight); chatHeight.BindValueChanged(height => { Height = height.NewValue; }, true); @@ -164,8 +159,6 @@ namespace osu.Game.Overlays textBar.OnSearchTermsChanged += searchTerms => channelListing.SearchTerm = searchTerms; textBar.OnChatMessageCommitted += handleChatMessage; - - selectorActive.BindValueChanged(v => channelListing.State.Value = v.NewValue ? Visibility.Visible : Visibility.Hidden, true); } /// @@ -244,22 +237,25 @@ namespace osu.Game.Overlays { Channel? newChannel = channel.NewValue; - loading.Show(); - if (newChannel == null) { - selectorActive.Value = true; - return; + // null channel denotes that we should be showing the listing. + channelListing.State.Value = Visibility.Visible; + textBar.ShowSearch.Value = true; } - - selectorActive.Value = false; - - LoadComponentAsync(new DrawableChannel(newChannel), loaded => + else { - currentChannelContainer.Clear(); - currentChannelContainer.Add(loaded); - loading.Hide(); - }); + channelListing.State.Value = Visibility.Hidden; + textBar.ShowSearch.Value = false; + + loading.Show(); + LoadComponentAsync(new DrawableChannel(newChannel), loaded => + { + currentChannelContainer.Clear(); + currentChannelContainer.Add(loaded); + loading.Hide(); + }); + } } private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) From 1d38e5bd33ad8764ae3b16b468a967430174f3e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 13:58:10 +0900 Subject: [PATCH 1057/1276] Add notes about weird local handling in `ChannelListSelector` --- osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs index 7beef7b16c..a07aad2041 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs @@ -73,6 +73,8 @@ namespace osu.Game.Overlays.Chat.ChannelList currentChannel.BindValueChanged(channel => { + // This logic should be handled by the chat overlay rather than this component. + // Selected state should be moved to an abstract class and shared with ChannelListItem. if (channel.NewValue == null) { text.FadeColour(colourProvider.Content1, 300, Easing.OutQuint); From c54ca937c505c7056daca10b1fd2f65a938c7d69 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 12 May 2022 14:55:20 +0900 Subject: [PATCH 1058/1276] Fix CI inspections --- .../Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index cb50ea0d4b..771612fcf1 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -7,7 +7,6 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Utils; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osuTK; From c0abce918fbacbd57f10a100d04cc1649d8bfb73 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 15:23:41 +0900 Subject: [PATCH 1059/1276] Add `enum` to snap method as alternative to mutliple nested invocations --- .../Edit/CatchHitObjectComposer.cs | 15 +++++--- .../Editor/TestSceneManiaBeatSnapGrid.cs | 7 +--- .../Edit/ManiaHitObjectComposer.cs | 4 +- .../Edit/OsuHitObjectComposer.cs | 37 ++++++++----------- .../Editing/TestSceneDistanceSnapGrid.cs | 5 +-- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 23 ++++++------ .../Rulesets/Edit/IPositionSnapProvider.cs | 14 +------ osu.Game/Rulesets/Edit/SnapType.cs | 15 ++++++++ .../Compose/Components/BlueprintContainer.cs | 4 +- .../Compose/Components/Timeline/Timeline.cs | 5 +-- 10 files changed, 62 insertions(+), 67 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/SnapType.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 630a2cf645..6f59b3e543 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -6,6 +6,7 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; @@ -89,15 +90,19 @@ namespace osu.Game.Rulesets.Catch.Edit new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }) }); - public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) { - var result = base.FindSnappedPositionAndTime(screenSpacePosition); + var result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType); + result.ScreenSpacePosition.X = screenSpacePosition.X; - if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(result.ScreenSpacePosition) is SnapResult snapResult && - Vector2.Distance(snapResult.ScreenSpacePosition, result.ScreenSpacePosition) < distance_snap_radius) + if (snapType.HasFlagFast(SnapType.Grids)) { - result = snapResult; + if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(result.ScreenSpacePosition) is SnapResult snapResult && + Vector2.Distance(snapResult.ScreenSpacePosition, result.ScreenSpacePosition) < distance_snap_radius) + { + result = snapResult; + } } return result; diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs index 4bb049b1a4..6130a80bb4 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs @@ -97,12 +97,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor set => InternalChild = value; } - public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) - { - throw new System.NotImplementedException(); - } - - public override SnapResult FindSnappedPosition(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) { throw new System.NotImplementedException(); } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index fef315e2ef..3c022dafc0 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -56,9 +56,9 @@ namespace osu.Game.Rulesets.Mania.Edit protected override Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => Playfield.GetColumnByPosition(screenSpacePosition); - public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) { - var result = base.FindSnappedPositionAndTime(screenSpacePosition); + var result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType); switch (ScrollingInfo.Direction.Value) { diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 02beb0f2a4..b0d6170190 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -123,33 +124,27 @@ namespace osu.Game.Rulesets.Osu.Edit } } - public override SnapResult FindSnappedPosition(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) { - if (snapToVisibleBlueprints(screenSpacePosition, out var snapResult)) + if (snapType.HasFlagFast(SnapType.NearbyObjects) && snapToVisibleBlueprints(screenSpacePosition, out var snapResult)) return snapResult; - return new SnapResult(screenSpacePosition, null); - } - - public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) - { - var positionSnap = FindSnappedPosition(screenSpacePosition); - if (positionSnap.ScreenSpacePosition != screenSpacePosition) - return positionSnap; - - if (distanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) + if (snapType.HasFlagFast(SnapType.Grids)) { - (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); - return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition)); + if (distanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) + { + (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); + return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition)); + } + + if (rectangularGridSnapToggle.Value == TernaryState.True) + { + Vector2 pos = rectangularPositionSnapGrid.GetSnappedPosition(rectangularPositionSnapGrid.ToLocalSpace(screenSpacePosition)); + return new SnapResult(rectangularPositionSnapGrid.ToScreenSpace(pos), null, PlayfieldAtScreenSpacePosition(screenSpacePosition)); + } } - if (rectangularGridSnapToggle.Value == TernaryState.True) - { - Vector2 pos = rectangularPositionSnapGrid.GetSnappedPosition(rectangularPositionSnapGrid.ToLocalSpace(screenSpacePosition)); - return new SnapResult(rectangularPositionSnapGrid.ToScreenSpace(pos), null, PlayfieldAtScreenSpacePosition(screenSpacePosition)); - } - - return base.FindSnappedPositionAndTime(screenSpacePosition); + return base.FindSnappedPositionAndTime(screenSpacePosition, snapType); } private bool snapToVisibleBlueprints(Vector2 screenSpacePosition, out SnapResult snapResult) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 3aa3481cbf..ef07c3e411 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -185,10 +185,7 @@ namespace osu.Game.Tests.Visual.Editing private class SnapProvider : IDistanceSnapProvider { - public SnapResult FindSnappedPosition(Vector2 screenSpacePosition) => - new SnapResult(screenSpacePosition, null); - - public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); + public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.Grids) => new SnapResult(screenSpacePosition, 0); public Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 216510fcf3..f8d796a778 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -7,6 +7,7 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; @@ -361,20 +362,23 @@ namespace osu.Game.Rulesets.Edit /// The most relevant . protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield; - public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) + public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) { var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition); double? targetTime = null; - if (playfield is ScrollingPlayfield scrollingPlayfield) + if (snapType.HasFlagFast(SnapType.Grids)) { - targetTime = scrollingPlayfield.TimeAtScreenSpacePosition(screenSpacePosition); + if (playfield is ScrollingPlayfield scrollingPlayfield) + { + targetTime = scrollingPlayfield.TimeAtScreenSpacePosition(screenSpacePosition); - // apply beat snapping - targetTime = BeatSnapProvider.SnapTime(targetTime.Value); + // apply beat snapping + targetTime = BeatSnapProvider.SnapTime(targetTime.Value); - // convert back to screen space - screenSpacePosition = scrollingPlayfield.ScreenSpacePositionAtTime(targetTime.Value); + // convert back to screen space + screenSpacePosition = scrollingPlayfield.ScreenSpacePositionAtTime(targetTime.Value); + } } return new SnapResult(screenSpacePosition, targetTime, playfield); @@ -414,10 +418,7 @@ namespace osu.Game.Rulesets.Edit #region IPositionSnapProvider - public abstract SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition); - - public virtual SnapResult FindSnappedPosition(Vector2 screenSpacePosition) => - new SnapResult(screenSpacePosition, null); + public abstract SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All); #endregion } diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index 837b04424a..a6a6e39e23 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -8,7 +8,6 @@ namespace osu.Game.Rulesets.Edit { /// /// A snap provider which given a proposed position for a hit object, potentially offers a more correct position and time value inferred from the context of the beatmap. - /// Provided values are inferred in an isolated context, without consideration of other nearby hit objects. /// [Cached] public interface IPositionSnapProvider @@ -16,18 +15,9 @@ namespace osu.Game.Rulesets.Edit /// /// Given a position, find a valid time and position snap. /// - /// - /// This call should be equivalent to running with any additional logic that can be performed without the time immutability restriction. - /// /// The screen-space position to be snapped. + /// The type of snapping to apply. /// The time and position post-snapping. - SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition); - - /// - /// Given a position, find a valid position snap, without changing the time value. - /// - /// The screen-space position to be snapped. - /// The position post-snapping. Time will always be null. - SnapResult FindSnappedPosition(Vector2 screenSpacePosition); + SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All); } } diff --git a/osu.Game/Rulesets/Edit/SnapType.cs b/osu.Game/Rulesets/Edit/SnapType.cs new file mode 100644 index 0000000000..4ab67fee63 --- /dev/null +++ b/osu.Game/Rulesets/Edit/SnapType.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Rulesets.Edit +{ + [Flags] + public enum SnapType + { + NearbyObjects = 0, + Grids = 1, + All = NearbyObjects | Grids, + } +} diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 186c66e0af..d56dc176f6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -486,7 +486,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Vector2 originalPosition = movementBlueprintOriginalPositions[i]; var testPosition = originalPosition + distanceTravelled; - var positionalResult = snapProvider.FindSnappedPosition(testPosition); + var positionalResult = snapProvider.FindSnappedPositionAndTime(testPosition, SnapType.NearbyObjects); if (positionalResult.ScreenSpacePosition == testPosition) continue; @@ -505,7 +505,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Vector2 movePosition = movementBlueprintOriginalPositions.First() + distanceTravelled; // Retrieve a snapped position. - var result = snapProvider?.FindSnappedPositionAndTime(movePosition); + var result = snapProvider?.FindSnappedPositionAndTime(movePosition, ~SnapType.NearbyObjects); if (result == null) { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 7e66c57917..992ab7947e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -303,10 +303,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// public double VisibleRange => track.Length / Zoom; - public SnapResult FindSnappedPosition(Vector2 screenSpacePosition) => - new SnapResult(screenSpacePosition, null); - - public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => + public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) => new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition)))); private double getTimeFromPosition(Vector2 localPosition) => From f7e055dbfee9b68b6f93b69262e04c281a836245 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 16:13:31 +0900 Subject: [PATCH 1060/1276] Move mania note height offset application to a much more suitable location --- .../Blueprints/ManiaPlacementBlueprint.cs | 25 ++++++++++++++- .../Edit/ManiaHitObjectComposer.cs | 31 +++---------------- .../UI/Scrolling/ScrollingPlayfield.cs | 2 +- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index 8f25668dd0..ad95fd5e87 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -5,7 +5,10 @@ using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Skinning.Default; using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Mania.Edit.Blueprints @@ -52,8 +55,28 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints { base.UpdateTimeAndPosition(result); + var playfield = (Column)result.Playfield; + + // Apply an offset to better align with the visual grid. + // This should only be applied during placement, as during selection / drag operations the movement is relative + // to the initial point of interaction rather than the grid. + switch (playfield.ScrollingInfo.Direction.Value) + { + case ScrollingDirection.Down: + result.ScreenSpacePosition -= new Vector2(0, getNoteHeight(playfield) / 2); + break; + + case ScrollingDirection.Up: + result.ScreenSpacePosition += new Vector2(0, getNoteHeight(playfield) / 2); + break; + } + if (PlacementActive == PlacementState.Waiting) - Column = result.Playfield as Column; + Column = playfield; } + + private float getNoteHeight(Column resultPlayfield) => + resultPlayfield.ToScreenSpace(new Vector2(DefaultNotePiece.NOTE_HEIGHT)).Y - + resultPlayfield.ToScreenSpace(Vector2.Zero).Y; } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 3c022dafc0..c389e1bced 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -1,15 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Mania.Objects; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Input; -using osu.Game.Rulesets.Mania.Skinning.Default; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -56,28 +55,6 @@ namespace osu.Game.Rulesets.Mania.Edit protected override Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => Playfield.GetColumnByPosition(screenSpacePosition); - public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) - { - var result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType); - - switch (ScrollingInfo.Direction.Value) - { - case ScrollingDirection.Down: - result.ScreenSpacePosition -= new Vector2(0, getNoteHeight() / 2); - break; - - case ScrollingDirection.Up: - result.ScreenSpacePosition += new Vector2(0, getNoteHeight() / 2); - break; - } - - return result; - } - - private float getNoteHeight() => - Playfield.GetColumn(0).ToScreenSpace(new Vector2(DefaultNotePiece.NOTE_HEIGHT)).Y - - Playfield.GetColumn(0).ToScreenSpace(Vector2.Zero).Y; - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) { drawableRuleset = new DrawableManiaEditorRuleset(ruleset, beatmap, mods); diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 2b75f93f9e..782255733f 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.UI.Scrolling public new ScrollingHitObjectContainer HitObjectContainer => (ScrollingHitObjectContainer)base.HitObjectContainer; [Resolved] - protected IScrollingInfo ScrollingInfo { get; private set; } + public IScrollingInfo ScrollingInfo { get; private set; } [BackgroundDependencyLoader] private void load() From 10e41d018af6768090cdcebd41ebdc4a4e6c14c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 17:36:35 +0900 Subject: [PATCH 1061/1276] Fix toggling hit animations on the editor not applying immediately --- osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs | 9 +++++++++ osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs index c89527d8bd..866e41d644 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Lists; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; @@ -46,15 +47,23 @@ namespace osu.Game.Rulesets.Osu.Edit HitPolicy = new AnyOrderHitPolicy(); } + private readonly WeakList drawableHitObjects = new WeakList(); + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { hitAnimations = config.GetBindable(OsuSetting.EditorHitAnimations); + hitAnimations.BindValueChanged(_ => + { + foreach (var d in drawableHitObjects) d.RefreshStateTransforms(); + }); } protected override void OnNewDrawableHitObject(DrawableHitObject d) { d.ApplyCustomUpdateState += updateState; + + drawableHitObjects.Add(d); } private void updateState(DrawableHitObject hitObject, ArmedState state) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 5531bf8b5a..2e573a7f85 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -448,7 +448,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Reapplies the current . /// - protected void RefreshStateTransforms() => updateState(State.Value, true); + public void RefreshStateTransforms() => updateState(State.Value, true); /// /// Apply (generally fade-in) transforms leading into the start time. From c4854d40048fde964eff9f54e01e60458c052555 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 17:55:12 +0900 Subject: [PATCH 1062/1276] Fix slider ball rotation becoming undefined when time is not flowing smoothly --- osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs index 8943a91076..710967b741 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs @@ -195,16 +195,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default public void UpdateProgress(double completionProgress) { - var newPos = drawableSlider.HitObject.CurvePositionAt(completionProgress); + Position = drawableSlider.HitObject.CurvePositionAt(completionProgress); - var diff = lastPosition.HasValue ? lastPosition.Value - newPos : newPos - drawableSlider.HitObject.CurvePositionAt(completionProgress + 0.01f); - if (diff == Vector2.Zero) + var diff = lastPosition.HasValue ? lastPosition.Value - Position : Position - drawableSlider.HitObject.CurvePositionAt(completionProgress + 0.01f); + + // Ensure the value is substantially high enough to allow for Atan2 to get a valid angle. + if (diff.LengthFast < 0.01f) return; - Position = newPos; ball.Rotation = -90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI); - - lastPosition = newPos; + lastPosition = Position; } private class FollowCircleContainer : CircularContainer From f5649b926a09976f05050534e074f7131c8b46c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 18:04:20 +0900 Subject: [PATCH 1063/1276] Use `AliveObjects` rather than tracking all hitobjects manually --- osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs index 866e41d644..72d04d4211 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Lists; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; @@ -47,23 +46,20 @@ namespace osu.Game.Rulesets.Osu.Edit HitPolicy = new AnyOrderHitPolicy(); } - private readonly WeakList drawableHitObjects = new WeakList(); - [BackgroundDependencyLoader] private void load(OsuConfigManager config) { hitAnimations = config.GetBindable(OsuSetting.EditorHitAnimations); hitAnimations.BindValueChanged(_ => { - foreach (var d in drawableHitObjects) d.RefreshStateTransforms(); + foreach (var d in HitObjectContainer.AliveObjects) + d.RefreshStateTransforms(); }); } protected override void OnNewDrawableHitObject(DrawableHitObject d) { d.ApplyCustomUpdateState += updateState; - - drawableHitObjects.Add(d); } private void updateState(DrawableHitObject hitObject, ArmedState state) From 4463a26f4e3c00287de0532fc14b02fdb3b96724 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 12 May 2022 17:19:07 +0900 Subject: [PATCH 1064/1276] Refactor opacity computation algorithm --- .../Preprocessing/OsuDifficultyHitObject.cs | 31 ++++++++++++++----- .../Difficulty/Skills/Flashlight.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 8 ++--- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 5cbddb5772..cf4802d282 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -4,6 +4,7 @@ using System; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; using osuTK; @@ -85,19 +86,33 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing setDistances(clockRate); } - public double Opacity(double mapTime, bool hidden) + public double OpacityAt(double time, bool hidden) { - double ms = BaseObject.StartTime - mapTime; - if (ms < 0) + if (time > BaseObject.StartTime) + { + // Consider a hitobject as being invisible when its start time is passed. + // In reality the hitobject will be visible beyond its start time up until its hittable window has passed, + // but this is an approximation and such a case is unlikely to be hit where this function is used. return 0.0; + } - double preemptTime = BaseObject.TimePreempt; - double fadeInTime = BaseObject.TimeFadeIn; + double fadeInStartTime = BaseObject.StartTime - BaseObject.TimePreempt; + double fadeInDuration = BaseObject.TimeFadeIn; if (hidden) - return Math.Clamp(Math.Min((1.0 - ms / preemptTime) * 2.5, (ms / preemptTime - 0.3) * (1.0 / 0.3)), 0.0, 1.0); - else - return Math.Clamp((preemptTime - ms) / fadeInTime, 0.0, 1.0); + { + // Taken from OsuModHidden. + double fadeOutStartTime = BaseObject.StartTime - BaseObject.TimePreempt + BaseObject.TimeFadeIn; + double fadeOutDuration = BaseObject.TimePreempt * OsuModHidden.FADE_OUT_DURATION_MULTIPLIER; + + return Math.Min + ( + Math.Clamp((time - fadeInStartTime) / fadeInDuration, 0.0, 1.0), + 1.0 - Math.Clamp((time - fadeOutStartTime) / fadeOutDuration, 0.0, 1.0) + ); + } + + return Math.Clamp((time - fadeInStartTime) / fadeInDuration, 0.0, 1.0); } private void setDistances(double clockRate) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 5d2052046b..d93007fae5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0); // Bonus based on how visible the object is. - double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.Opacity(currentHitObject.StartTime, hidden)); + double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.OpacityAt(currentHitObject.StartTime, hidden)); result += stackNerf * opacityBonus * scalingFactor * jumpDistance / cumulativeStrainTime; } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index d602fe67ee..fc04e4d091 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -27,8 +27,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) }; - private const double fade_in_duration_multiplier = 0.4; - private const double fade_out_duration_multiplier = 0.3; + public const double FADE_IN_DURATION_MULTIPLIER = 0.4; + public const double FADE_OUT_DURATION_MULTIPLIER = 0.3; protected override bool IsFirstAdjustableObject(HitObject hitObject) => !(hitObject is Spinner || hitObject is SpinnerTick); @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods static void applyFadeInAdjustment(OsuHitObject osuObject) { - osuObject.TimeFadeIn = osuObject.TimePreempt * fade_in_duration_multiplier; + osuObject.TimeFadeIn = osuObject.TimePreempt * FADE_IN_DURATION_MULTIPLIER; foreach (var nested in osuObject.NestedHitObjects.OfType()) applyFadeInAdjustment(nested); } @@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Mods static (double fadeStartTime, double fadeDuration) getParameters(OsuHitObject hitObject) { double fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn; - double fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier; + double fadeOutDuration = hitObject.TimePreempt * FADE_OUT_DURATION_MULTIPLIER; // new duration from completed fade in to end (before fading out) double longFadeDuration = hitObject.GetEndTime() - fadeOutStartTime; From 1b2c07e1423ef313b6e5228f347641176255f74e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 19:25:51 +0900 Subject: [PATCH 1065/1276] Extend lifetime of `OsuSelectionBlueprint` to allow for lingering display --- .../Edit/Blueprints/OsuSelectionBlueprint.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs index 994c5cebeb..dbf53ac9a6 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs @@ -1,19 +1,27 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Screens.Edit; namespace osu.Game.Rulesets.Osu.Edit.Blueprints { public abstract class OsuSelectionBlueprint : HitObjectSelectionBlueprint where T : OsuHitObject { + [Resolved] + private EditorClock editorClock { get; set; } + protected new DrawableOsuHitObject DrawableObject => (DrawableOsuHitObject)base.DrawableObject; protected override bool AlwaysShowWhenSelected => true; + protected override bool ShouldBeAlive => base.ShouldBeAlive || editorClock.CurrentTime - Item.GetEndTime() < DrawableOsuEditorRuleset.EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION; + protected OsuSelectionBlueprint(T hitObject) : base(hitObject) { From c63e65c677b5103e4fc18e11cef67bb32ad497a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 19:26:13 +0900 Subject: [PATCH 1066/1276] Don't hide `SelectionBlueprint`s themselves, only their children --- osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index 12ab89f79e..5e5bc9036d 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -89,16 +89,12 @@ namespace osu.Game.Rulesets.Edit // set the body piece's alpha directly to avoid arbitrarily rendering frame buffers etc. of children. foreach (var d in InternalChildren) d.Hide(); - - Hide(); } protected virtual void OnSelected() { foreach (var d in InternalChildren) d.Show(); - - Show(); } // When not selected, input is only required for the blueprint itself to receive IsHovering From be3d4c9bf5a52e5dfcdb78d834f068389db47035 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 19:26:34 +0900 Subject: [PATCH 1067/1276] Add circle overlap marker, bringing back stable-like visibility of circles in the past --- .../Components/HitCircleOverlapMarker.cs | 87 +++++++++++++++++++ .../HitCircles/HitCircleSelectionBlueprint.cs | 9 +- .../Blueprints/Sliders/SliderCircleOverlay.cs | 23 ++++- 3 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs new file mode 100644 index 0000000000..34e5e120f1 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs @@ -0,0 +1,87 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Skinning.Default; +using osu.Game.Screens.Edit; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components +{ + public class HitCircleOverlapMarker : BlueprintPiece + { + private readonly Circle circle; + private readonly RingPiece ring; + + [Resolved] + private EditorClock editorClock { get; set; } + + public HitCircleOverlapMarker() + { + Origin = Anchor.Centre; + + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + + InternalChildren = new Drawable[] + { + circle = new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + ring = new RingPiece + { + BorderThickness = 4, + } + }; + } + + [Resolved] + private ISkinSource skin { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + circle.BorderColour = colours.Yellow; + } + + public override void UpdateFrom(HitCircle hitObject) + { + base.UpdateFrom(hitObject); + + Scale = new Vector2(hitObject.Scale); + + if ((hitObject is IHasComboInformation combo)) + ring.BorderColour = combo.GetComboColour(skin); + + bool hasReachedObject = editorClock.CurrentTime >= hitObject.StartTime; + float interpolation = Interpolation.ValueAt(editorClock.CurrentTime, 0, 1f, hitObject.StartTime, hitObject.StartTime + DrawableOsuEditorRuleset.EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION, Easing.In); + float interpolation2 = MathHelper.Clamp(Interpolation.ValueAt(editorClock.CurrentTime, 0, 1f, hitObject.StartTime, hitObject.StartTime + DrawableOsuEditorRuleset.EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION / 2, Easing.OutQuint), 0, 1); + float interpolation3 = Interpolation.ValueAt(editorClock.CurrentTime, 0, 1f, hitObject.StartTime, hitObject.StartTime + DrawableOsuEditorRuleset.EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION); + + if (hasReachedObject) + { + circle.Scale = new Vector2(1 - 0.05f * interpolation3); + ring.Scale = new Vector2(1 + 0.1f * interpolation2); + Alpha = 0.9f * (1 - (interpolation)); + } + else + Alpha = 0; + } + + public override void Hide() + { + // intentional no op so we are not hidden when not selected. + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs index b21a3e038e..3f9cfe21d4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Objects; @@ -14,11 +15,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles protected new DrawableHitCircle DrawableObject => (DrawableHitCircle)base.DrawableObject; protected readonly HitCirclePiece CirclePiece; + private readonly HitCircleOverlapMarker marker; public HitCircleSelectionBlueprint(HitCircle circle) : base(circle) { - InternalChild = CirclePiece = new HitCirclePiece(); + InternalChildren = new Drawable[] + { + marker = new HitCircleOverlapMarker(), + CirclePiece = new HitCirclePiece(), + }; } protected override void Update() @@ -26,6 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles base.Update(); CirclePiece.UpdateFrom(HitObject); + marker.UpdateFrom(HitObject); } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.HitArea.ReceivePositionalInputAt(screenSpacePos); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleOverlay.cs index 241ff70a18..d31d2aed97 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleOverlay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Objects; @@ -13,20 +14,38 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private readonly Slider slider; private readonly SliderPosition position; + private readonly HitCircleOverlapMarker marker; public SliderCircleOverlay(Slider slider, SliderPosition position) { this.slider = slider; this.position = position; - InternalChild = CirclePiece = new HitCirclePiece(); + InternalChildren = new Drawable[] + { + marker = new HitCircleOverlapMarker(), + CirclePiece = new HitCirclePiece(), + }; } protected override void Update() { base.Update(); - CirclePiece.UpdateFrom(position == SliderPosition.Start ? (HitCircle)slider.HeadCircle : slider.TailCircle); + var circle = position == SliderPosition.Start ? (HitCircle)slider.HeadCircle : slider.TailCircle; + + CirclePiece.UpdateFrom(circle); + marker.UpdateFrom(circle); + } + + public override void Hide() + { + CirclePiece.Hide(); + } + + public override void Show() + { + CirclePiece.Show(); } } } From f22ff4e087d5567831e6c2388bb0b031080728d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 19:28:56 +0900 Subject: [PATCH 1068/1276] Remove editor animation toggling (replaced by overlap markers) --- .../Editor/TestSceneOsuEditorHitAnimations.cs | 114 ------------------ .../Edit/DrawableOsuEditorRuleset.cs | 76 ------------ osu.Game/Configuration/OsuConfigManager.cs | 2 - osu.Game/Screens/Edit/Editor.cs | 1 - 4 files changed, 193 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorHitAnimations.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorHitAnimations.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorHitAnimations.cs deleted file mode 100644 index 7ffa2c1f94..0000000000 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorHitAnimations.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Transforms; -using osu.Framework.Testing; -using osu.Framework.Utils; -using osu.Game.Configuration; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; - -namespace osu.Game.Rulesets.Osu.Tests.Editor -{ - [TestFixture] - public class TestSceneOsuEditorHitAnimations : TestSceneOsuEditor - { - [Resolved] - private OsuConfigManager config { get; set; } - - [Test] - public void TestHitCircleAnimationDisable() - { - HitCircle hitCircle = null; - DrawableHitCircle drawableHitCircle = null; - - AddStep("retrieve first hit circle", () => hitCircle = getHitCircle(0)); - toggleAnimations(true); - seekSmoothlyTo(() => hitCircle.StartTime + 10); - - AddStep("retrieve drawable", () => drawableHitCircle = (DrawableHitCircle)getDrawableObjectFor(hitCircle)); - assertFutureTransforms(() => drawableHitCircle.CirclePiece, true); - - AddStep("retrieve second hit circle", () => hitCircle = getHitCircle(1)); - toggleAnimations(false); - seekSmoothlyTo(() => hitCircle.StartTime + 10); - - AddStep("retrieve drawable", () => drawableHitCircle = (DrawableHitCircle)getDrawableObjectFor(hitCircle)); - assertFutureTransforms(() => drawableHitCircle.CirclePiece, false); - AddAssert("hit circle has longer fade-out applied", () => - { - var alphaTransform = drawableHitCircle.Transforms.Last(t => t.TargetMember == nameof(Alpha)); - return alphaTransform.EndTime - alphaTransform.StartTime == DrawableOsuEditorRuleset.EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION; - }); - } - - [Test] - public void TestSliderAnimationDisable() - { - Slider slider = null; - DrawableSlider drawableSlider = null; - DrawableSliderRepeat sliderRepeat = null; - - AddStep("retrieve first slider with repeats", () => slider = getSliderWithRepeats(0)); - toggleAnimations(true); - seekSmoothlyTo(() => slider.StartTime + slider.SpanDuration + 10); - - retrieveDrawables(); - assertFutureTransforms(() => sliderRepeat, true); - - AddStep("retrieve second slider with repeats", () => slider = getSliderWithRepeats(1)); - toggleAnimations(false); - seekSmoothlyTo(() => slider.StartTime + slider.SpanDuration + 10); - - retrieveDrawables(); - assertFutureTransforms(() => sliderRepeat.Arrow, false); - seekSmoothlyTo(() => slider.GetEndTime()); - AddAssert("slider has longer fade-out applied", () => - { - var alphaTransform = drawableSlider.Transforms.Last(t => t.TargetMember == nameof(Alpha)); - return alphaTransform.EndTime - alphaTransform.StartTime == DrawableOsuEditorRuleset.EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION; - }); - - void retrieveDrawables() => - AddStep("retrieve drawables", () => - { - drawableSlider = (DrawableSlider)getDrawableObjectFor(slider); - sliderRepeat = (DrawableSliderRepeat)getDrawableObjectFor(slider.NestedHitObjects.OfType().First()); - }); - } - - private HitCircle getHitCircle(int index) - => EditorBeatmap.HitObjects.OfType().ElementAt(index); - - private Slider getSliderWithRepeats(int index) - => EditorBeatmap.HitObjects.OfType().Where(s => s.RepeatCount >= 1).ElementAt(index); - - private DrawableHitObject getDrawableObjectFor(HitObject hitObject) - => this.ChildrenOfType().Single(ho => ho.HitObject == hitObject); - - private IEnumerable getTransformsRecursively(Drawable drawable) - => drawable.ChildrenOfType().SelectMany(d => d.Transforms); - - private void toggleAnimations(bool enabled) - => AddStep($"toggle animations {(enabled ? "on" : "off")}", () => config.SetValue(OsuSetting.EditorHitAnimations, enabled)); - - private void seekSmoothlyTo(Func targetTime) - { - AddStep("seek smoothly", () => EditorClock.SeekSmoothlyTo(targetTime.Invoke())); - AddUntilStep("wait for seek", () => Precision.AlmostEquals(targetTime.Invoke(), EditorClock.CurrentTime)); - } - - private void assertFutureTransforms(Func getDrawable, bool hasFutureTransforms) - => AddAssert($"object {(hasFutureTransforms ? "has" : "has no")} future transforms", - () => getTransformsRecursively(getDrawable()).Any(t => t.EndTime >= EditorClock.CurrentTime) == hasFutureTransforms); - } -} diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs index c89527d8bd..f542c88d89 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs @@ -2,16 +2,8 @@ // 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.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using osuTK; @@ -37,80 +29,12 @@ namespace osu.Game.Rulesets.Osu.Edit private class OsuEditorPlayfield : OsuPlayfield { - private Bindable hitAnimations; - protected override GameplayCursorContainer CreateCursor() => null; public OsuEditorPlayfield() { HitPolicy = new AnyOrderHitPolicy(); } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - hitAnimations = config.GetBindable(OsuSetting.EditorHitAnimations); - } - - protected override void OnNewDrawableHitObject(DrawableHitObject d) - { - d.ApplyCustomUpdateState += updateState; - } - - private void updateState(DrawableHitObject hitObject, ArmedState state) - { - if (state == ArmedState.Idle || hitAnimations.Value) - return; - - if (hitObject is DrawableHitCircle circle) - { - using (circle.BeginAbsoluteSequence(circle.HitStateUpdateTime)) - { - circle.ApproachCircle - .FadeOutFromOne(EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION * 4) - .Expire(); - - circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint); - } - } - - if (hitObject is IHasMainCirclePiece mainPieceContainer) - { - // clear any explode animation logic. - // this is scheduled after children to ensure that the clear happens after invocations of ApplyCustomUpdateState on the circle piece's nested skinnables. - ScheduleAfterChildren(() => - { - if (hitObject.HitObject == null) return; - - mainPieceContainer.CirclePiece.ApplyTransformsAt(hitObject.StateUpdateTime, true); - mainPieceContainer.CirclePiece.ClearTransformsAfter(hitObject.StateUpdateTime, true); - }); - } - - if (hitObject is DrawableSliderRepeat repeat) - { - repeat.Arrow.ApplyTransformsAt(hitObject.StateUpdateTime, true); - repeat.Arrow.ClearTransformsAfter(hitObject.StateUpdateTime, true); - } - - // adjust the visuals of top-level object types to make them stay on screen for longer than usual. - switch (hitObject) - { - case DrawableSlider _: - case DrawableHitCircle _: - // Get the existing fade out transform - var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha)); - - if (existing == null) - return; - - hitObject.RemoveTransform(existing); - - using (hitObject.BeginAbsoluteSequence(hitObject.HitStateUpdateTime)) - hitObject.FadeOut(EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION).Expire(); - break; - } - } } } } diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 026a83cceb..20d555c16c 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -163,7 +163,6 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full); SetDefault(OsuSetting.EditorWaveformOpacity, 0.25f); - SetDefault(OsuSetting.EditorHitAnimations, false); } public IDictionary GetLoggableState() => @@ -356,7 +355,6 @@ namespace osu.Game.Configuration GameplayDisableWinKey, SeasonalBackgroundMode, EditorWaveformOpacity, - EditorHitAnimations, DiscordRichPresence, AutomaticallyDownloadWhenSpectating, ShowOnlineExplicitContent, diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 947c184009..75d2480a97 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -270,7 +270,6 @@ namespace osu.Game.Screens.Edit Items = new MenuItem[] { new WaveformOpacityMenuItem(config.GetBindable(OsuSetting.EditorWaveformOpacity)), - new HitAnimationsMenuItem(config.GetBindable(OsuSetting.EditorHitAnimations)) } } } From 0c335592b3ed4eebd8428fa91dd4c90abb98010c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 19:42:35 +0900 Subject: [PATCH 1069/1276] Tidy up interpolation and move `const` to new home --- .../Components/HitCircleOverlapMarker.cs | 23 +++++++++++++------ .../Edit/Blueprints/OsuSelectionBlueprint.cs | 3 ++- .../Edit/DrawableOsuEditorRuleset.cs | 6 ----- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs index 34e5e120f1..579c9ef356 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs @@ -18,6 +18,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components { public class HitCircleOverlapMarker : BlueprintPiece { + /// + /// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay. + /// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points. + /// + public const double FADE_OUT_EXTENSION = 700; + private readonly Circle circle; private readonly RingPiece ring; @@ -64,16 +70,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components if ((hitObject is IHasComboInformation combo)) ring.BorderColour = combo.GetComboColour(skin); - bool hasReachedObject = editorClock.CurrentTime >= hitObject.StartTime; - float interpolation = Interpolation.ValueAt(editorClock.CurrentTime, 0, 1f, hitObject.StartTime, hitObject.StartTime + DrawableOsuEditorRuleset.EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION, Easing.In); - float interpolation2 = MathHelper.Clamp(Interpolation.ValueAt(editorClock.CurrentTime, 0, 1f, hitObject.StartTime, hitObject.StartTime + DrawableOsuEditorRuleset.EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION / 2, Easing.OutQuint), 0, 1); - float interpolation3 = Interpolation.ValueAt(editorClock.CurrentTime, 0, 1f, hitObject.StartTime, hitObject.StartTime + DrawableOsuEditorRuleset.EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION); + double editorTime = editorClock.CurrentTime; + double hitObjectTime = hitObject.StartTime; + bool hasReachedObject = editorTime >= hitObjectTime; if (hasReachedObject) { - circle.Scale = new Vector2(1 - 0.05f * interpolation3); - ring.Scale = new Vector2(1 + 0.1f * interpolation2); - Alpha = 0.9f * (1 - (interpolation)); + float alpha = Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION, Easing.In); + float circleScale = Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION); + float ringScale = MathHelper.Clamp(Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION / 2, Easing.OutQuint), 0, 1); + + circle.Scale = new Vector2(1 - 0.05f * circleScale); + ring.Scale = new Vector2(1 + 0.1f * ringScale); + Alpha = 0.9f * (1 - alpha); } else Alpha = 0; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs index dbf53ac9a6..1d32e922ac 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Screens.Edit; @@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints protected override bool AlwaysShowWhenSelected => true; - protected override bool ShouldBeAlive => base.ShouldBeAlive || editorClock.CurrentTime - Item.GetEndTime() < DrawableOsuEditorRuleset.EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION; + protected override bool ShouldBeAlive => base.ShouldBeAlive || editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION; protected OsuSelectionBlueprint(T hitObject) : base(hitObject) diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs index f542c88d89..516b34d807 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs @@ -12,12 +12,6 @@ namespace osu.Game.Rulesets.Osu.Edit { public class DrawableOsuEditorRuleset : DrawableOsuRuleset { - /// - /// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay. - /// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points. - /// - public const double EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION = 700; - public DrawableOsuEditorRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { From 0318944b807d8c3a2aa75735f78f955ec3dab837 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 23:37:29 +0900 Subject: [PATCH 1070/1276] Fix incorrect alive criteria causing clicking future objects to be too greedy --- osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs index 1d32e922ac..2d0c6fe81d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs @@ -21,7 +21,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints protected override bool AlwaysShowWhenSelected => true; - protected override bool ShouldBeAlive => base.ShouldBeAlive || editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION; + protected override bool ShouldBeAlive => base.ShouldBeAlive + || (editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION); protected OsuSelectionBlueprint(T hitObject) : base(hitObject) From a35c57860cb309ff73ac62d41be0b39b5257c3b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 23:54:43 +0900 Subject: [PATCH 1071/1276] Resolve colour inline --- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 79c4ed6068..b7e1bc999b 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -155,7 +155,8 @@ namespace osu.Game.Online.Chat { public Func CreateChatLineAction; - private Colour4 daySepColour; + [Resolved] + private OsuColour colours { get; set; } public StandAloneDrawableChannel(Channel channel) : base(channel) @@ -163,10 +164,9 @@ namespace osu.Game.Online.Chat } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { ChatLineFlow.Padding = new MarginPadding { Horizontal = 0 }; - daySepColour = colours.Yellow; } protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m); @@ -174,7 +174,7 @@ namespace osu.Game.Online.Chat protected override Drawable CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time) { TextSize = 14, - Colour = daySepColour, + Colour = colours.Yellow, LineHeight = 1, Padding = new MarginPadding { Horizontal = 10 }, Margin = new MarginPadding { Vertical = 5 }, From 9f5351e5a195d490db4b19b808263aeeac725e85 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Thu, 12 May 2022 23:48:58 +0100 Subject: [PATCH 1072/1276] Add drawable channel caching to new chat overlay --- osu.Game/Overlays/ChatOverlayV2.cs | 37 +++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 6eec0bbbf4..bd3823f090 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -39,8 +39,9 @@ namespace osu.Game.Overlays private ChatTextBar textBar = null!; private Container currentChannelContainer = null!; - private readonly BindableFloat chatHeight = new BindableFloat(); + private readonly Dictionary loadedChannels = new Dictionary(); + private readonly BindableFloat chatHeight = new BindableFloat(); private bool isDraggingTopBar; private float dragStartChatHeight; @@ -173,7 +174,7 @@ namespace osu.Game.Overlays if (currentChannel.Value?.Id != channel.Id) { if (!channel.Joined.Value) - channel = channelManager.JoinChannel(channel); + channel = channelManager.JoinChannel(channel, false); channelManager.CurrentChannel.Value = channel; } @@ -248,13 +249,26 @@ namespace osu.Game.Overlays channelListing.State.Value = Visibility.Hidden; textBar.ShowSearch.Value = false; - loading.Show(); - LoadComponentAsync(new ChatOverlayDrawableChannel(newChannel), loaded => + if (loadedChannels.ContainsKey(newChannel)) { - currentChannelContainer.Clear(); - currentChannelContainer.Add(loaded); - loading.Hide(); - }); + currentChannelContainer.Clear(false); + currentChannelContainer.Add(loadedChannels[newChannel]); + } + else + { + loading.Show(); + + // Ensure the drawable channel is stored before async load to prevent double loading + ChatOverlayDrawableChannel drawableChannel = new ChatOverlayDrawableChannel(newChannel); + loadedChannels.Add(newChannel, drawableChannel); + + LoadComponentAsync(drawableChannel, loaded => + { + currentChannelContainer.Clear(false); + currentChannelContainer.Add(loaded); + loading.Hide(); + }); + } } } @@ -264,14 +278,21 @@ namespace osu.Game.Overlays { case NotifyCollectionChangedAction.Add: IEnumerable joinedChannels = filterChannels(args.NewItems); + foreach (var channel in joinedChannels) channelList.AddChannel(channel); + break; case NotifyCollectionChangedAction.Remove: IEnumerable leftChannels = filterChannels(args.OldItems); + foreach (var channel in leftChannels) + { channelList.RemoveChannel(channel); + loadedChannels.Remove(channel); + } + break; } } From ff13ef6bb05e0633d8c59e4a40273e084645336b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 May 2022 19:46:21 +0900 Subject: [PATCH 1073/1276] Fix a couple of markdown inspections --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dba0b2670d..9daa9635a9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- + osu! logo

# osu! @@ -31,7 +31,7 @@ If you are looking to install or test osu! without setting up a development envi **Latest build:** -| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) +| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) | | ------------- | ------------- | ------------- | ------------- | ------------- | - The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets. From 4b4f3ec06432bebadf6f020653c938c8c5bb8d27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 May 2022 19:46:28 +0900 Subject: [PATCH 1074/1276] Add crowdin information to README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 9daa9635a9..75d61dad4d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![GitHub release](https://img.shields.io/github/release/ppy/osu.svg)](https://github.com/ppy/osu/releases/latest) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy) +[![Crowdin](https://d322cqt584bo4o.cloudfront.net/osu-web/localized.svg)](https://crowdin.com/project/osu-web) A free-to-win rhythm game. Rhythm is just a *click* away! @@ -104,6 +105,8 @@ When it comes to contributing to the project, the two main things you can do to Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured, with any libraries we are using, or with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as painless as possible. +If you wish to help with localisation efforts, head over to [crowdin](https://crowdin.com/project/osu-web). + For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via PayPal or osu!supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. ## Licence From 15291623180b570b3687f1e4ae3b2eda1875ad0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 May 2022 21:28:45 +0900 Subject: [PATCH 1075/1276] Remove circle scaling (apparently the fixed scale is required for spacing) --- .../Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs index 579c9ef356..e00abadb7e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs @@ -77,10 +77,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components if (hasReachedObject) { float alpha = Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION, Easing.In); - float circleScale = Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION); float ringScale = MathHelper.Clamp(Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION / 2, Easing.OutQuint), 0, 1); - circle.Scale = new Vector2(1 - 0.05f * circleScale); ring.Scale = new Vector2(1 + 0.1f * ringScale); Alpha = 0.9f * (1 - alpha); } From 38b8baf0954a95cbf993144a5533b1d582abda44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 May 2022 21:28:50 +0900 Subject: [PATCH 1076/1276] Remove unused border colour --- .../HitCircles/Components/HitCircleOverlapMarker.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs index e00abadb7e..b2fc310dcb 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Utils; -using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; @@ -55,12 +54,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components [Resolved] private ISkinSource skin { get; set; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - circle.BorderColour = colours.Yellow; - } - public override void UpdateFrom(HitCircle hitObject) { base.UpdateFrom(hitObject); From d9782b5ef67627ab3597176f2b38a6d0efd0a37e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 May 2022 21:31:49 +0900 Subject: [PATCH 1077/1276] Remove redundant parenthesis --- .../Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs index b2fc310dcb..b9d0d37d02 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components Scale = new Vector2(hitObject.Scale); - if ((hitObject is IHasComboInformation combo)) + if (hitObject is IHasComboInformation combo) ring.BorderColour = combo.GetComboColour(skin); double editorTime = editorClock.CurrentTime; From 35026ad6428f35b100575cdd0653d4694bdca3e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 May 2022 21:39:54 +0900 Subject: [PATCH 1078/1276] Add missing `EditorClock` caching --- osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs b/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs index c3fb3bfc17..5448783f6d 100644 --- a/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs @@ -2,16 +2,21 @@ // See the LICENCE file in the repository root for full licence text. using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Screens.Edit; namespace osu.Game.Tests.Visual { public abstract class SelectionBlueprintTestScene : OsuManualInputManagerTestScene { + [Cached] + private readonly EditorClock editorClock = new EditorClock(); + protected override Container Content => content ?? base.Content; private readonly Container content; From dec298cccbdfd34108c2c34f771ed82d81ce643c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 May 2022 21:47:22 +0900 Subject: [PATCH 1079/1276] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 98dc28d915..b984f528fe 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 772a78c8fe..7f149b4e35 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index af8f9b617c..932421a705 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From cf3ead80477621b7b911010f5435269964127d38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 May 2022 22:06:30 +0900 Subject: [PATCH 1080/1276] Remove unused local --- .../Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs index b9d0d37d02..ad4be2017e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCircleOverlapMarker.cs @@ -23,7 +23,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components ///
public const double FADE_OUT_EXTENSION = 700; - private readonly Circle circle; private readonly RingPiece ring; [Resolved] @@ -37,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components InternalChildren = new Drawable[] { - circle = new Circle + new Circle { Anchor = Anchor.Centre, Origin = Anchor.Centre, From c4871ebcf4eaf35b3fe804d74a86b95474410e3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 14 May 2022 02:50:51 +0900 Subject: [PATCH 1081/1276] Add editor track traversal keys (`Z`-`V`) --- osu.Game/Screens/Edit/Editor.cs | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 947c184009..0fa9d4ef01 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -33,6 +33,7 @@ using osu.Game.Overlays.Notifications; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Edit.Components.Menus; using osu.Game.Screens.Edit.Components.Timelines.Summary; @@ -475,6 +476,44 @@ namespace osu.Game.Screens.Edit case Key.Right: seek(e, 1); return true; + + // Track traversal keys. + // Matching osu-stable implementations. + case Key.Z: + // Seek to first object time, or track start if already there. + double? firstObjectTime = editorBeatmap.HitObjects.FirstOrDefault()?.StartTime; + + if (firstObjectTime == null || clock.CurrentTime == firstObjectTime) + clock.Seek(0); + else + clock.Seek(firstObjectTime.Value); + return true; + + case Key.X: + // Restart playback from beginning of track. + clock.Seek(0); + clock.Start(); + return true; + + case Key.C: + // Pause or resume. + if (clock.IsRunning) + clock.Stop(); + else + clock.Start(); + return true; + + case Key.V: + // Seek to last object time, or track end if already there. + // Note that in osu-stable subsequent presses when at track end won't return to last object. + // This has intentionally been changed to make it more useful. + double? lastObjectTime = editorBeatmap.HitObjects.LastOrDefault()?.GetEndTime(); + + if (lastObjectTime == null || clock.CurrentTime == lastObjectTime) + clock.Seek(clock.TrackLength); + else + clock.Seek(lastObjectTime.Value); + return true; } return base.OnKeyDown(e); From a346456daf26a32b528e7024cc302370c6944206 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 14 May 2022 03:01:42 +0900 Subject: [PATCH 1082/1276] Add test coverage of new traversal keys --- .../Visual/Editing/TestSceneEditorSeeking.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs index ff741a8ed5..da28387c4d 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs @@ -28,6 +28,51 @@ namespace osu.Game.Tests.Visual.Editing return beatmap; } + [Test] + public void TestSeekToFirst() + { + pressAndCheckTime(Key.Z, 2170); + pressAndCheckTime(Key.Z, 0); + pressAndCheckTime(Key.Z, 2170); + + AddAssert("track not running", () => !EditorClock.IsRunning); + } + + [Test] + public void TestRestart() + { + pressAndCheckTime(Key.V, 227170); + + AddAssert("track not running", () => !EditorClock.IsRunning); + + AddStep("press X", () => InputManager.Key(Key.X)); + + AddAssert("track running", () => EditorClock.IsRunning); + AddAssert("time restarted", () => EditorClock.CurrentTime < 100000); + } + + [Test] + public void TestPauseResume() + { + AddAssert("track not running", () => !EditorClock.IsRunning); + + AddStep("press C", () => InputManager.Key(Key.C)); + AddAssert("track running", () => EditorClock.IsRunning); + + AddStep("press C", () => InputManager.Key(Key.C)); + AddAssert("track not running", () => !EditorClock.IsRunning); + } + + [Test] + public void TestSeekToLast() + { + pressAndCheckTime(Key.V, 227170); + pressAndCheckTime(Key.V, 229170); + pressAndCheckTime(Key.V, 227170); + + AddAssert("track not running", () => !EditorClock.IsRunning); + } + [Test] public void TestSnappedSeeking() { From f88e416d1a7b6f63b0177f8bc042ec7470912d5b Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Fri, 13 May 2022 23:39:09 +0100 Subject: [PATCH 1083/1276] Ensure the wrong drawable channel isn't shown after load --- osu.Game/Overlays/ChatOverlayV2.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index bd3823f090..361c06b04d 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -264,6 +264,14 @@ namespace osu.Game.Overlays LoadComponentAsync(drawableChannel, loaded => { + // Ensure the current channel hasn't changed by the time the load completes + if (currentChannel.Value != newChannel) + return; + + // Ensure the cached reference hasn't been removed from leaving the channel + if (!loadedChannels.ContainsKey(newChannel)) + return; + currentChannelContainer.Clear(false); currentChannelContainer.Add(loaded); loading.Hide(); From 2163a78b7f591518a3e532ed2a4be7835a2953ea Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 14 May 2022 00:15:02 +0100 Subject: [PATCH 1084/1276] Ensure drawable channels removed from the cache are disposed --- osu.Game/Overlays/ChatOverlayV2.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 361c06b04d..e76d395204 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -241,6 +241,7 @@ namespace osu.Game.Overlays if (newChannel == null) { // null channel denotes that we should be showing the listing. + currentChannelContainer.Clear(false); channelListing.State.Value = Visibility.Visible; textBar.ShowSearch.Value = true; } @@ -298,7 +299,13 @@ namespace osu.Game.Overlays foreach (var channel in leftChannels) { channelList.RemoveChannel(channel); - loadedChannels.Remove(channel); + if (loadedChannels.ContainsKey(channel)) + { + ChatOverlayDrawableChannel loaded = loadedChannels[channel]; + loadedChannels.Remove(channel); + // DrawableChannel removed from cache must be manually disposed + loaded.Dispose(); + } } break; From bd68ffa805a6f3ae1d1a4a9853ebfa4f9aa1303d Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 14 May 2022 12:16:00 +0100 Subject: [PATCH 1085/1276] Fix textbox focus test in `ChatOverlayV2` --- osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs | 6 +++--- osu.Game/Overlays/ChatOverlayV2.cs | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index bf1767cc96..82089ff3d1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -365,19 +365,19 @@ namespace osu.Game.Tests.Visual.Online } [Test] - public void TextBoxRetainsFocus() + public void TestTextBoxRetainsFocus() { AddStep("Show overlay", () => chatOverlay.Show()); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); + AddStep("Click drawable channel", () => clickDrawable(currentDrawableChannel)); + AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click selector", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click listing", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); - AddStep("Click drawable channel", () => clickDrawable(currentDrawableChannel)); - AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click channel list", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click top bar", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index e76d395204..e91d57f18b 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -242,12 +242,12 @@ namespace osu.Game.Overlays { // null channel denotes that we should be showing the listing. currentChannelContainer.Clear(false); - channelListing.State.Value = Visibility.Visible; + channelListing.Show(); textBar.ShowSearch.Value = true; } else { - channelListing.State.Value = Visibility.Hidden; + channelListing.Hide(); textBar.ShowSearch.Value = false; if (loadedChannels.ContainsKey(newChannel)) @@ -299,6 +299,7 @@ namespace osu.Game.Overlays foreach (var channel in leftChannels) { channelList.RemoveChannel(channel); + if (loadedChannels.ContainsKey(channel)) { ChatOverlayDrawableChannel loaded = loadedChannels[channel]; From d5027cdfbd4418a129687aed5b881c41cc34a823 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 May 2022 19:46:13 +0300 Subject: [PATCH 1086/1276] Add `FeaturedInSpotlight` property to API beatmapsets --- .../Visual/Beatmaps/TestSceneBeatmapCard.cs | 17 +++++++++---- .../Online/TestSceneBeatmapSetOverlay.cs | 24 +++++++++++++++++++ .../API/Requests/Responses/APIBeatmapSet.cs | 3 +++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs index 94b693363a..6cb171974a 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs @@ -84,20 +84,26 @@ namespace osu.Game.Tests.Visual.Beatmaps explicitMap.Title = someDifficulties.TitleUnicode = "explicit beatmap"; explicitMap.HasExplicitContent = true; + var spotlightMap = CreateAPIBeatmapSet(Ruleset.Value); + spotlightMap.Title = someDifficulties.TitleUnicode = "spotlight beatmap"; + spotlightMap.FeaturedInSpotlight = true; + var featuredMap = CreateAPIBeatmapSet(Ruleset.Value); featuredMap.Title = someDifficulties.TitleUnicode = "featured artist beatmap"; featuredMap.TrackId = 1; - var explicitFeaturedMap = CreateAPIBeatmapSet(Ruleset.Value); - explicitFeaturedMap.Title = someDifficulties.TitleUnicode = "explicit featured artist"; - explicitFeaturedMap.HasExplicitContent = true; - explicitFeaturedMap.TrackId = 2; + var allBadgesMap = CreateAPIBeatmapSet(Ruleset.Value); + allBadgesMap.Title = someDifficulties.TitleUnicode = "all-badges beatmap"; + allBadgesMap.HasExplicitContent = true; + allBadgesMap.FeaturedInSpotlight = true; + allBadgesMap.TrackId = 2; var longName = CreateAPIBeatmapSet(Ruleset.Value); longName.Title = longName.TitleUnicode = "this track has an incredibly and implausibly long title"; longName.Artist = longName.ArtistUnicode = "and this artist! who would have thunk it. it's really such a long name."; longName.Source = "wow. even the source field has an impossibly long string in it. this really takes the cake, doesn't it?"; longName.HasExplicitContent = true; + longName.FeaturedInSpotlight = true; longName.TrackId = 444; testCases = new[] @@ -108,8 +114,9 @@ namespace osu.Game.Tests.Visual.Beatmaps someDifficulties, manyDifficulties, explicitMap, + spotlightMap, featuredMap, - explicitFeaturedMap, + allBadgesMap, longName }; diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index f87cca80b0..859727e632 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -165,6 +165,17 @@ namespace osu.Game.Tests.Visual.Online }); } + [Test] + public void TestSpotlightBeatmap() + { + AddStep("show spotlight map", () => + { + var beatmapSet = getBeatmapSet(); + beatmapSet.FeaturedInSpotlight = true; + overlay.ShowBeatmapSet(beatmapSet); + }); + } + [Test] public void TestFeaturedBeatmap() { @@ -176,6 +187,19 @@ namespace osu.Game.Tests.Visual.Online }); } + [Test] + public void TestAllBadgesBeatmap() + { + AddStep("show map with all badges", () => + { + var beatmapSet = getBeatmapSet(); + beatmapSet.HasExplicitContent = true; + beatmapSet.FeaturedInSpotlight = true; + beatmapSet.TrackId = 1; + overlay.ShowBeatmapSet(beatmapSet); + }); + } + [Test] public void TestHide() { diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index d99c13b977..79c65fa79e 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -42,6 +42,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"nsfw")] public bool HasExplicitContent { get; set; } + [JsonProperty(@"spotlight")] + public bool FeaturedInSpotlight { get; set; } + [JsonProperty(@"video")] public bool HasVideo { get; set; } From c8bea6273b484018310d654fd80f3499ea531e91 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 May 2022 19:46:34 +0300 Subject: [PATCH 1087/1276] Abstractify beatmap badge logic to own class --- .../Overlays/BeatmapSet/BeatmapBadgePill.cs | 67 +++++++++++++++++++ .../BeatmapSet/ExplicitContentBeatmapPill.cs | 42 ++---------- .../BeatmapSet/FeaturedArtistBeatmapPill.cs | 42 ++---------- 3 files changed, 79 insertions(+), 72 deletions(-) create mode 100644 osu.Game/Overlays/BeatmapSet/BeatmapBadgePill.cs diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapBadgePill.cs b/osu.Game/Overlays/BeatmapSet/BeatmapBadgePill.cs new file mode 100644 index 0000000000..2c5042942c --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/BeatmapBadgePill.cs @@ -0,0 +1,67 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +#nullable enable + +namespace osu.Game.Overlays.BeatmapSet +{ + public abstract class BeatmapBadgePill : CompositeDrawable + { + [Resolved] + protected OsuColour Colours { get; private set; } = null!; + + [Resolved(canBeNull: true)] + protected OverlayColourProvider? ColourProvider { get; private set; } + + /// + /// The text displayed on the badge's label. + /// + public abstract LocalisableString BadgeText { get; } + + /// + /// The colour of the badge's label. + /// + public abstract Colour4 BadgeColour { get; } + + // todo: add linking support, to allow redirecting featured artist badge to corresponding track and spotlight badge to wiki page. + + protected BeatmapBadgePill() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader(true)] + private void load() + { + InternalChild = new CircularContainer + { + Masking = true, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourProvider?.Background5 ?? Colours.Gray2, + }, + new OsuSpriteText + { + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), + Margin = new MarginPadding { Horizontal = 10, Vertical = 2 }, + Text = BadgeText.ToUpper(), + Colour = BadgeColour, + } + } + }; + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs index 21d1d1172c..f8530f3a16 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs @@ -1,47 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; +#nullable enable + namespace osu.Game.Overlays.BeatmapSet { - public class ExplicitContentBeatmapPill : CompositeDrawable + public class ExplicitContentBeatmapPill : BeatmapBadgePill { - public ExplicitContentBeatmapPill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, OverlayColourProvider colourProvider) - { - InternalChild = new CircularContainer - { - Masking = true, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider?.Background5 ?? colours.Gray2, - }, - new OsuSpriteText - { - Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, - Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(), - Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Colour = colours.Orange2 - } - } - }; - } + public override LocalisableString BadgeText => BeatmapsetsStrings.NsfwBadgeLabel; + public override Colour4 BadgeColour => Colours.Orange2; } } diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs index 1be987cde2..4c29ede12e 100644 --- a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs @@ -1,47 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; +#nullable enable + namespace osu.Game.Overlays.BeatmapSet { - public class FeaturedArtistBeatmapPill : CompositeDrawable + public class FeaturedArtistBeatmapPill : BeatmapBadgePill { - public FeaturedArtistBeatmapPill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, OverlayColourProvider colourProvider) - { - InternalChild = new CircularContainer - { - Masking = true, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider?.Background5 ?? colours.Gray2, - }, - new OsuSpriteText - { - Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, - Text = BeatmapsetsStrings.FeaturedArtistBadgeLabel.ToUpper(), - Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Colour = colours.Blue1 - } - } - }; - } + public override LocalisableString BadgeText => BeatmapsetsStrings.FeaturedArtistBadgeLabel; + public override Colour4 BadgeColour => Colours.Blue1; } } From 56d6cb5764f49eb2aaa98aef0b2ca97b3b88cbd6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 May 2022 19:46:50 +0300 Subject: [PATCH 1088/1276] Add "spotlight" beatmap badge --- .../Overlays/BeatmapSet/SpotlightBeatmapPill.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 osu.Game/Overlays/BeatmapSet/SpotlightBeatmapPill.cs diff --git a/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapPill.cs new file mode 100644 index 0000000000..7376889d7b --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapPill.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + +#nullable enable + +namespace osu.Game.Overlays.BeatmapSet +{ + public class SpotlightBeatmapPill : BeatmapBadgePill + { + public override LocalisableString BadgeText => BeatmapsetsStrings.SpotlightBadgeLabel; + public override Colour4 BadgeColour => Colours.Pink1; + } +} From 2878a6dcbed331b7f73e5f8eb877fbc73745f7c2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 May 2022 19:47:10 +0300 Subject: [PATCH 1089/1276] Integrate spotlight beatmap badge in info overlay --- .../Overlays/BeatmapSet/BeatmapSetHeaderContent.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index 8f4089c707..4233cfcb70 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -39,8 +39,11 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Box coverGradient; private readonly OsuSpriteText title, artist; private readonly AuthorInfo author; + private readonly ExplicitContentBeatmapPill explicitContentPill; + private readonly SpotlightBeatmapPill spotlightPill; private readonly FeaturedArtistBeatmapPill featuredArtistPill; + private readonly FillFlowContainer downloadButtonsContainer; private readonly BeatmapAvailability beatmapAvailability; private readonly BeatmapSetOnlineStatusPill onlineStatusPill; @@ -127,6 +130,13 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font }, explicitContentPill = new ExplicitContentBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Left = 10, Bottom = 4 }, + }, + spotlightPill = new SpotlightBeatmapPill { Alpha = 0f, Anchor = Anchor.BottomLeft, @@ -258,6 +268,7 @@ namespace osu.Game.Overlays.BeatmapSet artist.Text = new RomanisableString(setInfo.NewValue.ArtistUnicode, setInfo.NewValue.Artist); explicitContentPill.Alpha = setInfo.NewValue.HasExplicitContent ? 1 : 0; + spotlightPill.Alpha = setInfo.NewValue.FeaturedInSpotlight ? 1 : 0; featuredArtistPill.Alpha = setInfo.NewValue.TrackId != null ? 1 : 0; onlineStatusPill.FadeIn(500, Easing.OutQuint); From 9bac33ec9f6fb5a7c91f22356a803a50b3954f1e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 May 2022 19:47:29 +0300 Subject: [PATCH 1090/1276] Integrate spotlight beatmap badge in listing cards --- .../Drawables/Cards/BeatmapCardExtra.cs | 30 +++++++++++++---- .../Drawables/Cards/BeatmapCardNormal.cs | 32 ++++++++++++++----- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 535f222228..32da23b27d 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards Height = height; FillFlowContainer leftIconArea = null!; - GridContainer titleContainer = null!; + FillFlowContainer titleBadgeArea = null!; GridContainer artistContainer = null!; Child = content.With(c => @@ -93,7 +93,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards Direction = FillDirection.Vertical, Children = new Drawable[] { - titleContainer = new GridContainer + new GridContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -108,7 +108,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards }, Content = new[] { - new[] + new Drawable[] { new OsuSpriteText { @@ -117,7 +117,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards RelativeSizeAxes = Axes.X, Truncate = true }, - Empty() + titleBadgeArea = new FillFlowContainer + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + } } } }, @@ -244,14 +250,24 @@ namespace osu.Game.Beatmaps.Drawables.Cards if (BeatmapSet.HasStoryboard) leftIconArea.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) }); - if (BeatmapSet.HasExplicitContent) + if (BeatmapSet.FeaturedInSpotlight) { - titleContainer.Content[0][1] = new ExplicitContentBeatmapPill + titleBadgeArea.Add(new SpotlightBeatmapPill { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, Margin = new MarginPadding { Left = 5 } - }; + }); + } + + if (BeatmapSet.HasExplicitContent) + { + titleBadgeArea.Add(new ExplicitContentBeatmapPill + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding { Left = 5 } + }); } if (BeatmapSet.TrackId != null) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs index 08befd5340..fc91c91e3c 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs @@ -55,7 +55,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards Height = height; FillFlowContainer leftIconArea = null!; - GridContainer titleContainer = null!; + FillFlowContainer titleBadgeArea = null!; GridContainer artistContainer = null!; Child = content.With(c => @@ -94,14 +94,14 @@ namespace osu.Game.Beatmaps.Drawables.Cards Direction = FillDirection.Vertical, Children = new Drawable[] { - titleContainer = new GridContainer + new GridContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, ColumnDimensions = new[] { new Dimension(), - new Dimension(GridSizeMode.AutoSize) + new Dimension(GridSizeMode.AutoSize), }, RowDimensions = new[] { @@ -109,7 +109,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards }, Content = new[] { - new[] + new Drawable[] { new OsuSpriteText { @@ -118,7 +118,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards RelativeSizeAxes = Axes.X, Truncate = true }, - Empty() + titleBadgeArea = new FillFlowContainer + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + } } } }, @@ -225,14 +231,24 @@ namespace osu.Game.Beatmaps.Drawables.Cards if (BeatmapSet.HasStoryboard) leftIconArea.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) }); - if (BeatmapSet.HasExplicitContent) + if (BeatmapSet.FeaturedInSpotlight) { - titleContainer.Content[0][1] = new ExplicitContentBeatmapPill + titleBadgeArea.Add(new SpotlightBeatmapPill { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, Margin = new MarginPadding { Left = 5 } - }; + }); + } + + if (BeatmapSet.HasExplicitContent) + { + titleBadgeArea.Add(new ExplicitContentBeatmapPill + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding { Left = 5 } + }); } if (BeatmapSet.TrackId != null) From a759cf2dabd9a5cc4fd4fad57789007845d3699c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 May 2022 02:39:54 +0900 Subject: [PATCH 1091/1276] Add key binding to deselect all mods Defaults to `Backspace`. --- .../UserInterface/TestSceneModSelectOverlay.cs | 18 +++++++++++++++++- .../Input/Bindings/GlobalActionContainer.cs | 4 ++++ .../GlobalActionKeyBindingStrings.cs | 5 +++++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 0b037a10cd..e946524a4b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -416,6 +416,22 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("unimplemented mod panel is filtered", () => getPanelForMod(typeof(TestUnimplementedMod)).Filtered.Value); } + [Test] + public void TestDeselectAllViaKey() + { + createScreen(); + changeRuleset(0); + + AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); + AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + + AddStep("press bakcspace", () => + { + InputManager.Key(Key.BackSpace); + }); + AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any()); + } + [Test] public void TestDeselectAllViaButton() { diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 13a3f006fb..69ea6b00ca 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -109,6 +109,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.F2, GlobalAction.SelectNextRandom), new KeyBinding(new[] { InputKey.Shift, InputKey.F2 }, GlobalAction.SelectPreviousRandom), new KeyBinding(InputKey.F3, GlobalAction.ToggleBeatmapOptions), + new KeyBinding(InputKey.BackSpace, GlobalAction.DeselectAllMods), }; public IEnumerable AudioControlKeyBindings => new[] @@ -318,5 +319,8 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectNextGroup))] SelectNextGroup, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DeselectAllMods))] + DeselectAllMods, } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index b2f25de7f2..399e286478 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -209,6 +209,11 @@ namespace osu.Game.Localisation ///
public static LocalisableString ToggleModSelection => new TranslatableString(getKey(@"toggle_mod_selection"), @"Toggle Mod Select"); + /// + /// "Deselect all mods" + /// + public static LocalisableString DeselectAllMods => new TranslatableString(getKey(@"deselect_all_mods"), @"Deselect all mods"); + /// /// "Random" /// diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d068839ab0..fb988b5bfe 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -508,6 +508,10 @@ namespace osu.Game.Overlays.Mods hideOverlay(true); return true; } + + case GlobalAction.DeselectAllMods: + DeselectAll(); + return true; } return base.OnPressed(e); From 6c3a4375c529ff4940c37fa376b17b1eff1a2a99 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 May 2022 02:40:35 +0900 Subject: [PATCH 1092/1276] Fix case of "toggle mod select" string --- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 399e286478..e392ae619f 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -207,7 +207,7 @@ namespace osu.Game.Localisation /// /// "Toggle Mod Select" /// - public static LocalisableString ToggleModSelection => new TranslatableString(getKey(@"toggle_mod_selection"), @"Toggle Mod Select"); + public static LocalisableString ToggleModSelection => new TranslatableString(getKey(@"toggle_mod_selection"), @"Toggle mod select"); /// /// "Deselect all mods" From 88ba84ac9cc99c3a60632ca31aed38083a48dc7a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 May 2022 20:52:38 +0300 Subject: [PATCH 1093/1276] Replace `Pill` with `Badge` everywhere --- .../Drawables/Cards/BeatmapCardExtra.cs | 6 +++--- .../Drawables/Cards/BeatmapCardNormal.cs | 6 +++--- .../{BeatmapBadgePill.cs => BeatmapBadge.cs} | 4 ++-- .../BeatmapSet/BeatmapSetHeaderContent.cs | 18 +++++++++--------- ...pPill.cs => ExplicitContentBeatmapBadge.cs} | 2 +- ...apPill.cs => FeaturedArtistBeatmapBadge.cs} | 2 +- ...BeatmapPill.cs => SpotlightBeatmapBadge.cs} | 2 +- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 6 +++--- 8 files changed, 23 insertions(+), 23 deletions(-) rename osu.Game/Overlays/BeatmapSet/{BeatmapBadgePill.cs => BeatmapBadge.cs} (95%) rename osu.Game/Overlays/BeatmapSet/{ExplicitContentBeatmapPill.cs => ExplicitContentBeatmapBadge.cs} (88%) rename osu.Game/Overlays/BeatmapSet/{FeaturedArtistBeatmapPill.cs => FeaturedArtistBeatmapBadge.cs} (88%) rename osu.Game/Overlays/BeatmapSet/{SpotlightBeatmapPill.cs => SpotlightBeatmapBadge.cs} (89%) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 32da23b27d..58c1ebee0f 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -252,7 +252,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards if (BeatmapSet.FeaturedInSpotlight) { - titleBadgeArea.Add(new SpotlightBeatmapPill + titleBadgeArea.Add(new SpotlightBeatmapBadge { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -262,7 +262,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards if (BeatmapSet.HasExplicitContent) { - titleBadgeArea.Add(new ExplicitContentBeatmapPill + titleBadgeArea.Add(new ExplicitContentBeatmapBadge { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -272,7 +272,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards if (BeatmapSet.TrackId != null) { - artistContainer.Content[0][1] = new FeaturedArtistBeatmapPill + artistContainer.Content[0][1] = new FeaturedArtistBeatmapBadge { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs index fc91c91e3c..3d7e81de21 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs @@ -233,7 +233,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards if (BeatmapSet.FeaturedInSpotlight) { - titleBadgeArea.Add(new SpotlightBeatmapPill + titleBadgeArea.Add(new SpotlightBeatmapBadge { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -243,7 +243,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards if (BeatmapSet.HasExplicitContent) { - titleBadgeArea.Add(new ExplicitContentBeatmapPill + titleBadgeArea.Add(new ExplicitContentBeatmapBadge { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -253,7 +253,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards if (BeatmapSet.TrackId != null) { - artistContainer.Content[0][1] = new FeaturedArtistBeatmapPill + artistContainer.Content[0][1] = new FeaturedArtistBeatmapBadge { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapBadgePill.cs b/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs similarity index 95% rename from osu.Game/Overlays/BeatmapSet/BeatmapBadgePill.cs rename to osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs index 2c5042942c..6a444cc85f 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapBadgePill.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs @@ -14,7 +14,7 @@ using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.BeatmapSet { - public abstract class BeatmapBadgePill : CompositeDrawable + public abstract class BeatmapBadge : CompositeDrawable { [Resolved] protected OsuColour Colours { get; private set; } = null!; @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.BeatmapSet // todo: add linking support, to allow redirecting featured artist badge to corresponding track and spotlight badge to wiki page. - protected BeatmapBadgePill() + protected BeatmapBadge() { AutoSizeAxes = Axes.Both; } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index 4233cfcb70..56efb725cd 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -40,9 +40,9 @@ namespace osu.Game.Overlays.BeatmapSet private readonly OsuSpriteText title, artist; private readonly AuthorInfo author; - private readonly ExplicitContentBeatmapPill explicitContentPill; - private readonly SpotlightBeatmapPill spotlightPill; - private readonly FeaturedArtistBeatmapPill featuredArtistPill; + private readonly ExplicitContentBeatmapBadge explicitContent; + private readonly SpotlightBeatmapBadge spotlight; + private readonly FeaturedArtistBeatmapBadge featuredArtist; private readonly FillFlowContainer downloadButtonsContainer; private readonly BeatmapAvailability beatmapAvailability; @@ -129,14 +129,14 @@ namespace osu.Game.Overlays.BeatmapSet Origin = Anchor.BottomLeft, Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font }, - explicitContentPill = new ExplicitContentBeatmapPill + explicitContent = new ExplicitContentBeatmapBadge { Alpha = 0f, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Margin = new MarginPadding { Left = 10, Bottom = 4 }, }, - spotlightPill = new SpotlightBeatmapPill + spotlight = new SpotlightBeatmapBadge { Alpha = 0f, Anchor = Anchor.BottomLeft, @@ -156,7 +156,7 @@ namespace osu.Game.Overlays.BeatmapSet { Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), }, - featuredArtistPill = new FeaturedArtistBeatmapPill + featuredArtist = new FeaturedArtistBeatmapBadge { Alpha = 0f, Anchor = Anchor.BottomLeft, @@ -267,9 +267,9 @@ namespace osu.Game.Overlays.BeatmapSet title.Text = new RomanisableString(setInfo.NewValue.TitleUnicode, setInfo.NewValue.Title); artist.Text = new RomanisableString(setInfo.NewValue.ArtistUnicode, setInfo.NewValue.Artist); - explicitContentPill.Alpha = setInfo.NewValue.HasExplicitContent ? 1 : 0; - spotlightPill.Alpha = setInfo.NewValue.FeaturedInSpotlight ? 1 : 0; - featuredArtistPill.Alpha = setInfo.NewValue.TrackId != null ? 1 : 0; + explicitContent.Alpha = setInfo.NewValue.HasExplicitContent ? 1 : 0; + spotlight.Alpha = setInfo.NewValue.FeaturedInSpotlight ? 1 : 0; + featuredArtist.Alpha = setInfo.NewValue.TrackId != null ? 1 : 0; onlineStatusPill.FadeIn(500, Easing.OutQuint); onlineStatusPill.Status = setInfo.NewValue.Status; diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapBadge.cs similarity index 88% rename from osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs rename to osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapBadge.cs index f8530f3a16..b78b203a21 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapBadge.cs @@ -9,7 +9,7 @@ using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet { - public class ExplicitContentBeatmapPill : BeatmapBadgePill + public class ExplicitContentBeatmapBadge : BeatmapBadge { public override LocalisableString BadgeText => BeatmapsetsStrings.NsfwBadgeLabel; public override Colour4 BadgeColour => Colours.Orange2; diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs similarity index 88% rename from osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs rename to osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs index 4c29ede12e..b471911217 100644 --- a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs @@ -9,7 +9,7 @@ using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet { - public class FeaturedArtistBeatmapPill : BeatmapBadgePill + public class FeaturedArtistBeatmapBadge : BeatmapBadge { public override LocalisableString BadgeText => BeatmapsetsStrings.FeaturedArtistBadgeLabel; public override Colour4 BadgeColour => Colours.Blue1; diff --git a/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs similarity index 89% rename from osu.Game/Overlays/BeatmapSet/SpotlightBeatmapPill.cs rename to osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs index 7376889d7b..caa2043474 100644 --- a/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs @@ -9,7 +9,7 @@ using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet { - public class SpotlightBeatmapPill : BeatmapBadgePill + public class SpotlightBeatmapBadge : BeatmapBadge { public override LocalisableString BadgeText => BeatmapsetsStrings.SpotlightBadgeLabel; public override Colour4 BadgeColour => Colours.Pink1; diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 2618e15d31..39853a5c45 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.OnlinePlay private Container difficultyIconContainer; private LinkFlowContainer beatmapText; private LinkFlowContainer authorText; - private ExplicitContentBeatmapPill explicitContentPill; + private ExplicitContentBeatmapBadge explicitContent; private ModDisplay modDisplay; private FillFlowContainer buttonsFlow; private UpdateableAvatar ownerAvatar; @@ -293,7 +293,7 @@ namespace osu.Game.Screens.OnlinePlay } bool hasExplicitContent = (beatmap?.BeatmapSet as IBeatmapSetOnlineInfo)?.HasExplicitContent == true; - explicitContentPill.Alpha = hasExplicitContent ? 1 : 0; + explicitContent.Alpha = hasExplicitContent ? 1 : 0; modDisplay.Current.Value = requiredMods.ToArray(); @@ -380,7 +380,7 @@ namespace osu.Game.Screens.OnlinePlay Children = new Drawable[] { authorText = new LinkFlowContainer(fontParameters) { AutoSizeAxes = Axes.Both }, - explicitContentPill = new ExplicitContentBeatmapPill + explicitContent = new ExplicitContentBeatmapBadge { Alpha = 0f, Anchor = Anchor.CentreLeft, From 441957e18ee58e4c256002d593b583439921b6cc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 May 2022 21:01:29 +0300 Subject: [PATCH 1094/1276] Convert get-only virtual properties to avoid DI order dependency --- osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs | 26 +++++++++---------- .../BeatmapSet/ExplicitContentBeatmapBadge.cs | 12 ++++++--- .../BeatmapSet/FeaturedArtistBeatmapBadge.cs | 12 ++++++--- .../BeatmapSet/SpotlightBeatmapBadge.cs | 12 ++++++--- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs b/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs index 6a444cc85f..2604a58a2b 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs @@ -16,21 +16,23 @@ namespace osu.Game.Overlays.BeatmapSet { public abstract class BeatmapBadge : CompositeDrawable { - [Resolved] - protected OsuColour Colours { get; private set; } = null!; - - [Resolved(canBeNull: true)] - protected OverlayColourProvider? ColourProvider { get; private set; } - /// /// The text displayed on the badge's label. /// - public abstract LocalisableString BadgeText { get; } + public LocalisableString BadgeText + { + set => badgeLabel.Text = value.ToUpper(); + } /// /// The colour of the badge's label. /// - public abstract Colour4 BadgeColour { get; } + public Colour4 BadgeColour + { + set => badgeLabel.Colour = value; + } + + private OsuSpriteText badgeLabel = null!; // todo: add linking support, to allow redirecting featured artist badge to corresponding track and spotlight badge to wiki page. @@ -40,7 +42,7 @@ namespace osu.Game.Overlays.BeatmapSet } [BackgroundDependencyLoader(true)] - private void load() + private void load(OsuColour colours, OverlayColourProvider? colourProvider) { InternalChild = new CircularContainer { @@ -51,14 +53,12 @@ namespace osu.Game.Overlays.BeatmapSet new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourProvider?.Background5 ?? Colours.Gray2, + Colour = colourProvider?.Background5 ?? colours.Gray2, }, - new OsuSpriteText + badgeLabel = new OsuSpriteText { Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), Margin = new MarginPadding { Horizontal = 10, Vertical = 2 }, - Text = BadgeText.ToUpper(), - Colour = BadgeColour, } } }; diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapBadge.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapBadge.cs index b78b203a21..2a20d22b61 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapBadge.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapBadge.cs @@ -1,8 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; -using osu.Framework.Localisation; +using osu.Framework.Allocation; +using osu.Game.Graphics; using osu.Game.Resources.Localisation.Web; #nullable enable @@ -11,7 +11,11 @@ namespace osu.Game.Overlays.BeatmapSet { public class ExplicitContentBeatmapBadge : BeatmapBadge { - public override LocalisableString BadgeText => BeatmapsetsStrings.NsfwBadgeLabel; - public override Colour4 BadgeColour => Colours.Orange2; + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BadgeText = BeatmapsetsStrings.NsfwBadgeLabel; + BadgeColour = colours.Orange2; + } } } diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs index b471911217..230b8b5243 100644 --- a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs +++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs @@ -1,8 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; -using osu.Framework.Localisation; +using osu.Framework.Allocation; +using osu.Game.Graphics; using osu.Game.Resources.Localisation.Web; #nullable enable @@ -11,7 +11,11 @@ namespace osu.Game.Overlays.BeatmapSet { public class FeaturedArtistBeatmapBadge : BeatmapBadge { - public override LocalisableString BadgeText => BeatmapsetsStrings.FeaturedArtistBadgeLabel; - public override Colour4 BadgeColour => Colours.Blue1; + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BadgeText = BeatmapsetsStrings.FeaturedArtistBadgeLabel; + BadgeColour = colours.Blue1; + } } } diff --git a/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs b/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs index caa2043474..8bfd623b2e 100644 --- a/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs +++ b/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs @@ -1,8 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; -using osu.Framework.Localisation; +using osu.Framework.Allocation; +using osu.Game.Graphics; using osu.Game.Resources.Localisation.Web; #nullable enable @@ -11,7 +11,11 @@ namespace osu.Game.Overlays.BeatmapSet { public class SpotlightBeatmapBadge : BeatmapBadge { - public override LocalisableString BadgeText => BeatmapsetsStrings.SpotlightBadgeLabel; - public override Colour4 BadgeColour => Colours.Pink1; + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BadgeText = BeatmapsetsStrings.SpotlightBadgeLabel; + BadgeColour = colours.Pink1; + } } } From 1878578196e157bfb755318d39db742f5e5b74b0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 May 2022 21:02:56 +0300 Subject: [PATCH 1095/1276] Move todo comment to subclasses --- osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs | 2 -- osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs | 1 + osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs b/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs index 2604a58a2b..d19aff3a69 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs @@ -34,8 +34,6 @@ namespace osu.Game.Overlays.BeatmapSet private OsuSpriteText badgeLabel = null!; - // todo: add linking support, to allow redirecting featured artist badge to corresponding track and spotlight badge to wiki page. - protected BeatmapBadge() { AutoSizeAxes = Axes.Both; diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs index 230b8b5243..4f336d85fc 100644 --- a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs +++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapBadge.cs @@ -16,6 +16,7 @@ namespace osu.Game.Overlays.BeatmapSet { BadgeText = BeatmapsetsStrings.FeaturedArtistBadgeLabel; BadgeColour = colours.Blue1; + // todo: add linking support to allow redirecting featured artist badge to corresponding track. } } } diff --git a/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs b/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs index 8bfd623b2e..3204f79b21 100644 --- a/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs +++ b/osu.Game/Overlays/BeatmapSet/SpotlightBeatmapBadge.cs @@ -16,6 +16,7 @@ namespace osu.Game.Overlays.BeatmapSet { BadgeText = BeatmapsetsStrings.SpotlightBadgeLabel; BadgeColour = colours.Pink1; + // todo: add linking support to allow redirecting spotlight badge to https://osu.ppy.sh/wiki/en/Beatmap_Spotlights. } } } From 282c8aec867ad5adb889bd4b7f7bb488051b8098 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 May 2022 03:09:15 +0900 Subject: [PATCH 1096/1276] Fix typo in test step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index e946524a4b..2cc9a451a8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -425,7 +425,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); - AddStep("press bakcspace", () => + AddStep("press backspace", () => { InputManager.Key(Key.BackSpace); }); From 8a01050168038c41c6f8fc33d77848b91b85ccbb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 May 2022 03:16:33 +0900 Subject: [PATCH 1097/1276] Refactor mod select button initialisation to allow shared usage of deselect button --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 43 +++++++++++-------- .../OnlinePlay/FreeModSelectOverlay.cs | 16 +++---- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index fb988b5bfe..1dad185b78 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -56,11 +56,32 @@ namespace osu.Game.Overlays.Mods /// protected virtual bool ShowTotalMultiplier => true; + /// + /// Whether per-mod customisation controls are visible. + /// + protected virtual bool AllowCustomisation => true; + protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys); protected virtual IReadOnlyList ComputeNewModsFromSelection(IReadOnlyList oldSelection, IReadOnlyList newSelection) => newSelection; - protected virtual IEnumerable CreateFooterButtons() => createDefaultFooterButtons(); + protected virtual IEnumerable CreateFooterButtons() + { + if (AllowCustomisation) + { + yield return customisationButton = new ShearedToggleButton(BUTTON_WIDTH) + { + Text = ModSelectOverlayStrings.ModCustomisation, + Active = { BindTarget = customisationVisible } + }; + } + + yield return deselectAllButton = new ShearedButton(BUTTON_WIDTH) + { + Text = CommonStrings.DeselectAll, + Action = DeselectAll + }; + } private readonly Bindable>> availableMods = new Bindable>>(); private readonly Dictionary> localAvailableMods = new Dictionary>(); @@ -77,6 +98,7 @@ namespace osu.Game.Overlays.Mods private DifficultyMultiplierDisplay? multiplierDisplay; private ShearedToggleButton? customisationButton; + private ShearedButton? deselectAllButton; protected ModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green) : base(colourScheme) @@ -201,7 +223,7 @@ namespace osu.Game.Overlays.Mods // This is an optimisation to prevent refreshing the available settings controls when it can be // reasonably assumed that the settings panel is never to be displayed (e.g. FreeModSelectOverlay). - if (customisationButton != null) + if (AllowCustomisation) ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); SelectedMods.BindValueChanged(val => @@ -256,21 +278,6 @@ namespace osu.Game.Overlays.Mods }; } - private ShearedButton[] createDefaultFooterButtons() - => new[] - { - customisationButton = new ShearedToggleButton(BUTTON_WIDTH) - { - Text = ModSelectOverlayStrings.ModCustomisation, - Active = { BindTarget = customisationVisible } - }, - new ShearedButton(BUTTON_WIDTH) - { - Text = CommonStrings.DeselectAll, - Action = DeselectAll - } - }; - private void createLocalMods() { localAvailableMods.Clear(); @@ -510,7 +517,7 @@ namespace osu.Game.Overlays.Mods } case GlobalAction.DeselectAllMods: - DeselectAll(); + deselectAllButton?.TriggerClick(); return true; } diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 6e1c9b7a59..790333ea5b 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -4,6 +4,7 @@ using System; using osu.Game.Overlays; using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; @@ -17,6 +18,8 @@ namespace osu.Game.Screens.OnlinePlay { protected override bool ShowTotalMultiplier => false; + protected override bool AllowCustomisation => false; + public new Func IsValidMod { get => base.IsValidMod; @@ -31,22 +34,13 @@ namespace osu.Game.Screens.OnlinePlay protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys); - protected override IEnumerable CreateFooterButtons() => new[] - { + protected override IEnumerable CreateFooterButtons() => base.CreateFooterButtons().Prepend( new ShearedButton(BUTTON_WIDTH) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Text = CommonStrings.SelectAll, Action = SelectAll - }, - new ShearedButton(BUTTON_WIDTH) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Text = CommonStrings.DeselectAll, - Action = DeselectAll - } - }; + }); } } From b6575c216b8945bceee462216d87b4ac79d7e0a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 May 2022 03:18:44 +0900 Subject: [PATCH 1098/1276] Allow selecting all mods at free mod select using `ctrl`+`a` --- .../TestSceneFreeModSelectOverlay.cs | 13 +++++++++ .../TestSceneModSelectOverlay.cs | 5 +--- .../OnlinePlay/FreeModSelectOverlay.cs | 28 +++++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index f40c31b07f..c37bff2066 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; @@ -55,6 +56,18 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("customisation area not expanded", () => this.ChildrenOfType().Single().Height == 0); } + [Test] + public void TestSelectDeselectAllViaKeyboard() + { + createFreeModSelect(); + + AddStep("press ctrl+a", () => InputManager.Keys(PlatformAction.SelectAll)); + AddUntilStep("all mods selected", assertAllAvailableModsSelected); + + AddStep("press backspace", () => InputManager.Key(Key.BackSpace)); + AddUntilStep("all mods deselected", () => !freeModSelectOverlay.SelectedMods.Value.Any()); + } + [Test] public void TestSelectDeselectAll() { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 2cc9a451a8..75e30f76c3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -425,10 +425,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); - AddStep("press backspace", () => - { - InputManager.Key(Key.BackSpace); - }); + AddStep("press backspace", () => InputManager.Key(Key.BackSpace)); AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any()); } diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 790333ea5b..f494f9799a 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -6,6 +6,9 @@ using osu.Game.Overlays; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; @@ -14,7 +17,7 @@ using osu.Game.Localisation; namespace osu.Game.Screens.OnlinePlay { - public class FreeModSelectOverlay : ModSelectOverlay + public class FreeModSelectOverlay : ModSelectOverlay, IKeyBindingHandler { protected override bool ShowTotalMultiplier => false; @@ -26,6 +29,8 @@ namespace osu.Game.Screens.OnlinePlay set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m); } + private ShearedButton selectAllButton; + public FreeModSelectOverlay() : base(OverlayColourScheme.Plum) { @@ -35,12 +40,31 @@ namespace osu.Game.Screens.OnlinePlay protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys); protected override IEnumerable CreateFooterButtons() => base.CreateFooterButtons().Prepend( - new ShearedButton(BUTTON_WIDTH) + selectAllButton = new ShearedButton(BUTTON_WIDTH) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Text = CommonStrings.SelectAll, Action = SelectAll }); + + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Repeat) + return false; + + switch (e.Action) + { + case PlatformAction.SelectAll: + selectAllButton.TriggerClick(); + return true; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } } } From 4bb06873d53d4459268e13305788cb9996d373ec Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 May 2022 21:54:52 +0300 Subject: [PATCH 1099/1276] Move badge hierarchy declaration to constructor for safer access --- osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs b/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs index d19aff3a69..a75fc8e888 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapBadge.cs @@ -32,26 +32,22 @@ namespace osu.Game.Overlays.BeatmapSet set => badgeLabel.Colour = value; } - private OsuSpriteText badgeLabel = null!; + private readonly Box background; + private readonly OsuSpriteText badgeLabel; protected BeatmapBadge() { AutoSizeAxes = Axes.Both; - } - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, OverlayColourProvider? colourProvider) - { InternalChild = new CircularContainer { Masking = true, AutoSizeAxes = Axes.Both, Children = new Drawable[] { - new Box + background = new Box { RelativeSizeAxes = Axes.Both, - Colour = colourProvider?.Background5 ?? colours.Gray2, }, badgeLabel = new OsuSpriteText { @@ -61,5 +57,11 @@ namespace osu.Game.Overlays.BeatmapSet } }; } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, OverlayColourProvider? colourProvider) + { + background.Colour = colourProvider?.Background5 ?? colours.Gray2; + } } } From 92ae652555a0ca85941d87d1de040d7427cedf5f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 May 2022 22:07:42 +0300 Subject: [PATCH 1100/1276] Enable indirect input events on iOS for proper mouse support --- osu.Game.Rulesets.Catch.Tests.iOS/Info.plist | 2 ++ osu.Game.Rulesets.Mania.Tests.iOS/Info.plist | 2 ++ osu.Game.Rulesets.Osu.Tests.iOS/Info.plist | 2 ++ osu.Game.Rulesets.Taiko.Tests.iOS/Info.plist | 2 ++ osu.Game.Tests.iOS/Info.plist | 2 ++ osu.iOS/Info.plist | 2 ++ 6 files changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/Info.plist b/osu.Game.Rulesets.Catch.Tests.iOS/Info.plist index 33ddac6dfb..16a2b99997 100644 --- a/osu.Game.Rulesets.Catch.Tests.iOS/Info.plist +++ b/osu.Game.Rulesets.Catch.Tests.iOS/Info.plist @@ -37,6 +37,8 @@ XSAppIconAssets Assets.xcassets/AppIcon.appiconset + UIApplicationSupportsIndirectInputEvents + CADisableMinimumFrameDurationOnPhone diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/Info.plist b/osu.Game.Rulesets.Mania.Tests.iOS/Info.plist index 78349334b4..82d1c8ea24 100644 --- a/osu.Game.Rulesets.Mania.Tests.iOS/Info.plist +++ b/osu.Game.Rulesets.Mania.Tests.iOS/Info.plist @@ -37,6 +37,8 @@ XSAppIconAssets Assets.xcassets/AppIcon.appiconset + UIApplicationSupportsIndirectInputEvents + CADisableMinimumFrameDurationOnPhone diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/Info.plist b/osu.Game.Rulesets.Osu.Tests.iOS/Info.plist index b9f371c049..a88b74695c 100644 --- a/osu.Game.Rulesets.Osu.Tests.iOS/Info.plist +++ b/osu.Game.Rulesets.Osu.Tests.iOS/Info.plist @@ -37,6 +37,8 @@ XSAppIconAssets Assets.xcassets/AppIcon.appiconset + UIApplicationSupportsIndirectInputEvents + CADisableMinimumFrameDurationOnPhone diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/Info.plist b/osu.Game.Rulesets.Taiko.Tests.iOS/Info.plist index 65c47d2115..9628475b3e 100644 --- a/osu.Game.Rulesets.Taiko.Tests.iOS/Info.plist +++ b/osu.Game.Rulesets.Taiko.Tests.iOS/Info.plist @@ -37,6 +37,8 @@ XSAppIconAssets Assets.xcassets/AppIcon.appiconset + UIApplicationSupportsIndirectInputEvents + CADisableMinimumFrameDurationOnPhone diff --git a/osu.Game.Tests.iOS/Info.plist b/osu.Game.Tests.iOS/Info.plist index ed0c2e4dbf..31e2b3f257 100644 --- a/osu.Game.Tests.iOS/Info.plist +++ b/osu.Game.Tests.iOS/Info.plist @@ -37,6 +37,8 @@ XSAppIconAssets Assets.xcassets/AppIcon.appiconset + UIApplicationSupportsIndirectInputEvents + CADisableMinimumFrameDurationOnPhone diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist index 02968b87a7..16cb68fa7d 100644 --- a/osu.iOS/Info.plist +++ b/osu.iOS/Info.plist @@ -33,6 +33,8 @@ UIStatusBarHidden + UIApplicationSupportsIndirectInputEvents + CADisableMinimumFrameDurationOnPhone NSCameraUsageDescription From 170df01b4615b2aa73d6ec67c61b4e7b1101654b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 15 May 2022 20:10:53 +0200 Subject: [PATCH 1101/1276] Adjust difficulty multiplier scale transition on mod overlay The previous transition was supposed to be a center-anchored elastic scale-in, but this didn't work as intended - because the multiplier ended up inside of an auto-sized right-aligned container, the animation itself would end up being anchored right. Attempts to remove the scale transition resulted in a rather jarring-looking result, so swap out the elastic scale-in for a sweep-in effect from the top, to match the header and avoid introducing too many directions of movement. Delay values tweaked "to taste" - can be adjusted further if there is an alternative set of values that feels better. --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 1dad185b78..8d72855217 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -428,9 +428,9 @@ namespace osu.Game.Overlays.Mods base.PopIn(); multiplierDisplay? - .Delay(fade_in_duration * 0.65f) - .FadeIn(fade_in_duration / 2, Easing.OutQuint) - .ScaleTo(1, fade_in_duration, Easing.OutElastic); + .Delay(fade_in_duration / 5) + .FadeIn(fade_in_duration, Easing.OutQuint) + .MoveToY(0, fade_in_duration, Easing.OutQuint); int nonFilteredColumnCount = 0; @@ -465,7 +465,7 @@ namespace osu.Game.Overlays.Mods multiplierDisplay? .FadeOut(fade_out_duration / 2, Easing.OutQuint) - .ScaleTo(0.75f, fade_out_duration, Easing.OutQuint); + .MoveToY(-distance, fade_out_duration / 2, Easing.OutQuint); int nonFilteredColumnCount = 0; From ae5b6c3e1068a130396c3a2f8fab554c362603c5 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sun, 15 May 2022 19:38:37 +0100 Subject: [PATCH 1102/1276] Use dummy channel to show selector and remove `ChannelListSelector` Add dummy channel `DummySelectorChannel` which should be set as the current channel in the channel manager when the selector in the chat overlay should be shown. Refactors the `ChannelListItem` to not show mention pill and close button when the channel is the dummy selector channel. Ensure that the `ChannelList` selects the dummy channel on clicking the selector item. Removes `ChannelListSelector` as it is no longer needed. Removes the `setCurrent` parameter from `ChannelManager.JoinChannel` method as it is no longer needed. --- .../Visual/Online/TestSceneChannelList.cs | 12 +- .../Visual/Online/TestSceneChatOverlayV2.cs | 5 +- osu.Game/Online/Chat/ChannelManager.cs | 18 +-- .../Overlays/Chat/ChannelList/ChannelList.cs | 10 +- .../Chat/ChannelList/ChannelListItem.cs | 60 +++++++--- .../Chat/ChannelList/ChannelListSelector.cs | 103 ------------------ osu.Game/Overlays/ChatOverlayV2.cs | 13 ++- 7 files changed, 80 insertions(+), 141 deletions(-) delete mode 100644 osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs index 9929642539..53a48fcc58 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -118,35 +118,37 @@ namespace osu.Game.Tests.Visual.Online { AddStep("Unread Selected", () => { - if (selected.Value != null) + if (validItem) channelList.GetItem(selected.Value).Unread.Value = true; }); AddStep("Read Selected", () => { - if (selected.Value != null) + if (validItem) channelList.GetItem(selected.Value).Unread.Value = false; }); AddStep("Add Mention Selected", () => { - if (selected.Value != null) + if (validItem) channelList.GetItem(selected.Value).Mentions.Value++; }); AddStep("Add 98 Mentions Selected", () => { - if (selected.Value != null) + if (validItem) channelList.GetItem(selected.Value).Mentions.Value += 98; }); AddStep("Clear Mentions Selected", () => { - if (selected.Value != null) + if (validItem) channelList.GetItem(selected.Value).Mentions.Value = 0; }); } + private bool validItem => selected.Value != null && !(selected.Value is DummySelectorChannel); + private Channel createRandomPublicChannel() { int id = RNG.Next(0, 10000); diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index bf1767cc96..48557a4ddb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -372,7 +372,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); - AddStep("Click selector", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); + AddStep("Click selector", () => clickDrawable(channelSelectorButton)); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); AddStep("Click listing", () => clickDrawable(chatOverlay.ChildrenOfType().Single())); AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox); @@ -407,6 +407,9 @@ namespace osu.Game.Tests.Visual.Online private ChatOverlayTopBar chatOverlayTopBar => chatOverlay.ChildrenOfType().Single(); + private ChannelListItem channelSelectorButton => + chatOverlay.ChildrenOfType().Single(item => item.Channel is DummySelectorChannel); + private void clickDrawable(Drawable d) { InputManager.MoveMouseTo(d); diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 1fe784f68b..9524e8c1a7 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -14,6 +14,7 @@ using osu.Game.Input; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; using osu.Game.Overlays.Chat.Tabs; namespace osu.Game.Online.Chat @@ -133,7 +134,9 @@ namespace osu.Game.Online.Chat private void currentChannelChanged(ValueChangedEvent e) { - if (!(e.NewValue is ChannelSelectorTabItem.ChannelSelectorTabChannel)) + bool isSelectorChannel = e.NewValue is ChannelSelectorTabItem.ChannelSelectorTabChannel || e.NewValue is DummySelectorChannel; + + if (!isSelectorChannel) JoinChannel(e.NewValue); } @@ -194,7 +197,6 @@ namespace osu.Game.Online.Chat createNewPrivateMessageRequest.Failure += exception => { - handlePostException(exception); target.ReplaceMessage(message, null); dequeueAndRun(); }; @@ -420,11 +422,10 @@ namespace osu.Game.Online.Chat /// Joins a channel if it has not already been joined. Must be called from the update thread. ///
/// The channel to join. - /// Set the channel to join as the current channel if the current channel is null. /// The joined channel. Note that this may not match the parameter channel as it is a backed object. - public Channel JoinChannel(Channel channel, bool setCurrent = true) => joinChannel(channel, true, setCurrent); + public Channel JoinChannel(Channel channel) => joinChannel(channel, true); - private Channel joinChannel(Channel channel, bool fetchInitialMessages = false, bool setCurrent = true) + private Channel joinChannel(Channel channel, bool fetchInitialMessages = false) { if (channel == null) return null; @@ -440,7 +441,7 @@ namespace osu.Game.Online.Chat case ChannelType.Multiplayer: // join is implicit. happens when you join a multiplayer game. // this will probably change in the future. - joinChannel(channel, fetchInitialMessages, setCurrent); + joinChannel(channel, fetchInitialMessages); return channel; case ChannelType.PM: @@ -461,7 +462,7 @@ namespace osu.Game.Online.Chat default: var req = new JoinChannelRequest(channel); - req.Success += () => joinChannel(channel, fetchInitialMessages, setCurrent); + req.Success += () => joinChannel(channel, fetchInitialMessages); req.Failure += ex => LeaveChannel(channel); api.Queue(req); return channel; @@ -473,8 +474,7 @@ namespace osu.Game.Online.Chat this.fetchInitialMessages(channel); } - if (setCurrent) - CurrentChannel.Value ??= channel; + CurrentChannel.Value ??= channel; return channel; } diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 6bdf5ee084..c9f3a4d380 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -18,13 +18,16 @@ namespace osu.Game.Overlays.Chat.ChannelList { public class ChannelList : Container { - public Action? OnRequestSelect; + public Action? OnRequestSelect; public Action? OnRequestLeave; private readonly Dictionary channelMap = new Dictionary(); + private readonly DummySelectorChannel dummySelectorChannel = new DummySelectorChannel(); + private ChannelListItemFlow publicChannelFlow = null!; private ChannelListItemFlow privateChannelFlow = null!; + private ChannelListItem selector = null!; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) @@ -50,16 +53,17 @@ namespace osu.Game.Overlays.Chat.ChannelList Children = new Drawable[] { publicChannelFlow = new ChannelListItemFlow("CHANNELS"), - new ChannelListSelector + selector = new ChannelListItem(dummySelectorChannel) { Margin = new MarginPadding { Bottom = 10 }, - Action = () => OnRequestSelect?.Invoke(null), }, privateChannelFlow = new ChannelListItemFlow("DIRECT MESSAGES"), }, }, }, }; + + selector.OnRequestSelect += chan => OnRequestSelect?.Invoke(chan); } public void AddChannel(Channel channel) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index dd571c9ad9..dfb0b6d781 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Chat.ChannelList private Box hoverBox = null!; private Box selectBox = null!; private OsuSpriteText text = null!; - private ChannelListItemCloseButton close = null!; + private Drawable close = null!; [Resolved] private Bindable selectedChannel { get; set; } = null!; @@ -97,20 +97,8 @@ namespace osu.Game.Overlays.Chat.ChannelList RelativeSizeAxes = Axes.X, Truncate = true, }, - new ChannelListItemMentionPill - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Right = 3 }, - Mentions = { BindTarget = Mentions }, - }, - close = new ChannelListItemCloseButton - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Right = 3 }, - Action = () => OnRequestLeave?.Invoke(Channel), - } + createMentionPill(), + close = createCloseButton(), } }, }, @@ -131,14 +119,20 @@ namespace osu.Game.Overlays.Chat.ChannelList protected override bool OnHover(HoverEvent e) { hoverBox.FadeIn(300, Easing.OutQuint); - close.FadeIn(300, Easing.OutQuint); + + if (!isSelector) + close.FadeIn(300, Easing.OutQuint); + return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { hoverBox.FadeOut(200, Easing.OutQuint); - close.FadeOut(200, Easing.OutQuint); + + if (!isSelector) + close.FadeOut(200, Easing.OutQuint); + base.OnHoverLost(e); } @@ -158,9 +152,37 @@ namespace osu.Game.Overlays.Chat.ChannelList }; } + private Drawable createMentionPill() + { + if (isSelector) + return Drawable.Empty(); + + return new ChannelListItemMentionPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Right = 3 }, + Mentions = { BindTarget = Mentions }, + }; + } + + private Drawable createCloseButton() + { + if (isSelector) + return Drawable.Empty(); + + return new ChannelListItemCloseButton + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Right = 3 }, + Action = () => OnRequestLeave?.Invoke(Channel), + }; + } + private void updateState() { - bool selected = selectedChannel.Value == Channel; + bool selected = selectedChannel.Value == Channel || (isSelector && selectedChannel.Value == null); if (selected) selectBox.FadeIn(300, Easing.OutQuint); @@ -172,5 +194,7 @@ namespace osu.Game.Overlays.Chat.ChannelList else text.FadeColour(colourProvider.Light3, 200, Easing.OutQuint); } + + private bool isSelector => Channel is DummySelectorChannel; } } diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs deleted file mode 100644 index a07aad2041..0000000000 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable enable - -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.Chat; - -namespace osu.Game.Overlays.Chat.ChannelList -{ - public class ChannelListSelector : OsuClickableContainer - { - private Box hoverBox = null!; - private Box selectBox = null!; - private OsuSpriteText text = null!; - - [Resolved] - private Bindable currentChannel { get; set; } = null!; - - [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - Height = 30; - RelativeSizeAxes = Axes.X; - - Children = new Drawable[] - { - hoverBox = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background3, - Alpha = 0f, - }, - selectBox = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background4, - Alpha = 0f, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 18, Right = 10 }, - Child = text = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Text = "Add more channels", - Font = OsuFont.Torus.With(size: 17, weight: FontWeight.SemiBold), - Colour = colourProvider.Light3, - Margin = new MarginPadding { Bottom = 2 }, - RelativeSizeAxes = Axes.X, - Truncate = true, - }, - }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - currentChannel.BindValueChanged(channel => - { - // This logic should be handled by the chat overlay rather than this component. - // Selected state should be moved to an abstract class and shared with ChannelListItem. - if (channel.NewValue == null) - { - text.FadeColour(colourProvider.Content1, 300, Easing.OutQuint); - selectBox.FadeIn(300, Easing.OutQuint); - } - else - { - text.FadeColour(colourProvider.Light3, 200, Easing.OutQuint); - selectBox.FadeOut(200, Easing.OutQuint); - } - }, true); - } - - protected override bool OnHover(HoverEvent e) - { - hoverBox.FadeIn(300, Easing.OutQuint); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - hoverBox.FadeOut(200, Easing.OutQuint); - base.OnHoverLost(e); - } - } -} diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 6eec0bbbf4..0307dcfdf3 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -154,7 +154,7 @@ namespace osu.Game.Overlays channelList.OnRequestSelect += channel => channelManager.CurrentChannel.Value = channel; channelList.OnRequestLeave += channel => channelManager.LeaveChannel(channel); - channelListing.OnRequestJoin += channel => channelManager.JoinChannel(channel, false); + channelListing.OnRequestJoin += channel => channelManager.JoinChannel(channel); channelListing.OnRequestLeave += channel => channelManager.LeaveChannel(channel); textBar.OnSearchTermsChanged += searchTerms => channelListing.SearchTerm = searchTerms; @@ -237,7 +237,7 @@ namespace osu.Game.Overlays { Channel? newChannel = channel.NewValue; - if (newChannel == null) + if (newChannel == null || newChannel is DummySelectorChannel) { // null channel denotes that we should be showing the listing. channelListing.State.Value = Visibility.Visible; @@ -293,4 +293,13 @@ namespace osu.Game.Overlays channelManager.PostMessage(message); } } + + public class DummySelectorChannel : Channel + { + public DummySelectorChannel() + { + Name = "Add more channels"; + Type = ChannelType.System; + } + } } From a633501687c0535f76b2422cbb5f04f0935afccf Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 16 May 2022 14:09:37 +0900 Subject: [PATCH 1103/1276] Change FilterTerms to use LocalisableString --- osu.Game/Graphics/UserInterface/TriangleButton.cs | 3 ++- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 3 ++- osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs | 3 ++- osu.Game/Overlays/Chat/Selection/ChannelListItem.cs | 3 ++- osu.Game/Overlays/Chat/Selection/ChannelSection.cs | 3 ++- osu.Game/Overlays/Music/PlaylistItem.cs | 3 ++- .../Overlays/Settings/Sections/Audio/OffsetSettings.cs | 2 +- osu.Game/Overlays/Settings/Sections/AudioSection.cs | 2 +- .../Overlays/Settings/Sections/Gameplay/ModsSettings.cs | 2 +- osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs | 3 ++- .../Settings/Sections/Input/KeyBindingsSubsection.cs | 3 ++- osu.Game/Overlays/Settings/SettingsButton.cs | 5 ++--- osu.Game/Overlays/Settings/SettingsDropdown.cs | 3 ++- osu.Game/Overlays/Settings/SettingsItem.cs | 7 ++++--- osu.Game/Overlays/Settings/SettingsSection.cs | 2 +- osu.Game/Overlays/Settings/SettingsSubsection.cs | 6 +----- .../Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs | 5 ++++- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 3 ++- 18 files changed, 35 insertions(+), 26 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs index 003a81f562..5ae6130039 100644 --- a/osu.Game/Graphics/UserInterface/TriangleButton.cs +++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics.Backgrounds; namespace osu.Game.Graphics.UserInterface @@ -27,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface }); } - public virtual IEnumerable FilterTerms => new[] { Text.ToString() }; + public virtual IEnumerable FilterTerms => new[] { Text }; public bool MatchingFilter { diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index ec56b6d784..3c0c3b69e8 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -6,6 +6,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; @@ -39,7 +40,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 private void updateCornerRadius() => Content.CornerRadius = DrawHeight / 2; - public virtual IEnumerable FilterTerms => new[] { Text.ToString() }; + public virtual IEnumerable FilterTerms => new[] { Text }; public bool MatchingFilter { diff --git a/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs b/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs index 1f0cbae7e2..86c81d5d79 100644 --- a/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs +++ b/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -28,7 +29,7 @@ namespace osu.Game.Overlays.Chat.Listing public readonly Channel Channel; public bool FilteringActive { get; set; } - public IEnumerable FilterTerms => new[] { Channel.Name, Channel.Topic ?? string.Empty }; + public IEnumerable FilterTerms => new LocalisableString[] { Channel.Name, Channel.Topic ?? string.Empty }; public bool MatchingFilter { set => this.FadeTo(value ? 1f : 0f, 100); } private Box hoverBox = null!; diff --git a/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs index 1e58e8b640..59989ade7b 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; @@ -36,7 +37,7 @@ namespace osu.Game.Overlays.Chat.Selection private Color4 topicColour; private Color4 hoverColour; - public IEnumerable FilterTerms => new[] { Channel.Name, Channel.Topic ?? string.Empty }; + public IEnumerable FilterTerms => new LocalisableString[] { Channel.Name, Channel.Topic ?? string.Empty }; public bool MatchingFilter { diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSection.cs b/osu.Game/Overlays/Chat/Selection/ChannelSection.cs index 537ac975ac..070332180c 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelSection.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelSection.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; @@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Chat.Selection public readonly FillFlowContainer ChannelFlow; public IEnumerable FilterableChildren => ChannelFlow.Children; - public IEnumerable FilterTerms => Array.Empty(); + public IEnumerable FilterTerms => Array.Empty(); public bool MatchingFilter { diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index f081cc0503..82599d3ec9 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -113,7 +114,7 @@ namespace osu.Game.Overlays.Music } } - public IEnumerable FilterTerms => Model.PerformRead(m => m.Metadata.GetSearchableTerms()); + public IEnumerable FilterTerms => Model.PerformRead(m => m.Metadata.GetSearchableTerms()).Select(s => (LocalisableString)s).ToArray(); private bool matchingFilter = true; diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index 673252a99e..eaacb9293f 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { protected override LocalisableString Header => AudioSettingsStrings.OffsetHeader; - public override IEnumerable FilterTerms => base.FilterTerms.Concat(new[] { "universal", "uo", "timing" }); + public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "universal", "uo", "timing" }); [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/AudioSection.cs b/osu.Game/Overlays/Settings/Sections/AudioSection.cs index 694da0529a..7ffa0bd415 100644 --- a/osu.Game/Overlays/Settings/Sections/AudioSection.cs +++ b/osu.Game/Overlays/Settings/Sections/AudioSection.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections Icon = FontAwesome.Solid.VolumeUp }; - public override IEnumerable FilterTerms => base.FilterTerms.Concat(new[] { "sound" }); + public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "sound" }); public AudioSection() { diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs index dfa060e8d5..ed88d80570 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { protected override LocalisableString Header => GameplaySettingsStrings.ModsHeader; - public override IEnumerable FilterTerms => base.FilterTerms.Concat(new[] { "mod" }); + public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "mod" }); [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 459405f57d..7312748435 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -67,7 +68,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private Bindable isDefault { get; } = new BindableBool(true); - public IEnumerable FilterTerms => bindings.Select(b => keyCombinationProvider.GetReadableString(b.KeyCombination)).Prepend(text.Text.ToString()); + public IEnumerable FilterTerms => bindings.Select(b => (LocalisableString)keyCombinationProvider.GetReadableString(b.KeyCombination)).Prepend(text.Text); public KeyBindingRow(object action, List bindings) { diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index 922d371261..297af35fb5 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Database; using osu.Game.Input.Bindings; using osu.Game.Rulesets; @@ -74,6 +75,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input } // Empty FilterTerms so that the ResetButton is visible only when the whole subsection is visible. - public override IEnumerable FilterTerms => Enumerable.Empty(); + public override IEnumerable FilterTerms => Enumerable.Empty(); } } diff --git a/osu.Game/Overlays/Settings/SettingsButton.cs b/osu.Game/Overlays/Settings/SettingsButton.cs index be7f2de480..10aea92b22 100644 --- a/osu.Game/Overlays/Settings/SettingsButton.cs +++ b/osu.Game/Overlays/Settings/SettingsButton.cs @@ -20,13 +20,12 @@ namespace osu.Game.Overlays.Settings public LocalisableString TooltipText { get; set; } - public override IEnumerable FilterTerms + public override IEnumerable FilterTerms { get { if (TooltipText != default) - // TODO: this won't work as intended once the tooltip text is translated. - return base.FilterTerms.Append(TooltipText.ToString()); + return base.FilterTerms.Append(TooltipText); return base.FilterTerms; } diff --git a/osu.Game/Overlays/Settings/SettingsDropdown.cs b/osu.Game/Overlays/Settings/SettingsDropdown.cs index 1e90222d28..3c10c084ab 100644 --- a/osu.Game/Overlays/Settings/SettingsDropdown.cs +++ b/osu.Game/Overlays/Settings/SettingsDropdown.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings @@ -25,7 +26,7 @@ namespace osu.Game.Overlays.Settings set => Control.ItemSource = value; } - public override IEnumerable FilterTerms => base.FilterTerms.Concat(Control.Items.Select(i => i.ToString())); + public override IEnumerable FilterTerms => base.FilterTerms.Concat(Control.Items.Select(i => (LocalisableString)i.ToString())); protected sealed override Drawable CreateControl() => CreateDropdown(); diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index afcd41af22..ee9daa1c0d 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -98,13 +99,13 @@ namespace osu.Game.Overlays.Settings set => controlWithCurrent.Current = value; } - public virtual IEnumerable FilterTerms + public virtual IEnumerable FilterTerms { get { - var keywords = new List(Keywords ?? Array.Empty()) + var keywords = new List(Keywords?.Select(k => (LocalisableString)k) ?? Array.Empty()) { - LabelText.ToString() + LabelText }; if (HasClassicDefault) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index cfb0212b8c..da596e4d9d 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Settings public abstract LocalisableString Header { get; } public IEnumerable FilterableChildren => Children.OfType(); - public virtual IEnumerable FilterTerms => new[] { Header.ToString() }; + public virtual IEnumerable FilterTerms => new[] { Header }; public const int ITEM_SPACING = 14; diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs index c2cf08ac98..21391d5ccf 100644 --- a/osu.Game/Overlays/Settings/SettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs @@ -25,11 +25,7 @@ namespace osu.Game.Overlays.Settings public IEnumerable FilterableChildren => Children.OfType(); - // FilterTerms should contains both original string and localised string for user to search. - // Since LocalisableString is unable to get original string at this time (2021-08-14), - // only call .ToString() to use localised one. - // TODO: Update here when FilterTerms accept LocalisableString. - public virtual IEnumerable FilterTerms => new[] { Header.ToString() }; + public virtual IEnumerable FilterTerms => new[] { Header }; public bool MatchingFilter { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 74e4225f11..d61fbea387 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -80,7 +80,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components matchingFilter &= criteria.Ruleset == null || r.Room.PlaylistItemStats.Value?.RulesetIDs.Any(id => id == criteria.Ruleset.OnlineID) != false; if (!string.IsNullOrEmpty(criteria.SearchString)) - matchingFilter &= r.FilterTerms.Any(term => term.Contains(criteria.SearchString, StringComparison.InvariantCultureIgnoreCase)); + { + // Room name isn't translatable, so ToString() is used here for simplicity. + matchingFilter &= r.FilterTerms.Any(term => term.ToString().Contains(criteria.SearchString, StringComparison.InvariantCultureIgnoreCase)); + } r.MatchingFilter = matchingFilter; } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 7baa346c6f..35c903eb0c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -101,7 +102,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge public bool FilteringActive { get; set; } - public IEnumerable FilterTerms => new[] { Room.Name.Value }; + public IEnumerable FilterTerms => new LocalisableString[] { Room.Name.Value }; private bool matchingFilter = true; From 8c9edee360ba44364b5d3796f86e54db8bc04bf1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 15:47:00 +0900 Subject: [PATCH 1104/1276] Add ruleset context to sentry error reports --- osu.Game/Utils/SentryLogger.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 3edb27ca9b..ace397708e 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -18,6 +18,7 @@ using osu.Game.Database; using osu.Game.Models; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; +using osu.Game.Rulesets; using osu.Game.Skinning; using Sentry; using Sentry.Protocol; @@ -109,6 +110,7 @@ namespace osu.Game.Utils }, scope => { var beatmap = game.Dependencies.Get>().Value.BeatmapInfo; + var ruleset = game.Dependencies.Get>().Value; scope.Contexts[@"config"] = new { @@ -137,9 +139,17 @@ namespace osu.Game.Utils scope.Contexts[@"beatmap"] = new { Name = beatmap.ToString(), + Ruleset = beatmap.Ruleset.InstantiationInfo, beatmap.OnlineID, }; + scope.Contexts[@"ruleset"] = new + { + ruleset.Name, + ruleset.InstantiationInfo, + ruleset.OnlineID + }; + scope.Contexts[@"clocks"] = new { Audio = game.Dependencies.Get().CurrentTrack.CurrentTime, From 5ec05a86535038c8dd2a79d1f34c30d24360ba02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 15:50:15 +0900 Subject: [PATCH 1105/1276] Add tags for current screen and ruleset --- osu.Game/OsuGame.cs | 2 ++ osu.Game/Utils/SentryLogger.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3d56d33689..785881d97a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1207,6 +1207,8 @@ namespace osu.Game Current = newScreen?.GetType().ReadableName(), Previous = current?.GetType().ReadableName(), }; + + scope.SetTag(@"screen", newScreen?.GetType().ReadableName() ?? @"none"); }); switch (newScreen) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index ace397708e..b5d318bc64 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -145,6 +145,7 @@ namespace osu.Game.Utils scope.Contexts[@"ruleset"] = new { + ruleset.ShortName, ruleset.Name, ruleset.InstantiationInfo, ruleset.OnlineID @@ -155,6 +156,8 @@ namespace osu.Game.Utils Audio = game.Dependencies.Get().CurrentTrack.CurrentTime, Game = game.Clock.CurrentTime, }; + + scope.SetTag(@"ruleset", ruleset.ShortName); }); } else From 9331c6230669ebccdd2a244f317299a5a647a3d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 16:07:56 +0900 Subject: [PATCH 1106/1276] Also add realm ruleset count --- osu.Game/Utils/SentryLogger.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index b5d318bc64..5390c666ed 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -127,6 +127,8 @@ namespace osu.Game.Utils BeatmapSets = realm.All().Count(), Beatmaps = realm.All().Count(), Files = realm.All().Count(), + Rulesets = realm.All().Count(), + RulesetsAvailable = realm.All().Count(r => r.Available), Skins = realm.All().Count(), } }; From f78eb0066c7066f1f0ba4c71344a17487e8ef7a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 17:51:12 +0900 Subject: [PATCH 1107/1276] Update `Storage` in line with new methods --- osu.Game/IO/WrappedStorage.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index a6605de1d2..d4d9d531fa 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -67,9 +67,13 @@ namespace osu.Game.IO public override IEnumerable GetFiles(string path, string pattern = "*") => ToLocalRelative(UnderlyingStorage.GetFiles(MutatePath(path), pattern)); + public override Stream CreateFileSafely(string path) => UnderlyingStorage.CreateFileSafely(path); + public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) => UnderlyingStorage.GetStream(MutatePath(path), access, mode); + public override void Move(string from, string to) => UnderlyingStorage.Move(from, to); + public override bool OpenFileExternally(string filename) => UnderlyingStorage.OpenFileExternally(MutatePath(filename)); public override bool PresentFileExternally(string filename) => UnderlyingStorage.PresentFileExternally(MutatePath(filename)); From 218642c300417c38e0b20b7af19911e4a36f6364 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 18:03:53 +0900 Subject: [PATCH 1108/1276] Update unsafe file write usages --- osu.Game.Tests/Database/BeatmapImporterTests.cs | 2 +- osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs | 3 +-- osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs | 2 +- osu.Game.Tournament.Tests/Screens/TestSceneDrawingsScreen.cs | 2 +- osu.Game.Tournament/Models/StableInfo.cs | 2 +- osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs | 2 +- osu.Game.Tournament/TournamentGameBase.cs | 2 +- osu.Game/Database/LegacyExporter.cs | 2 +- osu.Game/Database/RealmAccess.cs | 2 +- osu.Game/Graphics/ScreenshotManager.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Stores/RealmFileStore.cs | 2 +- 12 files changed, 12 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Database/BeatmapImporterTests.cs b/osu.Game.Tests/Database/BeatmapImporterTests.cs index b7bfe14402..00276955aa 100644 --- a/osu.Game.Tests/Database/BeatmapImporterTests.cs +++ b/osu.Game.Tests/Database/BeatmapImporterTests.cs @@ -507,7 +507,7 @@ namespace osu.Game.Tests.Database using (var stream = storage.GetStream(firstFile.File.GetStoragePath())) originalLength = stream.Length; - using (var stream = storage.GetStream(firstFile.File.GetStoragePath(), FileAccess.Write, FileMode.Create)) + using (var stream = storage.CreateFileSafely(firstFile.File.GetStoragePath())) stream.WriteByte(0); var importedSecondTime = await LoadOszIntoStore(importer, realm.Realm); diff --git a/osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs b/osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs index 8498b9b28f..2ea768b878 100644 --- a/osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs +++ b/osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.IO; using System.Linq; using System.Runtime.InteropServices; using NUnit.Framework; @@ -25,7 +24,7 @@ namespace osu.Game.Tests.Visual.Navigation if (isDisposing) return; - using (var outStream = LocalStorage.GetStream(DatabaseContextFactory.DATABASE_NAME, FileAccess.Write, FileMode.Create)) + using (var outStream = LocalStorage.CreateFileSafely(DatabaseContextFactory.DATABASE_NAME)) using (var stream = TestResources.OpenResource(DatabaseContextFactory.DATABASE_NAME)) stream.CopyTo(outStream); } diff --git a/osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs b/osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs index 4c1256df2e..e5c539bbf1 100644 --- a/osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tournament.Tests.NonVisual // ReSharper disable once AccessToDisposedClosure var storage = host.Storage.GetStorageForDirectory(Path.Combine("tournaments", "default")); - using (var stream = storage.GetStream("bracket.json", FileAccess.Write, FileMode.Create)) + using (var stream = storage.CreateFileSafely("bracket.json")) using (var writer = new StreamWriter(stream)) { writer.Write(@"{ diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneDrawingsScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneDrawingsScreen.cs index e2954c8f10..0e38c777ba 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneDrawingsScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneDrawingsScreen.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tournament.Tests.Screens [BackgroundDependencyLoader] private void load(Storage storage) { - using (var stream = storage.GetStream("drawings.txt", FileAccess.Write)) + using (var stream = storage.CreateFileSafely("drawings.txt")) using (var writer = new StreamWriter(stream)) { writer.WriteLine("KR : South Korea : KOR"); diff --git a/osu.Game.Tournament/Models/StableInfo.cs b/osu.Game.Tournament/Models/StableInfo.cs index 1ebc81c773..c835b11a4d 100644 --- a/osu.Game.Tournament/Models/StableInfo.cs +++ b/osu.Game.Tournament/Models/StableInfo.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tournament.Models public void SaveChanges() { - using (var stream = configStorage.GetStream(config_path, FileAccess.Write, FileMode.Create)) + using (var stream = configStorage.CreateFileSafely(config_path)) using (var sw = new StreamWriter(stream)) { sw.Write(JsonConvert.SerializeObject(this, diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index d02e0ebf86..53ac93afea 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -205,7 +205,7 @@ namespace osu.Game.Tournament.Screens.Drawings try { // Write to drawings_results - using (Stream stream = storage.GetStream(results_filename, FileAccess.Write, FileMode.Create)) + using (Stream stream = storage.CreateFileSafely(results_filename)) using (StreamWriter sw = new StreamWriter(stream)) { sw.Write(text); diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index a251a043f7..363baccb37 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -321,7 +321,7 @@ namespace osu.Game.Tournament Converters = new JsonConverter[] { new JsonPointConverter() } }); - using (var stream = storage.GetStream(BRACKET_FILENAME, FileAccess.Write, FileMode.Create)) + using (var stream = storage.CreateFileSafely(BRACKET_FILENAME)) using (var sw = new StreamWriter(stream)) sw.Write(serialisedLadder); } diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index ee960b6b30..992d1854e7 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -37,7 +37,7 @@ namespace osu.Game.Database { string filename = $"{item.GetDisplayString().GetValidArchiveContentFilename()}{FileExtension}"; - using (var stream = exportStorage.GetStream(filename, FileAccess.Write, FileMode.Create)) + using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); exportStorage.PresentFileExternally(filename); diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 937876a70e..dbd3b96763 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -242,7 +242,7 @@ namespace osu.Game.Database storage.Delete(Filename); using (var inputStream = storage.GetStream(recoveryFilename)) - using (var outputStream = storage.GetStream(Filename, FileAccess.Write, FileMode.Create)) + using (var outputStream = storage.CreateFileSafely(Filename)) inputStream.CopyTo(outputStream); storage.Delete(recoveryFilename); diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index a2f1a3d7b9..1fc2633143 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -118,7 +118,7 @@ namespace osu.Game.Graphics if (filename == null) return; - using (var stream = storage.GetStream(filename, FileAccess.Write)) + using (var stream = storage.CreateFileSafely(filename)) { switch (screenshotFormat.Value) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 2e4758a134..564cb6c131 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -243,7 +243,7 @@ namespace osu.Game { if (source != null) { - using (var destination = Storage.GetStream(Path.Combine(backup_folder, $"collection.{migration}.db"), FileAccess.Write, FileMode.CreateNew)) + using (var destination = Storage.CreateFileSafely(Path.Combine(backup_folder, $"collection.{migration}.db"))) source.CopyTo(destination); } } diff --git a/osu.Game/Stores/RealmFileStore.cs b/osu.Game/Stores/RealmFileStore.cs index b5dd3d64e4..457d70f29a 100644 --- a/osu.Game/Stores/RealmFileStore.cs +++ b/osu.Game/Stores/RealmFileStore.cs @@ -65,7 +65,7 @@ namespace osu.Game.Stores { data.Seek(0, SeekOrigin.Begin); - using (var output = Storage.GetStream(file.GetStoragePath(), FileAccess.Write)) + using (var output = Storage.CreateFileSafely(file.GetStoragePath())) data.CopyTo(output); data.Seek(0, SeekOrigin.Begin); From d7aed77340b2d1b9a16f9de41081af910f304a31 Mon Sep 17 00:00:00 2001 From: "Hugo \"ThePooN\" Denizart" Date: Mon, 16 May 2022 16:01:42 +0200 Subject: [PATCH 1109/1276] =?UTF-8?q?=F0=9F=91=B7=20Push=20tagged=20releas?= =?UTF-8?q?es=20to=20Sentry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/sentry-release.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/sentry-release.yml diff --git a/.github/workflows/sentry-release.yml b/.github/workflows/sentry-release.yml new file mode 100644 index 0000000000..8ca9f38234 --- /dev/null +++ b/.github/workflows/sentry-release.yml @@ -0,0 +1,26 @@ +name: Add Release to Sentry + +on: + push: + tags: + - '*' + +jobs: + sentry_release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Create Sentry release + uses: getsentry/action-release@v1 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ppy + SENTRY_PROJECT: osu + SENTRY_URL: https://sentry.ppy.sh/ + with: + environment: production + version: ${{ github.ref }} From 48b7e0f0900d52a23bb9fa1a753983c373ca6ec8 Mon Sep 17 00:00:00 2001 From: Jay L Date: Tue, 10 May 2022 18:17:40 +1000 Subject: [PATCH 1110/1276] Stamina Skill Rewrite and Refactor --- .../Difficulty/Skills/Stamina.cs | 139 +++++++++--------- .../Difficulty/TaikoDifficultyCalculator.cs | 20 +-- 2 files changed, 75 insertions(+), 84 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 54cf233d69..b10da95444 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -1,16 +1,50 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +using System; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; -using osu.Game.Rulesets.Difficulty.Utils; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { + class SingleKeyStamina + { + private double previousHitTime = -1; + + private double strainValueOf(DifficultyHitObject current) + { + if (previousHitTime == -1) + { + previousHitTime = current.StartTime; + return 0; + } + else + { + double objectStrain = 0.5; + objectStrain += speedBonus(current.StartTime - previousHitTime); + previousHitTime = current.StartTime; + return objectStrain; + } + } + + public double StrainValueAt(DifficultyHitObject current) + { + return strainValueOf(current); + } + + /// + /// Applies a speed bonus dependent on the time since the last hit performed using this key. + /// + /// The duration between the current and previous note hit using the same key. + private double speedBonus(double notePairDuration) + { + return 175 / Math.Pow(notePairDuration + 100, 1); + } + } + /// /// Calculates the stamina coefficient of taiko difficulty. /// @@ -22,39 +56,43 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.4; - /// - /// Maximum number of entries to keep in . - /// - private const int max_history_length = 2; + private SingleKeyStamina[] keyStamina = new SingleKeyStamina[4] + { + new SingleKeyStamina(), + new SingleKeyStamina(), + new SingleKeyStamina(), + new SingleKeyStamina() + }; - /// - /// The index of the hand this instance is associated with. - /// - /// - /// The value of 0 indicates the left hand (full alternating gameplay starting with left hand is assumed). - /// This naturally translates onto index offsets of the objects in the map. - /// - private readonly int hand; - - /// - /// Stores the last durations between notes hit with the hand indicated by . - /// - private readonly LimitedCapacityQueue notePairDurationHistory = new LimitedCapacityQueue(max_history_length); - - /// - /// Stores the of the last object that was hit by the other hand. - /// - private double offhandObjectDuration = double.MaxValue; + private int donIndex = 1; + private int katIndex = 3; /// /// Creates a skill. /// /// Mods for use in skill calculations. - /// Whether this instance is performing calculations for the right hand. - public Stamina(Mod[] mods, bool rightHand) + public Stamina(Mod[] mods) : base(mods) { - hand = rightHand ? 1 : 0; + } + + private SingleKeyStamina getNextSingleKeyStamina(TaikoDifficultyHitObject current) + { + if (current.HitType == HitType.Centre) + { + donIndex = donIndex == 0 ? 1 : 0; + return keyStamina[donIndex]; + } + else + { + katIndex = katIndex == 2 ? 3 : 2; + return keyStamina[katIndex]; + } + } + + private double sigmoid(double val, double center, double width) + { + return Math.Tanh(Math.E * -(val - center) / width); } protected override double StrainValueOf(DifficultyHitObject current) @@ -65,52 +103,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills } TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current; + double objectStrain = getNextSingleKeyStamina(hitObject).StrainValueAt(hitObject); - if (hitObject.ObjectIndex % 2 == hand) - { - double objectStrain = 1; - - if (hitObject.ObjectIndex == 1) - return 1; - - notePairDurationHistory.Enqueue(hitObject.DeltaTime + offhandObjectDuration); - - double shortestRecentNote = notePairDurationHistory.Min(); - objectStrain += speedBonus(shortestRecentNote); - - if (hitObject.StaminaCheese) - objectStrain *= cheesePenalty(hitObject.DeltaTime + offhandObjectDuration); - - return objectStrain; - } - - offhandObjectDuration = hitObject.DeltaTime; - return 0; - } - - /// - /// Applies a penalty for hit objects marked with . - /// - /// The duration between the current and previous note hit using the hand indicated by . - private double cheesePenalty(double notePairDuration) - { - if (notePairDuration > 125) return 1; - if (notePairDuration < 100) return 0.6; - - return 0.6 + (notePairDuration - 100) * 0.016; - } - - /// - /// Applies a speed bonus dependent on the time since the last hit performed using this hand. - /// - /// The duration between the current and previous note hit using the hand indicated by . - private double speedBonus(double notePairDuration) - { - if (notePairDuration >= 200) return 0; - - double bonus = 200 - notePairDuration; - bonus *= bonus; - return bonus / 100000; + return objectStrain; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index a9d512f076..de69f63bf4 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { private const double rhythm_skill_multiplier = 0.014; private const double colour_skill_multiplier = 0.01; - private const double stamina_skill_multiplier = 0.02; + private const double stamina_skill_multiplier = 0.021; public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) @@ -33,8 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { new Colour(mods), new Rhythm(mods), - new Stamina(mods, true), - new Stamina(mods, false), + new Stamina(mods) }; protected override Mod[] DifficultyAdjustmentMods => new Mod[] @@ -58,7 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty ); } - new StaminaCheeseDetector(taikoDifficultyHitObjects).FindCheese(); return taikoDifficultyHitObjects; } @@ -69,17 +67,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty var colour = (Colour)skills[0]; var rhythm = (Rhythm)skills[1]; - var staminaRight = (Stamina)skills[2]; - var staminaLeft = (Stamina)skills[3]; + var stamina = (Stamina)skills[2]; double colourRating = colour.DifficultyValue() * colour_skill_multiplier; double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier; - double staminaRating = (staminaRight.DifficultyValue() + staminaLeft.DifficultyValue()) * stamina_skill_multiplier; + double staminaRating = stamina.DifficultyValue() * stamina_skill_multiplier; double staminaPenalty = simpleColourPenalty(staminaRating, colourRating); staminaRating *= staminaPenalty; - double combinedRating = locallyCombinedDifficulty(colour, rhythm, staminaRight, staminaLeft, staminaPenalty); + double combinedRating = locallyCombinedDifficulty(colour, rhythm, stamina, staminaPenalty); double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating); double starRating = 1.4 * separatedRating + 0.5 * combinedRating; starRating = rescale(starRating); @@ -127,20 +124,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty /// For each section, the peak strains of all separate skills are combined into a single peak strain for the section. /// The resulting partial rating of the beatmap is a weighted sum of the combined peaks (higher peaks are weighted more). /// - private double locallyCombinedDifficulty(Colour colour, Rhythm rhythm, Stamina staminaRight, Stamina staminaLeft, double staminaPenalty) + private double locallyCombinedDifficulty(Colour colour, Rhythm rhythm, Stamina stamina, double staminaPenalty) { List peaks = new List(); var colourPeaks = colour.GetCurrentStrainPeaks().ToList(); var rhythmPeaks = rhythm.GetCurrentStrainPeaks().ToList(); - var staminaRightPeaks = staminaRight.GetCurrentStrainPeaks().ToList(); - var staminaLeftPeaks = staminaLeft.GetCurrentStrainPeaks().ToList(); + var staminaPeaks = stamina.GetCurrentStrainPeaks().ToList(); for (int i = 0; i < colourPeaks.Count; i++) { double colourPeak = colourPeaks[i] * colour_skill_multiplier; double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier; - double staminaPeak = (staminaRightPeaks[i] + staminaLeftPeaks[i]) * stamina_skill_multiplier * staminaPenalty; + double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier * staminaPenalty; double peak = norm(2, colourPeak, rhythmPeak, staminaPeak); From f15738fded36c06d022014871effae3c3c380709 Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 15 May 2022 22:09:24 +1000 Subject: [PATCH 1111/1276] Remove Redundant Detection Not needed, as nerf is not required anymore and has negative effect. --- .../Preprocessing/StaminaCheeseDetector.cs | 145 ------------------ 1 file changed, 145 deletions(-) delete mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs deleted file mode 100644 index 3b1a9ad777..0000000000 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/StaminaCheeseDetector.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using osu.Game.Rulesets.Difficulty.Utils; -using osu.Game.Rulesets.Taiko.Objects; - -namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing -{ - /// - /// Detects special hit object patterns which are easier to hit using special techniques - /// than normally assumed in the fully-alternating play style. - /// - /// - /// This component detects two basic types of patterns, leveraged by the following techniques: - /// - /// Rolling allows hitting patterns with quickly and regularly alternating notes with a single hand. - /// TL tapping makes hitting longer sequences of consecutive same-colour notes with little to no colour changes in-between. - /// - /// - public class StaminaCheeseDetector - { - /// - /// The minimum number of consecutive objects with repeating patterns that can be classified as hittable using a roll. - /// - private const int roll_min_repetitions = 12; - - /// - /// The minimum number of consecutive objects with repeating patterns that can be classified as hittable using a TL tap. - /// - private const int tl_min_repetitions = 16; - - /// - /// The list of all s in the map. - /// - private readonly List hitObjects; - - public StaminaCheeseDetector(List hitObjects) - { - this.hitObjects = hitObjects; - } - - /// - /// Finds and marks all objects in that special difficulty-reducing techiques apply to - /// with the flag. - /// - public void FindCheese() - { - findRolls(3); - findRolls(4); - - findTlTap(0, HitType.Rim); - findTlTap(1, HitType.Rim); - findTlTap(0, HitType.Centre); - findTlTap(1, HitType.Centre); - } - - /// - /// Finds and marks all sequences hittable using a roll. - /// - /// The length of a single repeating pattern to consider (triplets/quadruplets). - private void findRolls(int patternLength) - { - var history = new LimitedCapacityQueue(2 * patternLength); - - // for convenience, we're tracking the index of the item *before* our suspected repeat's start, - // as that index can be simply subtracted from the current index to get the number of elements in between - // without off-by-one errors - int indexBeforeLastRepeat = -1; - int lastMarkEnd = 0; - - for (int i = 0; i < hitObjects.Count; i++) - { - history.Enqueue(hitObjects[i]); - if (!history.Full) - continue; - - if (!containsPatternRepeat(history, patternLength)) - { - // we're setting this up for the next iteration, hence the +1. - // right here this index will point at the queue's front (oldest item), - // but that item is about to be popped next loop with an enqueue. - indexBeforeLastRepeat = i - history.Count + 1; - continue; - } - - int repeatedLength = i - indexBeforeLastRepeat; - if (repeatedLength < roll_min_repetitions) - continue; - - markObjectsAsCheese(Math.Max(lastMarkEnd, i - repeatedLength + 1), i); - lastMarkEnd = i; - } - } - - /// - /// Determines whether the objects stored in contain a repetition of a pattern of length . - /// - private static bool containsPatternRepeat(LimitedCapacityQueue history, int patternLength) - { - for (int j = 0; j < patternLength; j++) - { - if (history[j].HitType != history[j + patternLength].HitType) - return false; - } - - return true; - } - - /// - /// Finds and marks all sequences hittable using a TL tap. - /// - /// Whether sequences starting with an odd- (1) or even-indexed (0) hit object should be checked. - /// The type of hit to check for TL taps. - private void findTlTap(int parity, HitType type) - { - int tlLength = -2; - int lastMarkEnd = 0; - - for (int i = parity; i < hitObjects.Count; i += 2) - { - if (hitObjects[i].HitType == type) - tlLength += 2; - else - tlLength = -2; - - if (tlLength < tl_min_repetitions) - continue; - - markObjectsAsCheese(Math.Max(lastMarkEnd, i - tlLength + 1), i); - lastMarkEnd = i; - } - } - - /// - /// Marks all objects from to (inclusive) as . - /// - private void markObjectsAsCheese(int start, int end) - { - for (int i = start; i <= end; i++) - hitObjects[i].StaminaCheese = true; - } - } -} From d174099016363de0cd77f62b5eb0c183d2df0b47 Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 15 May 2022 22:09:54 +1000 Subject: [PATCH 1112/1276] Refactor and Cleanup of Stamina.cs --- .../Difficulty/Skills/Stamina.cs | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index b10da95444..6d4a1fd948 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; @@ -10,29 +9,28 @@ using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { - class SingleKeyStamina + /// + /// Stamina of a single key, calculated based on repetition speed. + /// + public class SingleKeyStamina { private double previousHitTime = -1; - private double strainValueOf(DifficultyHitObject current) + /// + /// Similar to + /// + public double StrainValueOf(DifficultyHitObject current) { if (previousHitTime == -1) { previousHitTime = current.StartTime; return 0; } - else - { - double objectStrain = 0.5; - objectStrain += speedBonus(current.StartTime - previousHitTime); - previousHitTime = current.StartTime; - return objectStrain; - } - } - public double StrainValueAt(DifficultyHitObject current) - { - return strainValueOf(current); + double objectStrain = 0.5; + objectStrain += speedBonus(current.StartTime - previousHitTime); + previousHitTime = current.StartTime; + return objectStrain; } /// @@ -41,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// The duration between the current and previous note hit using the same key. private double speedBonus(double notePairDuration) { - return 175 / Math.Pow(notePairDuration + 100, 1); + return 175 / (notePairDuration + 100); } } @@ -56,7 +54,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.4; - private SingleKeyStamina[] keyStamina = new SingleKeyStamina[4] + /// + /// Stamina of each individual keys, calculated based on repetition speed. + /// + private readonly SingleKeyStamina[] keyStamina = { new SingleKeyStamina(), new SingleKeyStamina(), @@ -64,7 +65,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills new SingleKeyStamina() }; + /// + /// Current index to for a don hit. + /// private int donIndex = 1; + + /// + /// Current index to for a kat hit. + /// private int katIndex = 3; /// @@ -76,23 +84,21 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { } + /// + /// Get the next to use for the given . + /// + /// The current . private SingleKeyStamina getNextSingleKeyStamina(TaikoDifficultyHitObject current) { + // Alternate key for the same color. if (current.HitType == HitType.Centre) { donIndex = donIndex == 0 ? 1 : 0; return keyStamina[donIndex]; } - else - { - katIndex = katIndex == 2 ? 3 : 2; - return keyStamina[katIndex]; - } - } - private double sigmoid(double val, double center, double width) - { - return Math.Tanh(Math.E * -(val - center) / width); + katIndex = katIndex == 2 ? 3 : 2; + return keyStamina[katIndex]; } protected override double StrainValueOf(DifficultyHitObject current) @@ -103,9 +109,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills } TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current; - double objectStrain = getNextSingleKeyStamina(hitObject).StrainValueAt(hitObject); - - return objectStrain; + return getNextSingleKeyStamina(hitObject).StrainValueOf(hitObject); } } } From b44afb19b16a19e7464c9f1281307f4c396e1fc7 Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 15 May 2022 22:10:18 +1000 Subject: [PATCH 1113/1276] Include Convert Penalty For low colour variance --- .../Difficulty/TaikoDifficultyCalculator.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index de69f63bf4..5816b9957c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -76,6 +76,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty double staminaPenalty = simpleColourPenalty(staminaRating, colourRating); staminaRating *= staminaPenalty; + if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0 && colourRating < 0.05) + { + staminaPenalty *= 0.25; + } + double combinedRating = locallyCombinedDifficulty(colour, rhythm, stamina, staminaPenalty); double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating); double starRating = 1.4 * separatedRating + 0.5 * combinedRating; From d94e30bed3985f3301ea8d769ad5060affc94755 Mon Sep 17 00:00:00 2001 From: Jay L Date: Sun, 15 May 2022 22:11:01 +1000 Subject: [PATCH 1114/1276] Rewrite Performance difficultyValue Calculation To change values in line with the community survey, pp has been rescaled immensely, with changes on both the lower and upper end. --- .../Difficulty/TaikoPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index a8122551ff..8d99fd3b87 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes attributes) { - double difficultyValue = Math.Pow(5.0 * Math.Max(1.0, attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.0; + double difficultyValue = Math.Pow(5 * Math.Max(1.0, attributes.StarRating / 0.175) - 4.0, 2.25) / 450.0; double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0); difficultyValue *= lengthBonus; From 94294ba351c2c4609328da2ada403a37b89c55b7 Mon Sep 17 00:00:00 2001 From: Jay L Date: Mon, 16 May 2022 10:26:47 +1000 Subject: [PATCH 1115/1276] Apply Review Changes --- osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs | 2 +- osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 6d4a1fd948..7846e97a65 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class SingleKeyStamina { - private double previousHitTime = -1; + private double previousHitTime; /// /// Similar to diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 5816b9957c..1aa31c6fe4 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -76,6 +76,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty double staminaPenalty = simpleColourPenalty(staminaRating, colourRating); staminaRating *= staminaPenalty; + //TODO : This is a temporary fix for the stamina rating of converts, due to their low colour variance. if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0 && colourRating < 0.05) { staminaPenalty *= 0.25; From c7ec95c0d0d4416561ef622e134b1aef81ea285e Mon Sep 17 00:00:00 2001 From: Jay L Date: Mon, 16 May 2022 14:25:07 +1000 Subject: [PATCH 1116/1276] Rectify Review Changes --- osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 7846e97a65..d54da8b869 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -14,14 +14,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// public class SingleKeyStamina { - private double previousHitTime; + private double? previousHitTime; /// /// Similar to /// public double StrainValueOf(DifficultyHitObject current) { - if (previousHitTime == -1) + if (previousHitTime == null) { previousHitTime = current.StartTime; return 0; From db6abd86f03bc538fa2c3ffda98120b883f37d5c Mon Sep 17 00:00:00 2001 From: Jay L Date: Mon, 16 May 2022 14:48:39 +1000 Subject: [PATCH 1117/1276] Rectify null value call tired programming is never a good idea --- osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index d54da8b869..744dafd57e 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills } double objectStrain = 0.5; - objectStrain += speedBonus(current.StartTime - previousHitTime); + objectStrain += speedBonus(current.StartTime - previousHitTime.Value); previousHitTime = current.StartTime; return objectStrain; } From 934bcaf82e8851415a44656151f30a1c9eb5ed7f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 16 May 2022 14:22:30 +0900 Subject: [PATCH 1118/1276] Adjust tests --- .../TaikoDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index 226da7df09..51332a1ece 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - [TestCase(2.2420075288523802d, 200, "diffcalc-test")] - [TestCase(2.2420075288523802d, 200, "diffcalc-test-strong")] + [TestCase(1.9971301024093662d, 200, "diffcalc-test")] + [TestCase(1.9971301024093662d, 200, "diffcalc-test-strong")] public void Test(double expectedStarRating, int expectedMaxCombo, string name) => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(3.134084469440479d, 200, "diffcalc-test")] - [TestCase(3.134084469440479d, 200, "diffcalc-test-strong")] + [TestCase(3.1645810961313674d, 200, "diffcalc-test")] + [TestCase(3.1645810961313674d, 200, "diffcalc-test-strong")] public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) => Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime()); From bc294b81575b92a878c733ec9fe121cd93675ae7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 12:53:13 +0900 Subject: [PATCH 1119/1276] Add sentry tags for operating system and processor count These would usually be in the log header, but right now that's not conveyed. An example use case would be better understanding https://sentry.ppy.sh/organizations/ppy/issues/846/?project=2&query=is%3Aunresolved&sort=freq&statsPeriod=14d, where we currently don't know whether it is a desktop or mobile OS. --- osu.Game/Utils/SentryLogger.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 5390c666ed..137bf7e0aa 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Net; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; @@ -160,6 +161,8 @@ namespace osu.Game.Utils }; scope.SetTag(@"ruleset", ruleset.ShortName); + scope.SetTag(@"os", $"{RuntimeInfo.OS} ({Environment.OSVersion})"); + scope.SetTag(@"processor count", Environment.ProcessorCount.ToString()); }); } else From 31137bebf8d414cdbcacce62e0365bc271999e6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 14:52:27 +0900 Subject: [PATCH 1120/1276] Revert realm to previous release to fix iOS crashes --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b984f528fe..e867de73f3 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -56,6 +56,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7f149b4e35..cd3a84a504 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -34,7 +34,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 932421a705..1f677e54fd 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -89,6 +89,6 @@ - + From ef5b2233d7403f671a9ac9e13c708e1d02aef52c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 19:21:19 +0900 Subject: [PATCH 1121/1276] Improve the first run progress button to show loading and completion status --- .../FirstRunSetup/ProgressRoundedButton.cs | 103 ++++++++++++++++++ .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 45 +------- 2 files changed, 106 insertions(+), 42 deletions(-) create mode 100644 osu.Game/Overlays/FirstRunSetup/ProgressRoundedButton.cs diff --git a/osu.Game/Overlays/FirstRunSetup/ProgressRoundedButton.cs b/osu.Game/Overlays/FirstRunSetup/ProgressRoundedButton.cs new file mode 100644 index 0000000000..ee2db1f3d4 --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetup/ProgressRoundedButton.cs @@ -0,0 +1,103 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osuTK; + +namespace osu.Game.Overlays.FirstRunSetup +{ + public class ProgressRoundedButton : RoundedButton + { + public new Action? Action; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + private ProgressBar progressBar = null!; + + private LoadingSpinner loading = null!; + + private SpriteIcon tick = null!; + + public ProgressRoundedButton() + { + base.Action = () => + { + loading.Show(); + Enabled.Value = false; + + Action?.Invoke(); + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + AddRange(new Drawable[] + { + progressBar = new ProgressBar(false) + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + FillColour = BackgroundColour, + Alpha = 0.5f, + Depth = float.MinValue + }, + new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding(15), + Size = new Vector2(20), + Children = new Drawable[] + { + loading = new LoadingSpinner + { + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, + }, + tick = new SpriteIcon + { + Icon = FontAwesome.Solid.Check, + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, + Alpha = 0, + } + } + }, + }); + } + + public void Complete() + { + loading.Hide(); + tick.FadeIn(500, Easing.OutQuint); + + Background.FadeColour(colours.Green, 500, Easing.OutQuint); + progressBar.FillColour = colours.Green; + + this.TransformBindableTo(progressBar.Current, 1, 500, Easing.OutQuint); + } + + public void Abort() + { + loading.Hide(); + Enabled.Value = true; + this.TransformBindableTo(progressBar.Current, 0, 500, Easing.OutQuint); + } + + public void SetProgress(double progress, bool animated) + { + this.TransformBindableTo(progressBar.Current, progress, animated ? 500 : 0, Easing.OutQuint); + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 66acdca8c7..f7615d5ce9 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -14,8 +14,6 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; using osu.Game.Online; using osuTK; @@ -127,7 +125,10 @@ namespace osu.Game.Overlays.FirstRunSetup if (t.IsCompletedSuccessfully) importBeatmapsButton.Complete(); else + { importBeatmapsButton.Enabled.Value = true; + importBeatmapsButton.Abort(); + } })); } }, @@ -214,45 +215,5 @@ namespace osu.Game.Overlays.FirstRunSetup downloadBundledButton.SetProgress(progress, true); } } - - private class ProgressRoundedButton : RoundedButton - { - [Resolved] - private OsuColour colours { get; set; } = null!; - - private ProgressBar progressBar = null!; - - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(progressBar = new ProgressBar(false) - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, - FillColour = BackgroundColour, - Alpha = 0.5f, - Depth = float.MinValue - }); - } - - public void Complete() - { - Enabled.Value = false; - - Background.FadeColour(colours.Green, 500, Easing.OutQuint); - progressBar.FillColour = colours.Green; - - this.TransformBindableTo(progressBar.Current, 1, 500, Easing.OutQuint); - } - - public void SetProgress(double progress, bool animated) - { - if (!Enabled.Value) - return; - - this.TransformBindableTo(progressBar.Current, progress, animated ? 500 : 0, Easing.OutQuint); - } - } } } From 4412fec41a0fdc5ea7ff1c063dcbf0aff594210a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 19:21:26 +0900 Subject: [PATCH 1122/1276] Add import from stable screen --- .../FirstRunSetupOverlayStrings.cs | 10 ++ .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 34 +----- .../FirstRunSetup/ScreenImportFromStable.cs | 105 ++++++++++++++++++ osu.Game/Overlays/FirstRunSetupOverlay.cs | 25 +++-- 4 files changed, 131 insertions(+), 43 deletions(-) create mode 100644 osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs index 91b427e2ca..6b5ca7534d 100644 --- a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -74,6 +74,16 @@ We recommend you give the new defaults a try, but if you'd like to have things f /// public static LocalisableString ClassicDefaults => new TranslatableString(getKey(@"classic_defaults"), @"Classic defaults"); + /// + /// "Welcome" + /// + public static LocalisableString ImportTitle => new TranslatableString(getKey(@"import_title"), @"Import"); + + /// + /// "Import content from stable" + /// + public static LocalisableString ImportContentFromStable => new TranslatableString(getKey(@"import_content_from_stable"), @"Import content from osu!(stable)"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index f7615d5ce9..17e04c0c99 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -25,7 +25,6 @@ namespace osu.Game.Overlays.FirstRunSetup public class ScreenBeatmaps : FirstRunSetupScreen { private ProgressRoundedButton downloadBundledButton = null!; - private ProgressRoundedButton importBeatmapsButton = null!; private ProgressRoundedButton downloadTutorialButton = null!; private OsuTextFlowContainer currentlyLoadedBeatmaps = null!; @@ -41,8 +40,8 @@ namespace osu.Game.Overlays.FirstRunSetup private IDisposable? beatmapSubscription; - [BackgroundDependencyLoader(permitNulls: true)] - private void load(LegacyImportManager? legacyImportManager) + [BackgroundDependencyLoader] + private void load() { Vector2 buttonSize = new Vector2(400, 50); @@ -104,35 +103,6 @@ namespace osu.Game.Overlays.FirstRunSetup Action = downloadBundled }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) - { - Colour = OverlayColourProvider.Content1, - Text = "If you have an existing osu! install, you can also choose to import your existing beatmap collection.", - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }, - importBeatmapsButton = new ProgressRoundedButton - { - Size = buttonSize, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - BackgroundColour = colours.Blue3, - Text = MaintenanceSettingsStrings.ImportBeatmapsFromStable, - Action = () => - { - importBeatmapsButton.Enabled.Value = false; - legacyImportManager?.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => - { - if (t.IsCompletedSuccessfully) - importBeatmapsButton.Complete(); - else - { - importBeatmapsButton.Enabled.Value = true; - importBeatmapsButton.Abort(); - } - })); - } - }, - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = FirstRunSetupBeatmapScreenStrings.ObtainMoreBeatmaps, diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs new file mode 100644 index 0000000000..c55bb07295 --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -0,0 +1,105 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Localisation; +using osu.Game.Overlays.Settings; +using osuTK; + +namespace osu.Game.Overlays.FirstRunSetup +{ + [LocalisableDescription(typeof(FirstRunSetupOverlayStrings), nameof(FirstRunSetupOverlayStrings.ImportTitle))] + public class ScreenImportFromStable : FirstRunSetupScreen + { + private ProgressRoundedButton importButton = null!; + + private SettingsCheckbox checkboxSkins = null!; + private SettingsCheckbox checkboxBeatmaps = null!; + private SettingsCheckbox checkboxScores = null!; + private SettingsCheckbox checkboxCollections = null!; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved(canBeNull: true)] + private LegacyImportManager? legacyImportManager { get; set; } + + [BackgroundDependencyLoader(permitNulls: true)] + private void load() + { + Vector2 buttonSize = new Vector2(400, 50); + + Content.Children = new Drawable[] + { + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) + { + Colour = OverlayColourProvider.Content1, + Text = + "If you have an installation of a previous osu! version, you can choose to migrate your existing content. Note that this will create a copy, and not affect your existing installation.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + checkboxBeatmaps = new SettingsCheckbox + { + LabelText = "Beatmaps", + Current = { Value = true } + }, + checkboxScores = new SettingsCheckbox + { + LabelText = "Scores", + Current = { Value = true } + }, + checkboxSkins = new SettingsCheckbox + { + LabelText = "Skins", + Current = { Value = true } + }, + checkboxCollections = new SettingsCheckbox + { + LabelText = "Collections", + Current = { Value = true } + }, + importButton = new ProgressRoundedButton + { + Size = buttonSize, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + BackgroundColour = colours.Blue3, + Text = FirstRunSetupOverlayStrings.ImportContentFromStable, + Action = runImport + }, + }; + } + + private void runImport() + { + importButton.Enabled.Value = false; + + StableContent importableContent = 0; + + if (checkboxBeatmaps.Current.Value) importableContent |= StableContent.Beatmaps; + if (checkboxScores.Current.Value) importableContent |= StableContent.Scores; + if (checkboxSkins.Current.Value) importableContent |= StableContent.Skins; + if (checkboxCollections.Current.Value) importableContent |= StableContent.Collections; + + legacyImportManager?.ImportFromStableAsync(importableContent) + .ContinueWith(t => Schedule(() => + { + if (t.IsCompletedSuccessfully) + importButton.Complete(); + else + { + importButton.Enabled.Value = true; + importButton.Abort(); + } + })); + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index cebb2f5e3b..dae2815e98 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using System.Collections.Generic; using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -17,6 +18,7 @@ using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; @@ -55,13 +57,7 @@ namespace osu.Game.Overlays ///
public FirstRunSetupScreen? CurrentScreen => (FirstRunSetupScreen?)stack?.CurrentScreen; - private readonly Type[] steps = - { - typeof(ScreenWelcome), - typeof(ScreenBeatmaps), - typeof(ScreenUIScale), - typeof(ScreenBehaviour), - }; + private readonly List steps = new List(); private Container screenContent = null!; @@ -77,9 +73,16 @@ namespace osu.Game.Overlays { } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuColour colours, LegacyImportManager? legacyImportManager) { + steps.Add(typeof(ScreenWelcome)); + steps.Add(typeof(ScreenBeatmaps)); + if (legacyImportManager?.SupportsImportFromStable == true) + steps.Add(typeof(ScreenImportFromStable)); + steps.Add(typeof(ScreenUIScale)); + steps.Add(typeof(ScreenBehaviour)); + Header.Title = FirstRunSetupOverlayStrings.FirstRunSetupTitle; Header.Description = FirstRunSetupOverlayStrings.FirstRunSetupDescription; @@ -313,7 +316,7 @@ namespace osu.Game.Overlays currentStepIndex++; - if (currentStepIndex < steps.Length) + if (currentStepIndex < steps.Count) { var nextScreen = (Screen)Activator.CreateInstance(steps[currentStepIndex.Value]); @@ -345,7 +348,7 @@ namespace osu.Game.Overlays return; bool isFirstStep = currentStepIndex == 0; - bool isLastStep = currentStepIndex == steps.Length - 1; + bool isLastStep = currentStepIndex == steps.Count - 1; if (isFirstStep) { From 6448c97929d56ef86edb13a0808c58026fdfcbf8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 19:57:00 +0900 Subject: [PATCH 1123/1276] Allow retrieving count of available stable imports --- osu.Game/Collections/CollectionManager.cs | 12 ++++++++++++ osu.Game/Database/LegacyImportManager.cs | 23 +++++++++++++++++++++++ osu.Game/Database/LegacyModelImporter.cs | 2 ++ 3 files changed, 37 insertions(+) diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index 5845e0d4d1..700b0f5dcb 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -111,6 +111,18 @@ namespace osu.Game.Collections public Action PostNotification { protected get; set; } + public Task GetAvailableCount(StableStorage stableStorage) + { + if (!stableStorage.Exists(database_name)) + return Task.FromResult(0); + + return Task.Run(() => + { + using (var stream = stableStorage.GetStream(database_name)) + return readCollections(stream).Count; + }); + } + /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. /// diff --git a/osu.Game/Database/LegacyImportManager.cs b/osu.Game/Database/LegacyImportManager.cs index 59394c2952..a9f37fe4a7 100644 --- a/osu.Game/Database/LegacyImportManager.cs +++ b/osu.Game/Database/LegacyImportManager.cs @@ -49,6 +49,29 @@ namespace osu.Game.Database public bool SupportsImportFromStable => RuntimeInfo.IsDesktop; + public async Task GetImportCount(StableContent content) + { + var stableStorage = await getStableStorage().ConfigureAwait(false); + + switch (content) + { + case StableContent.Beatmaps: + return await new LegacyBeatmapImporter(beatmaps).GetAvailableCount(stableStorage); + + case StableContent.Skins: + return await new LegacySkinImporter(skins).GetAvailableCount(stableStorage); + + case StableContent.Collections: + return await collections.GetAvailableCount(stableStorage); + + case StableContent.Scores: + return await new LegacyScoreImporter(scores).GetAvailableCount(stableStorage); + + default: + throw new ArgumentException($"Only one {nameof(StableContent)} flag should be specified."); + } + } + public async Task ImportFromStableAsync(StableContent content) { var stableStorage = await getStableStorage().ConfigureAwait(false); diff --git a/osu.Game/Database/LegacyModelImporter.cs b/osu.Game/Database/LegacyModelImporter.cs index d85fb5aab2..a6e0f5bf8b 100644 --- a/osu.Game/Database/LegacyModelImporter.cs +++ b/osu.Game/Database/LegacyModelImporter.cs @@ -34,6 +34,8 @@ namespace osu.Game.Database Importer = importer; } + public Task GetAvailableCount(StableStorage stableStorage) => Task.Run(() => GetStableImportPaths(stableStorage).Count()); + public Task ImportFromStableAsync(StableStorage stableStorage) { var storage = PrepareStableStorage(stableStorage); From 1666d13d262455c2323fe097f1d4bfbde0006675 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 20:13:34 +0900 Subject: [PATCH 1124/1276] Add item counts to import screen --- .../Overlays/FirstRunSetup/ScreenImportFromStable.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index c55bb07295..42a10854e8 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -4,6 +4,7 @@ #nullable enable using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Database; @@ -28,8 +29,8 @@ namespace osu.Game.Overlays.FirstRunSetup [Resolved] private OsuColour colours { get; set; } = null!; - [Resolved(canBeNull: true)] - private LegacyImportManager? legacyImportManager { get; set; } + [Resolved] + private LegacyImportManager legacyImportManager { get; set; } = null!; [BackgroundDependencyLoader(permitNulls: true)] private void load() @@ -76,6 +77,11 @@ namespace osu.Game.Overlays.FirstRunSetup Action = runImport }, }; + + legacyImportManager.GetImportCount(StableContent.Beatmaps).ContinueWith(task => Schedule(() => checkboxBeatmaps.LabelText += $" ({task.GetResultSafely()} items)")); + legacyImportManager.GetImportCount(StableContent.Scores).ContinueWith(task => Schedule(() => checkboxScores.LabelText += $" ({task.GetResultSafely()} items)")); + legacyImportManager.GetImportCount(StableContent.Skins).ContinueWith(task => Schedule(() => checkboxSkins.LabelText += $" ({task.GetResultSafely()} items)")); + legacyImportManager.GetImportCount(StableContent.Collections).ContinueWith(task => Schedule(() => checkboxCollections.LabelText += $" ({task.GetResultSafely()} items)")); } private void runImport() @@ -89,7 +95,7 @@ namespace osu.Game.Overlays.FirstRunSetup if (checkboxSkins.Current.Value) importableContent |= StableContent.Skins; if (checkboxCollections.Current.Value) importableContent |= StableContent.Collections; - legacyImportManager?.ImportFromStableAsync(importableContent) + legacyImportManager.ImportFromStableAsync(importableContent) .ContinueWith(t => Schedule(() => { if (t.IsCompletedSuccessfully) From 13e70eab515c2aeacec660fdf978908deb93e399 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 20:37:38 +0900 Subject: [PATCH 1125/1276] Allow cancellation of count operations and bypassing interactive location logic --- osu.Game/Database/LegacyImportManager.cs | 35 +++++--- .../FirstRunSetup/ScreenImportFromStable.cs | 87 +++++++++++++++++-- 2 files changed, 107 insertions(+), 15 deletions(-) diff --git a/osu.Game/Database/LegacyImportManager.cs b/osu.Game/Database/LegacyImportManager.cs index a9f37fe4a7..9c436e8903 100644 --- a/osu.Game/Database/LegacyImportManager.cs +++ b/osu.Game/Database/LegacyImportManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using osu.Framework; using osu.Framework.Allocation; @@ -49,9 +50,14 @@ namespace osu.Game.Database public bool SupportsImportFromStable => RuntimeInfo.IsDesktop; - public async Task GetImportCount(StableContent content) + public async Task GetImportCount(StableContent content, CancellationToken cancellationToken) { - var stableStorage = await getStableStorage().ConfigureAwait(false); + var stableStorage = GetCurrentStableStorage(); + + if (stableStorage == null) + return 0; + + cancellationToken.ThrowIfCancellationRequested(); switch (content) { @@ -72,9 +78,22 @@ namespace osu.Game.Database } } - public async Task ImportFromStableAsync(StableContent content) + public async Task ImportFromStableAsync(StableContent content, bool interactiveLocateIfNotFound = true) { - var stableStorage = await getStableStorage().ConfigureAwait(false); + var stableStorage = GetCurrentStableStorage(); + + if (stableStorage == null) + { + if (!interactiveLocateIfNotFound) + return; + + var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + Schedule(() => dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource))); + string stablePath = await taskCompletionSource.Task.ConfigureAwait(false); + + stableStorage = cachedStorage = new StableStorage(stablePath, desktopGameHost); + } + var importTasks = new List(); Task beatmapImportTask = Task.CompletedTask; @@ -93,7 +112,7 @@ namespace osu.Game.Database await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); } - private async Task getStableStorage() + public StableStorage GetCurrentStableStorage() { if (cachedStorage != null) return cachedStorage; @@ -102,11 +121,7 @@ namespace osu.Game.Database if (stableStorage != null) return cachedStorage = stableStorage; - var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - Schedule(() => dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource))); - string stablePath = await taskCompletionSource.Task.ConfigureAwait(false); - - return cachedStorage = new StableStorage(stablePath, desktopGameHost); + return null; } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index 42a10854e8..e31f70b686 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -3,6 +3,8 @@ #nullable enable +using System.Linq; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics; @@ -10,6 +12,7 @@ using osu.Framework.Localisation; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; using osu.Game.Overlays.Settings; using osuTK; @@ -26,12 +29,16 @@ namespace osu.Game.Overlays.FirstRunSetup private SettingsCheckbox checkboxScores = null!; private SettingsCheckbox checkboxCollections = null!; + private OsuTextFlowContainer currentStablePath = null!; + [Resolved] private OsuColour colours { get; set; } = null!; [Resolved] private LegacyImportManager legacyImportManager { get; set; } = null!; + private CancellationTokenSource? stablePathUpdateCancellation; + [BackgroundDependencyLoader(permitNulls: true)] private void load() { @@ -47,6 +54,21 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, + currentStablePath = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: HEADER_FONT_SIZE, weight: FontWeight.SemiBold)) + { + Colour = OverlayColourProvider.Content2, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + new RoundedButton + { + Size = buttonSize, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + BackgroundColour = colours.Blue3, + Text = "Locate osu!(stable) install", + Action = locateStable, + }, checkboxBeatmaps = new SettingsCheckbox { LabelText = "Beatmaps", @@ -78,10 +100,65 @@ namespace osu.Game.Overlays.FirstRunSetup }, }; - legacyImportManager.GetImportCount(StableContent.Beatmaps).ContinueWith(task => Schedule(() => checkboxBeatmaps.LabelText += $" ({task.GetResultSafely()} items)")); - legacyImportManager.GetImportCount(StableContent.Scores).ContinueWith(task => Schedule(() => checkboxScores.LabelText += $" ({task.GetResultSafely()} items)")); - legacyImportManager.GetImportCount(StableContent.Skins).ContinueWith(task => Schedule(() => checkboxSkins.LabelText += $" ({task.GetResultSafely()} items)")); - legacyImportManager.GetImportCount(StableContent.Collections).ContinueWith(task => Schedule(() => checkboxCollections.LabelText += $" ({task.GetResultSafely()} items)")); + updateStablePath(); + } + + private void locateStable() + { + legacyImportManager.ImportFromStableAsync(0).ContinueWith(task => + { + Schedule(updateStablePath); + }); + } + + private void updateStablePath() + { + stablePathUpdateCancellation?.Cancel(); + + var storage = legacyImportManager.GetCurrentStableStorage(); + + if (storage == null) + { + foreach (var c in Content.Children.OfType()) + c.Current.Disabled = true; + currentStablePath.Text = "No installation found"; + return; + } + + foreach (var c in Content.Children.OfType()) + c.Current.Disabled = false; + + currentStablePath.Text = storage.GetFullPath(string.Empty); + stablePathUpdateCancellation = new CancellationTokenSource(); + + legacyImportManager.GetImportCount(StableContent.Beatmaps, stablePathUpdateCancellation.Token).ContinueWith(task => Schedule(() => + { + if (task.IsCanceled) + return; + + checkboxBeatmaps.LabelText = $"Beatmaps ({task.GetResultSafely()} items)"; + })); + legacyImportManager.GetImportCount(StableContent.Scores, stablePathUpdateCancellation.Token).ContinueWith(task => Schedule(() => + { + if (task.IsCanceled) + return; + + checkboxScores.LabelText = $"Scores ({task.GetResultSafely()} items)"; + })); + legacyImportManager.GetImportCount(StableContent.Skins, stablePathUpdateCancellation.Token).ContinueWith(task => Schedule(() => + { + if (task.IsCanceled) + return; + + checkboxSkins.LabelText = $"Skins ({task.GetResultSafely()} items)"; + })); + legacyImportManager.GetImportCount(StableContent.Collections, stablePathUpdateCancellation.Token).ContinueWith(task => Schedule(() => + { + if (task.IsCanceled) + return; + + checkboxCollections.LabelText = $"Collections ({task.GetResultSafely()} items)"; + })); } private void runImport() @@ -95,7 +172,7 @@ namespace osu.Game.Overlays.FirstRunSetup if (checkboxSkins.Current.Value) importableContent |= StableContent.Skins; if (checkboxCollections.Current.Value) importableContent |= StableContent.Collections; - legacyImportManager.ImportFromStableAsync(importableContent) + legacyImportManager.ImportFromStableAsync(importableContent, false) .ContinueWith(t => Schedule(() => { if (t.IsCompletedSuccessfully) From 1593f05ff37c34bad7cc25b2f3d0d9ad9c84738d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 20:53:04 +0900 Subject: [PATCH 1126/1276] Tidy up checkbox implementation --- .../FirstRunSetup/ScreenImportFromStable.cs | 134 ++++++++---------- 1 file changed, 63 insertions(+), 71 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index e31f70b686..dd3580508a 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -3,6 +3,7 @@ #nullable enable +using System.Collections.Generic; using System.Linq; using System.Threading; using osu.Framework.Allocation; @@ -24,11 +25,6 @@ namespace osu.Game.Overlays.FirstRunSetup { private ProgressRoundedButton importButton = null!; - private SettingsCheckbox checkboxSkins = null!; - private SettingsCheckbox checkboxBeatmaps = null!; - private SettingsCheckbox checkboxScores = null!; - private SettingsCheckbox checkboxCollections = null!; - private OsuTextFlowContainer currentStablePath = null!; [Resolved] @@ -39,6 +35,8 @@ namespace osu.Game.Overlays.FirstRunSetup private CancellationTokenSource? stablePathUpdateCancellation; + private IEnumerable contentCheckboxes => Content.Children.OfType(); + [BackgroundDependencyLoader(permitNulls: true)] private void load() { @@ -69,26 +67,10 @@ namespace osu.Game.Overlays.FirstRunSetup Text = "Locate osu!(stable) install", Action = locateStable, }, - checkboxBeatmaps = new SettingsCheckbox - { - LabelText = "Beatmaps", - Current = { Value = true } - }, - checkboxScores = new SettingsCheckbox - { - LabelText = "Scores", - Current = { Value = true } - }, - checkboxSkins = new SettingsCheckbox - { - LabelText = "Skins", - Current = { Value = true } - }, - checkboxCollections = new SettingsCheckbox - { - LabelText = "Collections", - Current = { Value = true } - }, + new ImportCheckbox("Beatmaps", StableContent.Beatmaps), + new ImportCheckbox("Scores", StableContent.Scores), + new ImportCheckbox("Skins", StableContent.Skins), + new ImportCheckbox("Collections", StableContent.Collections), importButton = new ProgressRoundedButton { Size = buttonSize, @@ -119,46 +101,20 @@ namespace osu.Game.Overlays.FirstRunSetup if (storage == null) { - foreach (var c in Content.Children.OfType()) + foreach (var c in contentCheckboxes) c.Current.Disabled = true; currentStablePath.Text = "No installation found"; return; } - foreach (var c in Content.Children.OfType()) + foreach (var c in contentCheckboxes) + { c.Current.Disabled = false; + c.UpdateCount(); + } currentStablePath.Text = storage.GetFullPath(string.Empty); stablePathUpdateCancellation = new CancellationTokenSource(); - - legacyImportManager.GetImportCount(StableContent.Beatmaps, stablePathUpdateCancellation.Token).ContinueWith(task => Schedule(() => - { - if (task.IsCanceled) - return; - - checkboxBeatmaps.LabelText = $"Beatmaps ({task.GetResultSafely()} items)"; - })); - legacyImportManager.GetImportCount(StableContent.Scores, stablePathUpdateCancellation.Token).ContinueWith(task => Schedule(() => - { - if (task.IsCanceled) - return; - - checkboxScores.LabelText = $"Scores ({task.GetResultSafely()} items)"; - })); - legacyImportManager.GetImportCount(StableContent.Skins, stablePathUpdateCancellation.Token).ContinueWith(task => Schedule(() => - { - if (task.IsCanceled) - return; - - checkboxSkins.LabelText = $"Skins ({task.GetResultSafely()} items)"; - })); - legacyImportManager.GetImportCount(StableContent.Collections, stablePathUpdateCancellation.Token).ContinueWith(task => Schedule(() => - { - if (task.IsCanceled) - return; - - checkboxCollections.LabelText = $"Collections ({task.GetResultSafely()} items)"; - })); } private void runImport() @@ -167,22 +123,58 @@ namespace osu.Game.Overlays.FirstRunSetup StableContent importableContent = 0; - if (checkboxBeatmaps.Current.Value) importableContent |= StableContent.Beatmaps; - if (checkboxScores.Current.Value) importableContent |= StableContent.Scores; - if (checkboxSkins.Current.Value) importableContent |= StableContent.Skins; - if (checkboxCollections.Current.Value) importableContent |= StableContent.Collections; + foreach (var c in contentCheckboxes.Where(c => c.Current.Value)) + importableContent |= c.StableContent; - legacyImportManager.ImportFromStableAsync(importableContent, false) - .ContinueWith(t => Schedule(() => - { - if (t.IsCompletedSuccessfully) - importButton.Complete(); - else - { - importButton.Enabled.Value = true; - importButton.Abort(); - } - })); + legacyImportManager.ImportFromStableAsync(importableContent, false).ContinueWith(t => Schedule(() => + { + if (t.IsCompletedSuccessfully) + importButton.Complete(); + else + { + importButton.Enabled.Value = true; + importButton.Abort(); + } + })); + } + + private class ImportCheckbox : SettingsCheckbox + { + public readonly StableContent StableContent; + + private readonly LocalisableString title; + + [Resolved] + private LegacyImportManager legacyImportManager { get; set; } = null!; + + private CancellationTokenSource? countUpdateCancellation; + + public ImportCheckbox(LocalisableString title, StableContent stableContent) + { + this.title = title; + + StableContent = stableContent; + + Current.Value = true; + + LabelText = title; + } + + public void UpdateCount() + { + LabelText = LocalisableString.Interpolate($"{title} (calculating...)"); + + countUpdateCancellation?.Cancel(); + countUpdateCancellation = new CancellationTokenSource(); + + legacyImportManager.GetImportCount(StableContent, countUpdateCancellation.Token).ContinueWith(task => Schedule(() => + { + if (task.IsCanceled) + return; + + LabelText = LocalisableString.Interpolate($"{title} ({task.GetResultSafely()} items)"); + })); + } } } } From 4af1a788d14fadffcbd98953a769aacdd5b87d6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 21:07:42 +0900 Subject: [PATCH 1127/1276] Add locate stable button / screen --- osu.Game/Database/LegacyImportManager.cs | 5 +- .../FirstRunSetup/FirstRunSetupScreen.cs | 4 +- .../FirstRunSetup/ScreenImportFromStable.cs | 134 +++++++++++++++++- 3 files changed, 136 insertions(+), 7 deletions(-) diff --git a/osu.Game/Database/LegacyImportManager.cs b/osu.Game/Database/LegacyImportManager.cs index 9c436e8903..35b1d05fa1 100644 --- a/osu.Game/Database/LegacyImportManager.cs +++ b/osu.Game/Database/LegacyImportManager.cs @@ -50,6 +50,8 @@ namespace osu.Game.Database public bool SupportsImportFromStable => RuntimeInfo.IsDesktop; + public void UpdateStorage(string stablePath) => cachedStorage = new StableStorage(stablePath, desktopGameHost); + public async Task GetImportCount(StableContent content, CancellationToken cancellationToken) { var stableStorage = GetCurrentStableStorage(); @@ -91,7 +93,8 @@ namespace osu.Game.Database Schedule(() => dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource))); string stablePath = await taskCompletionSource.Task.ConfigureAwait(false); - stableStorage = cachedStorage = new StableStorage(stablePath, desktopGameHost); + UpdateStorage(stablePath); + stableStorage = GetCurrentStableStorage(); } var importTasks = new List(); diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index d1ea91e51a..b043f05bd8 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -21,6 +21,8 @@ namespace osu.Game.Overlays.FirstRunSetup protected const float CONTENT_FONT_SIZE = 16; + protected const float CONTENT_PADDING = 30; + protected const float HEADER_FONT_SIZE = 24; [Resolved] @@ -41,7 +43,7 @@ namespace osu.Game.Overlays.FirstRunSetup { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 30 }, + Padding = new MarginPadding { Horizontal = CONTENT_PADDING }, Children = new Drawable[] { new OsuSpriteText diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index dd3580508a..acc0fdbb7c 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -4,19 +4,24 @@ #nullable enable using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; +using osu.Framework.Screens; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.IO; using osu.Game.Localisation; using osu.Game.Overlays.Settings; using osuTK; +using CommonStrings = osu.Game.Resources.Localisation.Web.CommonStrings; namespace osu.Game.Overlays.FirstRunSetup { @@ -24,6 +29,7 @@ namespace osu.Game.Overlays.FirstRunSetup public class ScreenImportFromStable : FirstRunSetupScreen { private ProgressRoundedButton importButton = null!; + private RoundedButton locateStableButton = null!; private OsuTextFlowContainer currentStablePath = null!; @@ -58,7 +64,7 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }, - new RoundedButton + locateStableButton = new RoundedButton { Size = buttonSize, Anchor = Anchor.TopCentre, @@ -85,12 +91,15 @@ namespace osu.Game.Overlays.FirstRunSetup updateStablePath(); } - private void locateStable() + private void locateStable() => this.Push(new LocateStableScreen()); + + public override void OnResuming(ScreenTransitionEvent e) { - legacyImportManager.ImportFromStableAsync(0).ContinueWith(task => - { + base.OnResuming(e); + + if (e.Last is LocateStableScreen) + // stable storage may have changed. Schedule(updateStablePath); - }); } private void updateStablePath() @@ -120,6 +129,7 @@ namespace osu.Game.Overlays.FirstRunSetup private void runImport() { importButton.Enabled.Value = false; + locateStableButton.Enabled.Value = false; StableContent importableContent = 0; @@ -128,6 +138,8 @@ namespace osu.Game.Overlays.FirstRunSetup legacyImportManager.ImportFromStableAsync(importableContent, false).ContinueWith(t => Schedule(() => { + locateStableButton.Enabled.Value = true; + if (t.IsCompletedSuccessfully) importButton.Complete(); else @@ -176,5 +188,117 @@ namespace osu.Game.Overlays.FirstRunSetup })); } } + + private class LocateStableScreen : FirstRunSetupScreen + { + private RoundedButton selectionButton = null!; + + private OsuDirectorySelector directorySelector = null!; + + protected bool IsValidDirectory(DirectoryInfo? info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; + + public LocalisableString HeaderText => "Please select your osu!stable install location"; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + // Don't want the scroll content provided by `FirstRunSetupScreen` so we don't use `Content`. + InternalChild = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = CONTENT_PADDING }, + Children = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + new OsuTextFlowContainer(cp => + { + cp.Font = OsuFont.Default.With(size: 24); + }) + { + Text = HeaderText, + TextAnchor = Anchor.TopCentre, + Margin = new MarginPadding(10), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + }, + new Drawable[] + { + directorySelector = new OsuDirectorySelector + { + RelativeSizeAxes = Axes.Both, + } + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new RoundedButton + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = 300, + Margin = new MarginPadding(10), + Colour = colours.Pink2, + Text = CommonStrings.ButtonsCancel, + Action = this.Exit + }, + selectionButton = new RoundedButton + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Width = 300, + Margin = new MarginPadding(10), + Text = MaintenanceSettingsStrings.SelectDirectory, + Action = () => + { + legacyImportManager.UpdateStorage(directorySelector.CurrentPath.Value.FullName); + this.Exit(); + } + }, + } + }, + } + } + } + } + }; + } + + [Resolved] + private LegacyImportManager legacyImportManager { get; set; } = null!; + + protected override void LoadComplete() + { + if (legacyImportManager.GetCurrentStableStorage() is StableStorage storage) + directorySelector.CurrentPath.Value = new DirectoryInfo(storage.GetFullPath(string.Empty)); + + directorySelector.CurrentPath.BindValueChanged(e => selectionButton.Enabled.Value = e.NewValue != null && IsValidDirectory(e.NewValue), true); + base.LoadComplete(); + } + + public override void OnSuspending(ScreenTransitionEvent e) + { + base.OnSuspending(e); + + this.FadeOut(250); + } + } } } From 98e5ad44a71a090476a55ba40d7a0dfbb67cc327 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 21:24:36 +0900 Subject: [PATCH 1128/1276] Add `OverlayColourProvider` support to `OsuDirectorySelector` --- .../UserInterfaceV2/OsuDirectorySelectorDirectory.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs index ce2e7794a9..456bde6d1b 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -44,8 +45,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 internal class Background : CompositeDrawable { - [BackgroundDependencyLoader] - private void load(OsuColour colours) + [BackgroundDependencyLoader(true)] + private void load(OverlayColourProvider overlayColourProvider, OsuColour colours) { RelativeSizeAxes = Axes.Both; @@ -54,7 +55,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 InternalChild = new Box { - Colour = colours.GreySeaFoamDarker, + Colour = overlayColourProvider?.Background5 ?? colours.GreySeaFoamDarker, RelativeSizeAxes = Axes.Both, }; } From e2ea98693f8761ba0fc0fd76f786963d434eba5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 May 2022 21:33:15 +0900 Subject: [PATCH 1129/1276] Tidy up buttons and text --- .../FirstRunSetup/ScreenImportFromStable.cs | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index acc0fdbb7c..f867adff79 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -28,6 +28,8 @@ namespace osu.Game.Overlays.FirstRunSetup [LocalisableDescription(typeof(FirstRunSetupOverlayStrings), nameof(FirstRunSetupOverlayStrings.ImportTitle))] public class ScreenImportFromStable : FirstRunSetupScreen { + private static readonly Vector2 button_size = new Vector2(400, 50); + private ProgressRoundedButton importButton = null!; private RoundedButton locateStableButton = null!; @@ -46,8 +48,6 @@ namespace osu.Game.Overlays.FirstRunSetup [BackgroundDependencyLoader(permitNulls: true)] private void load() { - Vector2 buttonSize = new Vector2(400, 50); - Content.Children = new Drawable[] { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) @@ -63,14 +63,14 @@ namespace osu.Game.Overlays.FirstRunSetup Colour = OverlayColourProvider.Content2, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + TextAnchor = Anchor.Centre, }, locateStableButton = new RoundedButton { - Size = buttonSize, + Size = button_size, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - BackgroundColour = colours.Blue3, - Text = "Locate osu!(stable) install", + Text = "Change location", Action = locateStable, }, new ImportCheckbox("Beatmaps", StableContent.Beatmaps), @@ -79,10 +79,9 @@ namespace osu.Game.Overlays.FirstRunSetup new ImportCheckbox("Collections", StableContent.Collections), importButton = new ProgressRoundedButton { - Size = buttonSize, + Size = button_size, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - BackgroundColour = colours.Blue3, Text = FirstRunSetupOverlayStrings.ImportContentFromStable, Action = runImport }, @@ -112,7 +111,10 @@ namespace osu.Game.Overlays.FirstRunSetup { foreach (var c in contentCheckboxes) c.Current.Disabled = true; + currentStablePath.FadeColour(colours.Red1, 500, Easing.OutQuint); currentStablePath.Text = "No installation found"; + + importButton.Enabled.Value = false; return; } @@ -122,8 +124,10 @@ namespace osu.Game.Overlays.FirstRunSetup c.UpdateCount(); } - currentStablePath.Text = storage.GetFullPath(string.Empty); + currentStablePath.FadeColour(OverlayColourProvider.Content2); + currentStablePath.Text = $"Found installation: {storage.GetFullPath(string.Empty)}"; stablePathUpdateCancellation = new CancellationTokenSource(); + importButton.Enabled.Value = true; } private void runImport() @@ -138,13 +142,12 @@ namespace osu.Game.Overlays.FirstRunSetup legacyImportManager.ImportFromStableAsync(importableContent, false).ContinueWith(t => Schedule(() => { - locateStableButton.Enabled.Value = true; - if (t.IsCompletedSuccessfully) importButton.Complete(); else { importButton.Enabled.Value = true; + locateStableButton.Enabled.Value = true; importButton.Abort(); } })); @@ -253,9 +256,11 @@ namespace osu.Game.Overlays.FirstRunSetup { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Width = 300, + RelativeSizeAxes = Axes.X, + Width = 0.45f, + Height = button_size.Y, Margin = new MarginPadding(10), - Colour = colours.Pink2, + BackgroundColour = colours.Pink2, Text = CommonStrings.ButtonsCancel, Action = this.Exit }, @@ -263,7 +268,9 @@ namespace osu.Game.Overlays.FirstRunSetup { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Width = 300, + RelativeSizeAxes = Axes.X, + Width = 0.45f, + Height = button_size.Y, Margin = new MarginPadding(10), Text = MaintenanceSettingsStrings.SelectDirectory, Action = () => From 8112416335429ee4d18475887d88c40558fd6c1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 15:25:45 +0900 Subject: [PATCH 1130/1276] Assert that downloads are queued early enough to work --- osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index df0a69cb25..84903d381a 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -3,9 +3,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Database; using osu.Game.Online; @@ -89,6 +91,8 @@ namespace osu.Game.Beatmaps.Drawables private void queueDownloads(string[] sourceFilenames, int? limit = null) { + Debug.Assert(LoadState == LoadState.NotLoaded); + try { // Matches osu-stable, in order to provide new users with roughly the same randomised selection of bundled beatmaps. From 3ff0399281f2f2ca0b5356aa08c48133623962db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 16:32:31 +0900 Subject: [PATCH 1131/1276] Split out `LabelledTextBoxWithPopover` for reuse --- .../Edit/Setup/FileChooserLabelledTextBox.cs | 37 +------------- .../Edit/Setup/LabelledTextBoxWithPopover.cs | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+), 35 deletions(-) create mode 100644 osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs diff --git a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs index d1e35ae20d..aae19396db 100644 --- a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs +++ b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs @@ -11,11 +11,8 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; using osu.Game.Database; -using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osuTK; @@ -24,7 +21,7 @@ namespace osu.Game.Screens.Edit.Setup /// /// A labelled textbox which reveals an inline file chooser when clicked. /// - internal class FileChooserLabelledTextBox : LabelledTextBox, ICanAcceptFiles, IHasPopover + internal class FileChooserLabelledTextBox : LabelledTextBoxWithPopover, ICanAcceptFiles { private readonly string[] handledExtensions; @@ -40,16 +37,6 @@ namespace osu.Game.Screens.Edit.Setup this.handledExtensions = handledExtensions; } - protected override OsuTextBox CreateTextBox() => - new FileChooserOsuTextBox - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - CornerRadius = CORNER_RADIUS, - OnFocused = this.ShowPopover - }; - protected override void LoadComplete() { base.LoadComplete(); @@ -81,27 +68,7 @@ namespace osu.Game.Screens.Edit.Setup game.UnregisterImportHandler(this); } - internal class FileChooserOsuTextBox : OsuTextBox - { - public Action OnFocused; - - protected override bool OnDragStart(DragStartEvent e) - { - // This text box is intended to be "read only" without actually specifying that. - // As such we don't want to allow the user to select its content with a drag. - return false; - } - - protected override void OnFocus(FocusEvent e) - { - OnFocused?.Invoke(); - base.OnFocus(e); - - GetContainingInputManager().TriggerFocusContention(this); - } - } - - public Popover GetPopover() => new FileChooserPopover(handledExtensions, currentFile); + public override Popover GetPopover() => new FileChooserPopover(handledExtensions, currentFile); private class FileChooserPopover : OsuPopover { diff --git a/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs b/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs new file mode 100644 index 0000000000..1136675347 --- /dev/null +++ b/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; + +namespace osu.Game.Screens.Edit.Setup +{ + internal abstract class LabelledTextBoxWithPopover : LabelledTextBox, IHasPopover + { + public abstract Popover GetPopover(); + + protected override OsuTextBox CreateTextBox() => + new PopoverTextBox + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + CornerRadius = CORNER_RADIUS, + OnFocused = this.ShowPopover + }; + + internal class PopoverTextBox : OsuTextBox + { + public Action OnFocused; + + protected override bool OnDragStart(DragStartEvent e) + { + // This text box is intended to be "read only" without actually specifying that. + // As such we don't want to allow the user to select its content with a drag. + return false; + } + + protected override void OnFocus(FocusEvent e) + { + OnFocused?.Invoke(); + base.OnFocus(e); + + GetContainingInputManager().TriggerFocusContention(this); + } + } + } +} From adc7b61240c55d9b2bec397f1a05a1427cf56558 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 16:33:02 +0900 Subject: [PATCH 1132/1276] Switch directory selector to use a semi-shared popover implementation --- .../FirstRunSetup/ScreenImportFromStable.cs | 226 +++++++----------- osu.Game/Overlays/FirstRunSetupOverlay.cs | 3 +- 2 files changed, 86 insertions(+), 143 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index f867adff79..8b7550e301 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -3,25 +3,27 @@ #nullable enable +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; -using osu.Framework.Screens; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterfaceV2; -using osu.Game.IO; using osu.Game.Localisation; using osu.Game.Overlays.Settings; +using osu.Game.Screens.Edit.Setup; using osuTK; -using CommonStrings = osu.Game.Resources.Localisation.Web.CommonStrings; namespace osu.Game.Overlays.FirstRunSetup { @@ -31,18 +33,14 @@ namespace osu.Game.Overlays.FirstRunSetup private static readonly Vector2 button_size = new Vector2(400, 50); private ProgressRoundedButton importButton = null!; - private RoundedButton locateStableButton = null!; - - private OsuTextFlowContainer currentStablePath = null!; - - [Resolved] - private OsuColour colours { get; set; } = null!; [Resolved] private LegacyImportManager legacyImportManager { get; set; } = null!; private CancellationTokenSource? stablePathUpdateCancellation; + private StableLocatorLabelledTextBox stableLocatorTextBox = null!; + private IEnumerable contentCheckboxes => Content.Children.OfType(); [BackgroundDependencyLoader(permitNulls: true)] @@ -58,20 +56,10 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, - currentStablePath = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: HEADER_FONT_SIZE, weight: FontWeight.SemiBold)) + stableLocatorTextBox = new StableLocatorLabelledTextBox { - Colour = OverlayColourProvider.Content2, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - TextAnchor = Anchor.Centre, - }, - locateStableButton = new RoundedButton - { - Size = button_size, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = "Change location", - Action = locateStable, + Label = "previous osu! install", + PlaceholderText = "Click to locate a previous osu! install" }, new ImportCheckbox("Beatmaps", StableContent.Beatmaps), new ImportCheckbox("Scores", StableContent.Scores), @@ -87,18 +75,7 @@ namespace osu.Game.Overlays.FirstRunSetup }, }; - updateStablePath(); - } - - private void locateStable() => this.Push(new LocateStableScreen()); - - public override void OnResuming(ScreenTransitionEvent e) - { - base.OnResuming(e); - - if (e.Last is LocateStableScreen) - // stable storage may have changed. - Schedule(updateStablePath); + stableLocatorTextBox.Current.BindValueChanged(_ => updateStablePath(), true); } private void updateStablePath() @@ -111,9 +88,8 @@ namespace osu.Game.Overlays.FirstRunSetup { foreach (var c in contentCheckboxes) c.Current.Disabled = true; - currentStablePath.FadeColour(colours.Red1, 500, Easing.OutQuint); - currentStablePath.Text = "No installation found"; + stableLocatorTextBox.Current.Value = string.Empty; importButton.Enabled.Value = false; return; } @@ -124,8 +100,8 @@ namespace osu.Game.Overlays.FirstRunSetup c.UpdateCount(); } - currentStablePath.FadeColour(OverlayColourProvider.Content2); - currentStablePath.Text = $"Found installation: {storage.GetFullPath(string.Empty)}"; + stableLocatorTextBox.Current.Value = storage.GetFullPath(string.Empty); + stablePathUpdateCancellation = new CancellationTokenSource(); importButton.Enabled.Value = true; } @@ -133,7 +109,7 @@ namespace osu.Game.Overlays.FirstRunSetup private void runImport() { importButton.Enabled.Value = false; - locateStableButton.Enabled.Value = false; + stableLocatorTextBox.Current.Disabled = true; StableContent importableContent = 0; @@ -147,7 +123,7 @@ namespace osu.Game.Overlays.FirstRunSetup else { importButton.Enabled.Value = true; - locateStableButton.Enabled.Value = true; + stableLocatorTextBox.Current.Disabled = false; importButton.Abort(); } })); @@ -192,119 +168,85 @@ namespace osu.Game.Overlays.FirstRunSetup } } - private class LocateStableScreen : FirstRunSetupScreen + internal class StableLocatorLabelledTextBox : LabelledTextBoxWithPopover, ICanAcceptFiles { - private RoundedButton selectionButton = null!; - - private OsuDirectorySelector directorySelector = null!; - - protected bool IsValidDirectory(DirectoryInfo? info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; - - public LocalisableString HeaderText => "Please select your osu!stable install location"; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - // Don't want the scroll content provided by `FirstRunSetupScreen` so we don't use `Content`. - InternalChild = new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = CONTENT_PADDING }, - Children = new Drawable[] - { - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] - { - new OsuTextFlowContainer(cp => - { - cp.Font = OsuFont.Default.With(size: 24); - }) - { - Text = HeaderText, - TextAnchor = Anchor.TopCentre, - Margin = new MarginPadding(10), - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - } - }, - new Drawable[] - { - directorySelector = new OsuDirectorySelector - { - RelativeSizeAxes = Axes.Both, - } - }, - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new RoundedButton - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - Width = 0.45f, - Height = button_size.Y, - Margin = new MarginPadding(10), - BackgroundColour = colours.Pink2, - Text = CommonStrings.ButtonsCancel, - Action = this.Exit - }, - selectionButton = new RoundedButton - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.X, - Width = 0.45f, - Height = button_size.Y, - Margin = new MarginPadding(10), - Text = MaintenanceSettingsStrings.SelectDirectory, - Action = () => - { - legacyImportManager.UpdateStorage(directorySelector.CurrentPath.Value.FullName); - this.Exit(); - } - }, - } - }, - } - } - } - } - }; - } - [Resolved] private LegacyImportManager legacyImportManager { get; set; } = null!; + // TODO: test + public IEnumerable HandledExtensions { get; } = new[] { string.Empty }; + + private readonly Bindable currentDirectory = new Bindable(); + + [Resolved] + private OsuGameBase game { get; set; } = null!; + protected override void LoadComplete() { - if (legacyImportManager.GetCurrentStableStorage() is StableStorage storage) - directorySelector.CurrentPath.Value = new DirectoryInfo(storage.GetFullPath(string.Empty)); - - directorySelector.CurrentPath.BindValueChanged(e => selectionButton.Enabled.Value = e.NewValue != null && IsValidDirectory(e.NewValue), true); base.LoadComplete(); + + game.RegisterImportHandler(this); + + currentDirectory.BindValueChanged(onDirectorySelected); + + string? fullPath = legacyImportManager.GetCurrentStableStorage()?.GetFullPath(string.Empty); + if (fullPath != null) + currentDirectory.Value = new DirectoryInfo(fullPath); } - public override void OnSuspending(ScreenTransitionEvent e) + private void onDirectorySelected(ValueChangedEvent directory) { - base.OnSuspending(e); + if (directory.NewValue == null || directory.OldValue == null) + { + Current.Value = string.Empty; + return; + } - this.FadeOut(250); + // DirectorySelectors can trigger a noop value changed, but `DirectoryInfo` equality doesn't catch this. + if (directory.OldValue.FullName == directory.NewValue.FullName) + return; + + if (directory.NewValue?.GetFiles(@"osu!.*.cfg").Any() ?? false) + { + this.HidePopover(); + + string path = directory.NewValue.FullName; + + legacyImportManager.UpdateStorage(path); + Current.Value = path; + } + } + + Task ICanAcceptFiles.Import(params string[] paths) + { + Schedule(() => currentDirectory.Value = new DirectoryInfo(paths.First())); + return Task.CompletedTask; + } + + Task ICanAcceptFiles.Import(params ImportTask[] tasks) => throw new NotImplementedException(); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + game.UnregisterImportHandler(this); + } + + public override Popover GetPopover() => new DirectoryChooserPopover(currentDirectory); + + private class DirectoryChooserPopover : OsuPopover + { + public DirectoryChooserPopover(Bindable currentDirectory) + { + Child = new Container + { + Size = new Vector2(600, 400), + Child = new OsuDirectorySelector(currentDirectory.Value?.FullName) + { + RelativeSizeAxes = Axes.Both, + CurrentPath = { BindTarget = currentDirectory } + }, + }; + } } } } diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index dae2815e98..890f17b013 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; @@ -88,7 +89,7 @@ namespace osu.Game.Overlays MainAreaContent.AddRange(new Drawable[] { - content = new Container + content = new PopoverContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 30fdbc3de070fffecdf6e7666de15b4e6e264b42 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 16:55:41 +0900 Subject: [PATCH 1133/1276] Add safety against calling `GetStableImportPaths` when path doesn't exist --- osu.Game/Database/LegacyModelImporter.cs | 10 ++++++++-- osu.Game/Database/LegacyScoreImporter.cs | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/LegacyModelImporter.cs b/osu.Game/Database/LegacyModelImporter.cs index a6e0f5bf8b..13a9025ece 100644 --- a/osu.Game/Database/LegacyModelImporter.cs +++ b/osu.Game/Database/LegacyModelImporter.cs @@ -24,8 +24,14 @@ namespace osu.Game.Database /// /// Select paths to import from stable where all paths should be absolute. Default implementation iterates all directories in . /// - protected virtual IEnumerable GetStableImportPaths(Storage storage) => storage.GetDirectories(ImportFromStablePath) - .Select(path => storage.GetFullPath(path)); + protected virtual IEnumerable GetStableImportPaths(Storage storage) + { + if (!storage.ExistsDirectory(ImportFromStablePath)) + return Enumerable.Empty(); + + return storage.GetDirectories(ImportFromStablePath) + .Select(path => storage.GetFullPath(path)); + } protected readonly IModelImporter Importer; diff --git a/osu.Game/Database/LegacyScoreImporter.cs b/osu.Game/Database/LegacyScoreImporter.cs index 48445b7bdb..131b4ffb0e 100644 --- a/osu.Game/Database/LegacyScoreImporter.cs +++ b/osu.Game/Database/LegacyScoreImporter.cs @@ -15,8 +15,14 @@ namespace osu.Game.Database protected override string ImportFromStablePath => Path.Combine("Data", "r"); protected override IEnumerable GetStableImportPaths(Storage storage) - => storage.GetFiles(ImportFromStablePath).Where(p => Importer.HandledExtensions.Any(ext => Path.GetExtension(p)?.Equals(ext, StringComparison.OrdinalIgnoreCase) ?? false)) - .Select(path => storage.GetFullPath(path)); + { + if (!storage.ExistsDirectory(ImportFromStablePath)) + return Enumerable.Empty(); + + return storage.GetFiles(ImportFromStablePath) + .Where(p => Importer.HandledExtensions.Any(ext => Path.GetExtension(p)?.Equals(ext, StringComparison.OrdinalIgnoreCase) ?? false)) + .Select(path => storage.GetFullPath(path)); + } public LegacyScoreImporter(IModelImporter importer) : base(importer) From 03d3900a02ede396fe0fa0eb095b2579586fde66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 17:12:10 +0900 Subject: [PATCH 1134/1276] Fix incorrect default state of checkboxes --- osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index 8b7550e301..7791c3614e 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -146,6 +146,7 @@ namespace osu.Game.Overlays.FirstRunSetup StableContent = stableContent; + Current.Default = true; Current.Value = true; LabelText = title; From 17e0105c2cff5b17aefd5f9b52c1dd29d6b9482c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 17:26:26 +0900 Subject: [PATCH 1135/1276] Fix interaction with popover when textbox is disabled --- osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs b/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs index 1136675347..799311dd2d 100644 --- a/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs +++ b/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs @@ -39,6 +39,9 @@ namespace osu.Game.Screens.Edit.Setup protected override void OnFocus(FocusEvent e) { + if (Current.Disabled) + return; + OnFocused?.Invoke(); base.OnFocus(e); From c0de1f96ff2158a293fc546a11b729d84ea4df77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 17:26:44 +0900 Subject: [PATCH 1136/1276] Tidy up interaction toggling and add progress message --- .../FirstRunSetupOverlayStrings.cs | 4 +- .../FirstRunSetup/ScreenImportFromStable.cs | 42 ++++++++++++------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs index 6b5ca7534d..181e35e4ce 100644 --- a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -80,9 +80,9 @@ We recommend you give the new defaults a try, but if you'd like to have things f public static LocalisableString ImportTitle => new TranslatableString(getKey(@"import_title"), @"Import"); /// - /// "Import content from stable" + /// "Import content from previous version" /// - public static LocalisableString ImportContentFromStable => new TranslatableString(getKey(@"import_content_from_stable"), @"Import content from osu!(stable)"); + public static LocalisableString ImportContentFromPreviousVersion => new TranslatableString(getKey(@"import_content_from_previous_version"), @"Import content from previous version"); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index 7791c3614e..3b3cf4caf7 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -34,11 +34,11 @@ namespace osu.Game.Overlays.FirstRunSetup private ProgressRoundedButton importButton = null!; + private OsuTextFlowContainer progressText = null!; + [Resolved] private LegacyImportManager legacyImportManager { get; set; } = null!; - private CancellationTokenSource? stablePathUpdateCancellation; - private StableLocatorLabelledTextBox stableLocatorTextBox = null!; private IEnumerable contentCheckboxes => Content.Children.OfType(); @@ -70,9 +70,18 @@ namespace osu.Game.Overlays.FirstRunSetup Size = button_size, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = FirstRunSetupOverlayStrings.ImportContentFromStable, + Text = FirstRunSetupOverlayStrings.ImportContentFromPreviousVersion, Action = runImport }, + progressText = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) + { + Colour = OverlayColourProvider.Content1, + Text = + "Your import will continue in the background. Check on its progress in the notifications sidebar!", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Alpha = 0, + }, }; stableLocatorTextBox.Current.BindValueChanged(_ => updateStablePath(), true); @@ -80,17 +89,14 @@ namespace osu.Game.Overlays.FirstRunSetup private void updateStablePath() { - stablePathUpdateCancellation?.Cancel(); - var storage = legacyImportManager.GetCurrentStableStorage(); if (storage == null) { - foreach (var c in contentCheckboxes) - c.Current.Disabled = true; + allowInteraction(false); + stableLocatorTextBox.Current.Disabled = false; stableLocatorTextBox.Current.Value = string.Empty; - importButton.Enabled.Value = false; return; } @@ -100,16 +106,15 @@ namespace osu.Game.Overlays.FirstRunSetup c.UpdateCount(); } + allowInteraction(true); stableLocatorTextBox.Current.Value = storage.GetFullPath(string.Empty); - - stablePathUpdateCancellation = new CancellationTokenSource(); importButton.Enabled.Value = true; } private void runImport() { - importButton.Enabled.Value = false; - stableLocatorTextBox.Current.Disabled = true; + allowInteraction(false); + progressText.FadeIn(1000, Easing.OutQuint); StableContent importableContent = 0; @@ -118,17 +123,26 @@ namespace osu.Game.Overlays.FirstRunSetup legacyImportManager.ImportFromStableAsync(importableContent, false).ContinueWith(t => Schedule(() => { + progressText.FadeOut(500, Easing.OutQuint); + if (t.IsCompletedSuccessfully) importButton.Complete(); else { - importButton.Enabled.Value = true; - stableLocatorTextBox.Current.Disabled = false; + allowInteraction(true); importButton.Abort(); } })); } + private void allowInteraction(bool allow) + { + importButton.Enabled.Value = allow; + stableLocatorTextBox.Current.Disabled = !allow; + foreach (var c in contentCheckboxes) + c.Current.Disabled = !allow; + } + private class ImportCheckbox : SettingsCheckbox { public readonly StableContent StableContent; From 83e781d5a1ef9da5e3c0d6f071ca33c44df20279 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 17:42:46 +0900 Subject: [PATCH 1137/1276] Allow localisation of `PlaceholderText` --- osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs | 3 ++- .../Edit/Compose/Components/Timeline/SamplePointPiece.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs index fd64cc2056..82b9fe559f 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterfaceV2 @@ -25,7 +26,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 set => Component.ReadOnly = value; } - public string PlaceholderText + public LocalisableString PlaceholderText { set => Component.PlaceholderText = value; } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs index ab21a83c43..9abea73f6b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs @@ -154,7 +154,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void updateBankPlaceholderText(IEnumerable objects) { string? commonBank = getCommonBank(objects.Select(h => h.SampleControlPoint).ToArray()); - bank.PlaceholderText = string.IsNullOrEmpty(commonBank) ? "(multiple)" : null; + bank.PlaceholderText = string.IsNullOrEmpty(commonBank) ? "(multiple)" : string.Empty; } private void updateVolumeFor(IEnumerable objects, int? newVolume) From f4e0ad8c4c87c41454ce28033f150569fa01174a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 17:51:28 +0900 Subject: [PATCH 1138/1276] Fix drag drop of osu! folder not working as expected --- osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index 3b3cf4caf7..b145dadd39 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -211,14 +211,14 @@ namespace osu.Game.Overlays.FirstRunSetup private void onDirectorySelected(ValueChangedEvent directory) { - if (directory.NewValue == null || directory.OldValue == null) + if (directory.NewValue == null) { Current.Value = string.Empty; return; } // DirectorySelectors can trigger a noop value changed, but `DirectoryInfo` equality doesn't catch this. - if (directory.OldValue.FullName == directory.NewValue.FullName) + if (directory.OldValue?.FullName == directory.NewValue.FullName) return; if (directory.NewValue?.GetFiles(@"osu!.*.cfg").Any() ?? false) From bf00b062ad05b000f5cb955881494c6da57ece9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 17:58:24 +0900 Subject: [PATCH 1139/1276] Add full localisation of import beatmaps screen --- ...RunOverlayImportFromStableScreenStrings.cs | 56 +++++++++++++++++++ .../FirstRunSetupOverlayStrings.cs | 10 ---- .../FirstRunSetup/ScreenImportFromStable.cs | 20 +++---- 3 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs diff --git a/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs b/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs new file mode 100644 index 0000000000..deac7d8628 --- /dev/null +++ b/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs @@ -0,0 +1,56 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class FirstRunOverlayImportFromStableScreenStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.ScreenImportFromStable"; + + /// + /// "Import" + /// + public static LocalisableString Header => new TranslatableString(getKey(@"header"), @"Import"); + + /// + /// "If you have an installation of a previous osu! version, you can choose to migrate your existing content. Note that this will create a copy, and not affect your existing installation." + /// + public static LocalisableString Description => new TranslatableString(getKey(@"description"), + @"If you have an installation of a previous osu! version, you can choose to migrate your existing content. Note that this will create a copy, and not affect your existing installation."); + + /// + /// "previous osu! install" + /// + public static LocalisableString LocateDirectoryLabel => new TranslatableString(getKey(@"locate_directory_label"), @"previous osu! install"); + + /// + /// "Click to locate a previous osu! install" + /// + public static LocalisableString LocateDirectoryPlaceholder => new TranslatableString(getKey(@"locate_directory_placeholder"), @"Click to locate a previous osu! install"); + + /// + /// "Import content from previous version" + /// + public static LocalisableString ImportButton => new TranslatableString(getKey(@"import_button"), @"Import content from previous version"); + + /// + /// "Your import will continue in the background. Check on its progress in the notifications sidebar!" + /// + public static LocalisableString ImportInProgress => + new TranslatableString(getKey(@"import_in_progress"), @"Your import will continue in the background. Check on its progress in the notifications sidebar!"); + + /// + /// "calculating..." + /// + public static LocalisableString Calculating => new TranslatableString(getKey(@"calculating"), @"calculating..."); + + /// + /// "{0} items" + /// + public static LocalisableString Items(int arg0) => new TranslatableString(getKey(@"items"), @"{0} item(s)", arg0); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs index 181e35e4ce..91b427e2ca 100644 --- a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -74,16 +74,6 @@ We recommend you give the new defaults a try, but if you'd like to have things f ///
public static LocalisableString ClassicDefaults => new TranslatableString(getKey(@"classic_defaults"), @"Classic defaults"); - /// - /// "Welcome" - /// - public static LocalisableString ImportTitle => new TranslatableString(getKey(@"import_title"), @"Import"); - - /// - /// "Import content from previous version" - /// - public static LocalisableString ImportContentFromPreviousVersion => new TranslatableString(getKey(@"import_content_from_previous_version"), @"Import content from previous version"); - private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index b145dadd39..d5ef879479 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -27,7 +27,7 @@ using osuTK; namespace osu.Game.Overlays.FirstRunSetup { - [LocalisableDescription(typeof(FirstRunSetupOverlayStrings), nameof(FirstRunSetupOverlayStrings.ImportTitle))] + [LocalisableDescription(typeof(FirstRunOverlayImportFromStableScreenStrings), nameof(FirstRunOverlayImportFromStableScreenStrings.Header))] public class ScreenImportFromStable : FirstRunSetupScreen { private static readonly Vector2 button_size = new Vector2(400, 50); @@ -51,15 +51,14 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, - Text = - "If you have an installation of a previous osu! version, you can choose to migrate your existing content. Note that this will create a copy, and not affect your existing installation.", + Text = FirstRunOverlayImportFromStableScreenStrings.Description, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, stableLocatorTextBox = new StableLocatorLabelledTextBox { - Label = "previous osu! install", - PlaceholderText = "Click to locate a previous osu! install" + Label = FirstRunOverlayImportFromStableScreenStrings.LocateDirectoryLabel, + PlaceholderText = FirstRunOverlayImportFromStableScreenStrings.LocateDirectoryPlaceholder }, new ImportCheckbox("Beatmaps", StableContent.Beatmaps), new ImportCheckbox("Scores", StableContent.Scores), @@ -70,14 +69,13 @@ namespace osu.Game.Overlays.FirstRunSetup Size = button_size, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = FirstRunSetupOverlayStrings.ImportContentFromPreviousVersion, + Text = FirstRunOverlayImportFromStableScreenStrings.ImportButton, Action = runImport }, progressText = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, - Text = - "Your import will continue in the background. Check on its progress in the notifications sidebar!", + Text = FirstRunOverlayImportFromStableScreenStrings.ImportInProgress, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Alpha = 0, @@ -168,7 +166,7 @@ namespace osu.Game.Overlays.FirstRunSetup public void UpdateCount() { - LabelText = LocalisableString.Interpolate($"{title} (calculating...)"); + LabelText = LocalisableString.Interpolate($"{title} ({FirstRunOverlayImportFromStableScreenStrings.Calculating})"); countUpdateCancellation?.Cancel(); countUpdateCancellation = new CancellationTokenSource(); @@ -178,7 +176,9 @@ namespace osu.Game.Overlays.FirstRunSetup if (task.IsCanceled) return; - LabelText = LocalisableString.Interpolate($"{title} ({task.GetResultSafely()} items)"); + int count = task.GetResultSafely(); + + LabelText = LocalisableString.Interpolate($"{title} ({FirstRunOverlayImportFromStableScreenStrings.Items(count)})"); })); } } From 1b7ec1be26e806796258298641cba65a9d043370 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 18:15:14 +0900 Subject: [PATCH 1140/1276] Add basic test coverage of new screen --- ...TestSceneFirstRunScreenImportFromStable.cs | 48 +++++++++++++++++++ osu.Game/Database/LegacyImportManager.cs | 8 ++-- 2 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenImportFromStable.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenImportFromStable.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenImportFromStable.cs new file mode 100644 index 0000000000..081b240795 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenImportFromStable.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading; +using System.Threading.Tasks; +using Moq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Screens; +using osu.Framework.Utils; +using osu.Game.Database; +using osu.Game.Overlays; +using osu.Game.Overlays.FirstRunSetup; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneFirstRunScreenImportFromStable : OsuManualInputManagerTestScene + { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + private readonly Mock legacyImportManager = new Mock(); + + [BackgroundDependencyLoader] + private void load() + { + legacyImportManager.Setup(m => m.GetImportCount(It.IsAny(), It.IsAny())).Returns(() => Task.FromResult(RNG.Next(0, 256))); + + Dependencies.CacheAs(legacyImportManager.Object); + } + + public TestSceneFirstRunScreenImportFromStable() + { + AddStep("load screen", () => + { + Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new ScreenStack(new ScreenImportFromStable()) + } + }; + }); + } + } +} diff --git a/osu.Game/Database/LegacyImportManager.cs b/osu.Game/Database/LegacyImportManager.cs index 35b1d05fa1..af9db1b6ec 100644 --- a/osu.Game/Database/LegacyImportManager.cs +++ b/osu.Game/Database/LegacyImportManager.cs @@ -37,13 +37,13 @@ namespace osu.Game.Database [Resolved] private CollectionManager collections { get; set; } - [Resolved] + [Resolved(canBeNull: true)] private OsuGame game { get; set; } [Resolved] private IDialogOverlay dialogOverlay { get; set; } - [Resolved(CanBeNull = true)] + [Resolved(canBeNull: true)] private DesktopGameHost desktopGameHost { get; set; } private StableStorage cachedStorage; @@ -52,7 +52,7 @@ namespace osu.Game.Database public void UpdateStorage(string stablePath) => cachedStorage = new StableStorage(stablePath, desktopGameHost); - public async Task GetImportCount(StableContent content, CancellationToken cancellationToken) + public virtual async Task GetImportCount(StableContent content, CancellationToken cancellationToken) { var stableStorage = GetCurrentStableStorage(); @@ -120,7 +120,7 @@ namespace osu.Game.Database if (cachedStorage != null) return cachedStorage; - var stableStorage = game.GetStorageForStableInstall(); + var stableStorage = game?.GetStorageForStableInstall(); if (stableStorage != null) return cachedStorage = stableStorage; From 062ffe64acf700d295d22585af9a8d1c95362684 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 May 2022 18:21:19 +0900 Subject: [PATCH 1141/1276] Remove delay on pop in --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 8d72855217..f1a998bd3c 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -428,7 +428,6 @@ namespace osu.Game.Overlays.Mods base.PopIn(); multiplierDisplay? - .Delay(fade_in_duration / 5) .FadeIn(fade_in_duration, Easing.OutQuint) .MoveToY(0, fade_in_duration, Easing.OutQuint); From 5ad969246702e79eef2b10f155891fa4b61ee3ce Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 17 May 2022 14:59:18 +0900 Subject: [PATCH 1142/1276] Adjust minimum health targets to match osu-stable --- osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index dfeb6b4788..e7bb6d79f8 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -27,17 +27,17 @@ namespace osu.Game.Rulesets.Scoring /// /// The minimum health target at an HP drain rate of 0. /// - private const double min_health_target = 0.95; + private const double min_health_target = 0.99; /// /// The minimum health target at an HP drain rate of 5. /// - private const double mid_health_target = 0.70; + private const double mid_health_target = 0.9; /// /// The minimum health target at an HP drain rate of 10. /// - private const double max_health_target = 0.30; + private const double max_health_target = 0.4; private IBeatmap beatmap; From 932442e2422db30333071d6a441b993d4f50667b Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 17 May 2022 18:52:19 +0100 Subject: [PATCH 1143/1276] Use `null` for empty grid cells in `ChannelListItem` --- .../Chat/ChannelList/ChannelListItem.cs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index dfb0b6d781..da1cfaf617 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Chat.ChannelList private Box hoverBox = null!; private Box selectBox = null!; private OsuSpriteText text = null!; - private Drawable close = null!; + private Drawable? close; [Resolved] private Bindable selectedChannel { get; set; } = null!; @@ -119,9 +119,7 @@ namespace osu.Game.Overlays.Chat.ChannelList protected override bool OnHover(HoverEvent e) { hoverBox.FadeIn(300, Easing.OutQuint); - - if (!isSelector) - close.FadeIn(300, Easing.OutQuint); + close?.FadeIn(300, Easing.OutQuint); return base.OnHover(e); } @@ -129,17 +127,15 @@ namespace osu.Game.Overlays.Chat.ChannelList protected override void OnHoverLost(HoverLostEvent e) { hoverBox.FadeOut(200, Easing.OutQuint); - - if (!isSelector) - close.FadeOut(200, Easing.OutQuint); + close?.FadeOut(200, Easing.OutQuint); base.OnHoverLost(e); } - private Drawable createIcon() + private UpdateableAvatar? createIcon() { if (Channel.Type != ChannelType.PM) - return Drawable.Empty(); + return null; return new UpdateableAvatar(Channel.Users.First(), isInteractive: false) { @@ -152,10 +148,10 @@ namespace osu.Game.Overlays.Chat.ChannelList }; } - private Drawable createMentionPill() + private ChannelListItemMentionPill? createMentionPill() { if (isSelector) - return Drawable.Empty(); + return null; return new ChannelListItemMentionPill { @@ -166,10 +162,10 @@ namespace osu.Game.Overlays.Chat.ChannelList }; } - private Drawable createCloseButton() + private ChannelListItemCloseButton? createCloseButton() { if (isSelector) - return Drawable.Empty(); + return null; return new ChannelListItemCloseButton { From 4015042e369a6d0900cca61beb0a13ee795d1639 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 18 May 2022 01:35:39 +0100 Subject: [PATCH 1144/1276] Ensure close button component is not stored as a `Drawable` --- osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index da1cfaf617..eb2bdfdf8a 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Chat.ChannelList private Box hoverBox = null!; private Box selectBox = null!; private OsuSpriteText text = null!; - private Drawable? close; + private ChannelListItemCloseButton? close; [Resolved] private Bindable selectedChannel { get; set; } = null!; @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Chat.ChannelList }, Content = new[] { - new[] + new Drawable?[] { createIcon(), text = new OsuSpriteText From dd4b11c593f7869295e246241e388c701e641f99 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 18 May 2022 01:47:23 +0100 Subject: [PATCH 1145/1276] Re-add exception handling on PM message request failure --- osu.Game/Online/Chat/ChannelManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 9524e8c1a7..9e85067f15 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -197,6 +197,7 @@ namespace osu.Game.Online.Chat createNewPrivateMessageRequest.Failure += exception => { + handlePostException(exception); target.ReplaceMessage(message, null); dequeueAndRun(); }; From 49340888c09cd27e9e030648404947474d07510f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 18 May 2022 10:56:42 +0900 Subject: [PATCH 1146/1276] Fix compile errors --- osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs | 2 +- osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 377873f64a..97463b7466 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Settings .OfType() .OfType() .Where(f => !(f is IHasFilterableChildren)) - .All(f => f.FilterTerms.Any(t => t.Contains("scaling"))) + .All(f => f.FilterTerms.Any(t => t.ToString().Contains("scaling"))) )); AddAssert("ensure section is current", () => settings.CurrentSection.Value is GraphicsSection); diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs index 09e5bc849e..84a06d97df 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePlaylistOverlay.cs @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("results filtered correctly", () => playlistOverlay.ChildrenOfType() .Where(item => item.MatchingFilter) - .All(item => item.FilterTerms.Any(term => term.Contains("10")))); + .All(item => item.FilterTerms.Any(term => term.ToString().Contains("10")))); } } } From 3f71224dfc26a7cd87c6737bac79f86af84de2b2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 18 May 2022 14:05:21 +0900 Subject: [PATCH 1147/1276] Package .json files in nupkg output --- osu.Desktop/osu.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Desktop/osu.nuspec b/osu.Desktop/osu.nuspec index dc1ec17e2c..db58c325bd 100644 --- a/osu.Desktop/osu.nuspec +++ b/osu.Desktop/osu.nuspec @@ -18,5 +18,6 @@ + From fe49a7e6782e66dedbe5805192c6930589aa6522 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 18 May 2022 17:10:19 +0900 Subject: [PATCH 1148/1276] Add failing tests --- .../Formats/LegacyBeatmapDecoderTest.cs | 35 +++++++++++++++++++ .../Resources/adjacent-catmull-segments.osu | 2 ++ 2 files changed, 37 insertions(+) create mode 100644 osu.Game.Tests/Resources/adjacent-catmull-segments.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 468cb7683c..89baaf228d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -863,5 +863,40 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(decoded.Difficulty.OverallDifficulty, Is.EqualTo(1)); } } + + [Test] + public void TestLegacyAdjacentImplicitCatmullSegmentsAreMerged() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("adjacent-catmull-segments.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var decoded = decoder.Decode(stream); + var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints; + + Assert.That(controlPoints.Count, Is.EqualTo(6)); + Assert.That(controlPoints.Single(c => c.Type != null).Type, Is.EqualTo(PathType.Catmull)); + } + } + + [Test] + public void TestNonLegacyAdjacentImplicitCatmullSegmentsAreNotMerged() + { + var decoder = new LegacyBeatmapDecoder(LegacyBeatmapEncoder.FIRST_LAZER_VERSION) { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("adjacent-catmull-segments.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var decoded = decoder.Decode(stream); + var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints; + + Assert.That(controlPoints.Count, Is.EqualTo(4)); + Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull)); + Assert.That(controlPoints[1].Type, Is.EqualTo(PathType.Catmull)); + Assert.That(controlPoints[2].Type, Is.EqualTo(PathType.Catmull)); + Assert.That(controlPoints[3].Type, Is.Null); + } + } } } diff --git a/osu.Game.Tests/Resources/adjacent-catmull-segments.osu b/osu.Game.Tests/Resources/adjacent-catmull-segments.osu new file mode 100644 index 0000000000..a436fe5228 --- /dev/null +++ b/osu.Game.Tests/Resources/adjacent-catmull-segments.osu @@ -0,0 +1,2 @@ +[HitObjects] +200,304,23875,6,0,C|288:304|288:304|288:208|288:208|352:208,1,260,8|0 \ No newline at end of file From 731f0960ec53ed3a6059ae5a29cfd5a27f5c3d67 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 18 May 2022 17:11:08 +0900 Subject: [PATCH 1149/1276] Don't merge adjacent legacy Catmull segments --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 ++-- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index b91a74c4a1..3b4200e7a9 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -22,7 +22,7 @@ namespace osu.Game.Beatmaps.Formats { public class LegacyBeatmapEncoder { - public const int LATEST_VERSION = 128; + public const int FIRST_LAZER_VERSION = 128; /// /// osu! is generally slower than taiko, so a factor is added to increase @@ -55,7 +55,7 @@ namespace osu.Game.Beatmaps.Formats public void Encode(TextWriter writer) { - writer.WriteLine($"osu file format v{LATEST_VERSION}"); + writer.WriteLine($"osu file format v{FIRST_LAZER_VERSION}"); writer.WriteLine(); handleGeneral(writer); diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 2a7f2b037f..7cf68a2df7 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -336,10 +336,14 @@ namespace osu.Game.Rulesets.Objects.Legacy while (++endIndex < vertices.Length - endPointLength) { - // Keep incrementing while an implicit segment doesn't need to be started + // Keep incrementing while an implicit segment doesn't need to be started. if (vertices[endIndex].Position != vertices[endIndex - 1].Position) continue; + // Adjacent legacy Catmull segments should be treated as a single segment. + if (FormatVersion < LegacyBeatmapEncoder.FIRST_LAZER_VERSION && type == PathType.Catmull) + continue; + // The last control point of each segment is not allowed to start a new implicit segment. if (endIndex == vertices.Length - endPointLength - 1) continue; From df4968a55bda4c0d6dcc0cd9bb9d18ca94f511f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 May 2022 21:20:14 +0900 Subject: [PATCH 1150/1276] Add `new Guid` bannedsymbols rule --- CodeAnalysis/BannedSymbols.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index e96ad48325..b3cca21c98 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -11,6 +11,7 @@ T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal exten T:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods. M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast() instead. M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IRealmCollection,NotificationCallbackDelegate) instead. +M:System.Guid.#ctor;Probably meaning to use Guid.New() instead. If actually wanting empty, use Guid.Empty(). M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable,NotificationCallbackDelegate) instead. M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IList,NotificationCallbackDelegate) instead. M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks. From fa09270e6278ad808c82021938227b1fb3f46b36 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 May 2022 21:21:20 +0900 Subject: [PATCH 1151/1276] Remove left-over todo --- osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index d5ef879479..ad6ff21353 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -188,7 +188,6 @@ namespace osu.Game.Overlays.FirstRunSetup [Resolved] private LegacyImportManager legacyImportManager { get; set; } = null!; - // TODO: test public IEnumerable HandledExtensions { get; } = new[] { string.Empty }; private readonly Bindable currentDirectory = new Bindable(); From d54e1503c763f735360bc8caac09019a4a5dbd60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 May 2022 21:22:17 +0900 Subject: [PATCH 1152/1276] Rename interaction toggle method --- .../Overlays/FirstRunSetup/ScreenImportFromStable.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index ad6ff21353..106e78dc18 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.FirstRunSetup if (storage == null) { - allowInteraction(false); + toggleInteraction(false); stableLocatorTextBox.Current.Disabled = false; stableLocatorTextBox.Current.Value = string.Empty; @@ -104,14 +104,14 @@ namespace osu.Game.Overlays.FirstRunSetup c.UpdateCount(); } - allowInteraction(true); + toggleInteraction(true); stableLocatorTextBox.Current.Value = storage.GetFullPath(string.Empty); importButton.Enabled.Value = true; } private void runImport() { - allowInteraction(false); + toggleInteraction(false); progressText.FadeIn(1000, Easing.OutQuint); StableContent importableContent = 0; @@ -127,13 +127,13 @@ namespace osu.Game.Overlays.FirstRunSetup importButton.Complete(); else { - allowInteraction(true); + toggleInteraction(true); importButton.Abort(); } })); } - private void allowInteraction(bool allow) + private void toggleInteraction(bool allow) { importButton.Enabled.Value = allow; stableLocatorTextBox.Current.Disabled = !allow; From 0cee90e156f31492bd81283c7e4b39238f10734d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 May 2022 21:32:32 +0900 Subject: [PATCH 1153/1276] Add common strings for missing localisable content --- osu.Game/Localisation/CommonStrings.cs | 22 ++++++++++++++++++- .../FirstRunSetup/ScreenImportFromStable.cs | 8 +++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 9cd626af0f..1fd677034d 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -69,6 +69,26 @@ namespace osu.Game.Localisation /// public static LocalisableString SelectAll => new TranslatableString(getKey(@"select_all"), @"Select All"); + /// + /// "Beatmaps" + /// + public static LocalisableString Beatmaps => new TranslatableString(getKey(@"beatmaps"), @"Beatmaps"); + + /// + /// "Scores" + /// + public static LocalisableString Scores => new TranslatableString(getKey(@"scores"), @"Scores"); + + /// + /// "Skins" + /// + public static LocalisableString Skins => new TranslatableString(getKey(@"skins"), @"Skins"); + + /// + /// "Collections" + /// + public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"Collections"); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} +} \ No newline at end of file diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index 106e78dc18..cf8abf0a76 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -60,10 +60,10 @@ namespace osu.Game.Overlays.FirstRunSetup Label = FirstRunOverlayImportFromStableScreenStrings.LocateDirectoryLabel, PlaceholderText = FirstRunOverlayImportFromStableScreenStrings.LocateDirectoryPlaceholder }, - new ImportCheckbox("Beatmaps", StableContent.Beatmaps), - new ImportCheckbox("Scores", StableContent.Scores), - new ImportCheckbox("Skins", StableContent.Skins), - new ImportCheckbox("Collections", StableContent.Collections), + new ImportCheckbox(CommonStrings.Beatmaps, StableContent.Beatmaps), + new ImportCheckbox(CommonStrings.Scores, StableContent.Scores), + new ImportCheckbox(CommonStrings.Skins, StableContent.Skins), + new ImportCheckbox(CommonStrings.Collections, StableContent.Collections), importButton = new ProgressRoundedButton { Size = button_size, From f5235f6a563a013529387a73ea481ea3dc9fd5aa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 May 2022 20:38:13 +0300 Subject: [PATCH 1154/1276] Correctify recommended method and property --- CodeAnalysis/BannedSymbols.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index b3cca21c98..b72df0a306 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -11,7 +11,7 @@ T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal exten T:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods. M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast() instead. M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IRealmCollection,NotificationCallbackDelegate) instead. -M:System.Guid.#ctor;Probably meaning to use Guid.New() instead. If actually wanting empty, use Guid.Empty(). +M:System.Guid.#ctor;Probably meaning to use Guid.NewGuid() instead. If actually wanting empty, use Guid.Empty. M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable,NotificationCallbackDelegate) instead. M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IList,NotificationCallbackDelegate) instead. M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks. From 371738b047105ec1f14b84baf992b1acc52b068d Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 18 May 2022 20:00:42 +0100 Subject: [PATCH 1155/1276] Check against the loaded drawable channel --- osu.Game/Overlays/ChatOverlayV2.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index e91d57f18b..611c539f8f 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -263,18 +263,18 @@ namespace osu.Game.Overlays ChatOverlayDrawableChannel drawableChannel = new ChatOverlayDrawableChannel(newChannel); loadedChannels.Add(newChannel, drawableChannel); - LoadComponentAsync(drawableChannel, loaded => + LoadComponentAsync(drawableChannel, loadedDrawable => { // Ensure the current channel hasn't changed by the time the load completes - if (currentChannel.Value != newChannel) + if (currentChannel.Value != loadedDrawable.Channel) return; // Ensure the cached reference hasn't been removed from leaving the channel - if (!loadedChannels.ContainsKey(newChannel)) + if (!loadedChannels.ContainsKey(loadedDrawable.Channel)) return; currentChannelContainer.Clear(false); - currentChannelContainer.Add(loaded); + currentChannelContainer.Add(loadedDrawable); loading.Hide(); }); } From 5d3878a737ae957c359aff643285954172731a52 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 May 2022 22:58:39 +0300 Subject: [PATCH 1156/1276] Add test coverage for slow-loading channels --- .../Visual/Online/TestSceneChatOverlayV2.cs | 64 ++++++++++++++++++- osu.Game/Overlays/ChatOverlayV2.cs | 6 +- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 82089ff3d1..3e3c8122b3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -5,6 +5,8 @@ using System; using System.Linq; using System.Collections.Generic; using System.Net; +using System.Threading; +using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -31,7 +33,7 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneChatOverlayV2 : OsuManualInputManagerTestScene { - private ChatOverlayV2 chatOverlay; + private TestChatOverlayV2 chatOverlay; private ChannelManager channelManager; private APIUser testUser; @@ -61,7 +63,7 @@ namespace osu.Game.Tests.Visual.Online Children = new Drawable[] { channelManager, - chatOverlay = new ChatOverlayV2 { RelativeSizeAxes = Axes.Both }, + chatOverlay = new TestChatOverlayV2 { RelativeSizeAxes = Axes.Both }, }, }; }); @@ -386,6 +388,34 @@ namespace osu.Game.Tests.Visual.Online AddAssert("TextBox is not focused", () => InputManager.FocusedDrawable == null); } + [Test] + public void TestSlowLoadingChannel() + { + AddStep("Show overlay (slow-loading)", () => + { + chatOverlay.Show(); + chatOverlay.SlowLoading = true; + }); + AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); + AddAssert("Channel 1 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel1).LoadState == LoadState.Loading); + + AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2)); + AddStep("Select channel 2", () => clickDrawable(getChannelListItem(testChannel2))); + AddAssert("Channel 2 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel2).LoadState == LoadState.Loading); + + AddStep("Finish channel 1 load", () => chatOverlay.GetSlowLoadingChannel(testChannel1).LoadEvent.Set()); + AddAssert("Channel 1 ready", () => chatOverlay.GetSlowLoadingChannel(testChannel1).LoadState == LoadState.Ready); + AddAssert("Channel 1 not displayed", () => !channelIsVisible); + + AddStep("Finish channel 2 load", () => chatOverlay.GetSlowLoadingChannel(testChannel2).LoadEvent.Set()); + AddAssert("Channel 2 loaded", () => chatOverlay.GetSlowLoadingChannel(testChannel2).IsLoaded); + AddAssert("Channel 2 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); + + AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); + AddAssert("Channel 1 loaded", () => chatOverlay.GetSlowLoadingChannel(testChannel1).IsLoaded); + AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + } + private bool listingIsVisible => chatOverlay.ChildrenOfType().Single().State.Value == Visibility.Visible; @@ -432,5 +462,35 @@ namespace osu.Game.Tests.Visual.Online Topic = $"We talk about the number {id} here", Type = ChannelType.Public, }; + + private class TestChatOverlayV2 : ChatOverlayV2 + { + public bool SlowLoading { get; set; } + + public SlowLoadingDrawableChannel GetSlowLoadingChannel(Channel channel) => DrawableChannels.OfType().Single(c => c.Channel == channel); + + protected override ChatOverlayDrawableChannel CreateDrawableChannel(Channel newChannel) + { + return SlowLoading + ? new SlowLoadingDrawableChannel(newChannel) + : new ChatOverlayDrawableChannel(newChannel); + } + } + + private class SlowLoadingDrawableChannel : ChatOverlayDrawableChannel + { + public readonly ManualResetEventSlim LoadEvent = new ManualResetEventSlim(); + + public SlowLoadingDrawableChannel([NotNull] Channel channel) + : base(channel) + { + } + + [BackgroundDependencyLoader] + private void load() + { + LoadEvent.Wait(10000); + } + } } } diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 611c539f8f..b2c1f6858c 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -41,6 +41,8 @@ namespace osu.Game.Overlays private readonly Dictionary loadedChannels = new Dictionary(); + protected IEnumerable DrawableChannels => loadedChannels.Values; + private readonly BindableFloat chatHeight = new BindableFloat(); private bool isDraggingTopBar; private float dragStartChatHeight; @@ -260,7 +262,7 @@ namespace osu.Game.Overlays loading.Show(); // Ensure the drawable channel is stored before async load to prevent double loading - ChatOverlayDrawableChannel drawableChannel = new ChatOverlayDrawableChannel(newChannel); + ChatOverlayDrawableChannel drawableChannel = CreateDrawableChannel(newChannel); loadedChannels.Add(newChannel, drawableChannel); LoadComponentAsync(drawableChannel, loadedDrawable => @@ -281,6 +283,8 @@ namespace osu.Game.Overlays } } + protected virtual ChatOverlayDrawableChannel CreateDrawableChannel(Channel newChannel) => new ChatOverlayDrawableChannel(newChannel); + private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) { switch (args.Action) From 3bca014b52b2b7ea11b3b0ce491d0f0456854311 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 19 May 2022 09:44:11 +0900 Subject: [PATCH 1157/1276] Bust CI cache on CodeAnalysis ruleset changes --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 729f2f266d..1d5c565d60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: uses: actions/cache@v3 with: path: ${{ github.workspace }}/inspectcode - key: inspectcode-${{ hashFiles('.config/dotnet-tools.json', '.github/workflows/ci.yml', 'osu.sln*', '.editorconfig', '.globalconfig') }} + key: inspectcode-${{ hashFiles('.config/dotnet-tools.json', '.github/workflows/ci.yml', 'osu.sln*', '.editorconfig', '.globalconfig', 'CodeAnalysis/*') }} - name: Dotnet code style run: dotnet build -c Debug -warnaserror osu.Desktop.slnf -p:EnforceCodeStyleInBuild=true From a443200d0d25c54a0e28a0691b9ada5a9bd2bab1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 May 2022 13:49:52 +0900 Subject: [PATCH 1158/1276] Make dependency nullable to allow for safer disposal unbinding --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 2 +- osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 391147648f..dd6226e19b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly Bindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); - [Resolved(canBeNull: true)] + [Resolved(canBeNull: true)] // Can't really be null but required to handle potential of disposal before DI completes. private DrawableHitObject? drawableObject { get; set; } [Resolved] diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index cf8abf0a76..62b517d982 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -192,14 +192,14 @@ namespace osu.Game.Overlays.FirstRunSetup private readonly Bindable currentDirectory = new Bindable(); - [Resolved] - private OsuGameBase game { get; set; } = null!; + [Resolved(canBeNull: true)] // Can't really be null but required to handle potential of disposal before DI completes. + private OsuGameBase? game { get; set; } protected override void LoadComplete() { base.LoadComplete(); - game.RegisterImportHandler(this); + game?.RegisterImportHandler(this); currentDirectory.BindValueChanged(onDirectorySelected); @@ -242,7 +242,7 @@ namespace osu.Game.Overlays.FirstRunSetup protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - game.UnregisterImportHandler(this); + game?.UnregisterImportHandler(this); } public override Popover GetPopover() => new DirectoryChooserPopover(currentDirectory); From f6fb8f3fb99be7352e40d4cd7b4df12e755349b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 May 2022 13:54:44 +0900 Subject: [PATCH 1159/1276] Delete and ignore rider's `misc.xml` This file has changed on us too many times. Doesn't seem to contain anything of value. --- .gitignore | 2 ++ .idea/.idea.osu.Desktop/.idea/misc.xml | 11 ----------- 2 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 .idea/.idea.osu.Desktop/.idea/misc.xml diff --git a/.gitignore b/.gitignore index 5b19270ab9..0c7a18b437 100644 --- a/.gitignore +++ b/.gitignore @@ -340,3 +340,5 @@ inspectcode # Fody (pulled in by Realm) - schema file FodyWeavers.xsd **/FodyWeavers.xml + +.idea/.idea.osu.Desktop/.idea/misc.xml \ No newline at end of file diff --git a/.idea/.idea.osu.Desktop/.idea/misc.xml b/.idea/.idea.osu.Desktop/.idea/misc.xml deleted file mode 100644 index 4e1d56f4dd..0000000000 --- a/.idea/.idea.osu.Desktop/.idea/misc.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - \ No newline at end of file From 70bd40ce443cf7ece4df644dd013afe9f3842c89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 May 2022 14:01:24 +0900 Subject: [PATCH 1160/1276] Fix incorrect count of beatmaps available to import --- osu.Game/Database/LegacyModelImporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyModelImporter.cs b/osu.Game/Database/LegacyModelImporter.cs index 13a9025ece..9b2a54dada 100644 --- a/osu.Game/Database/LegacyModelImporter.cs +++ b/osu.Game/Database/LegacyModelImporter.cs @@ -40,7 +40,7 @@ namespace osu.Game.Database Importer = importer; } - public Task GetAvailableCount(StableStorage stableStorage) => Task.Run(() => GetStableImportPaths(stableStorage).Count()); + public Task GetAvailableCount(StableStorage stableStorage) => Task.Run(() => GetStableImportPaths(PrepareStableStorage(stableStorage)).Count()); public Task ImportFromStableAsync(StableStorage stableStorage) { From 5af7641e946939a15e7729f58ac76de4b6511269 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 May 2022 15:53:53 +0900 Subject: [PATCH 1161/1276] Add safety against playfield potentially not being available during mania note placement --- .../Blueprints/ManiaPlacementBlueprint.cs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs index ad95fd5e87..7a99565e8a 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs @@ -55,24 +55,25 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints { base.UpdateTimeAndPosition(result); - var playfield = (Column)result.Playfield; - - // Apply an offset to better align with the visual grid. - // This should only be applied during placement, as during selection / drag operations the movement is relative - // to the initial point of interaction rather than the grid. - switch (playfield.ScrollingInfo.Direction.Value) + if (result.Playfield is Column col) { - case ScrollingDirection.Down: - result.ScreenSpacePosition -= new Vector2(0, getNoteHeight(playfield) / 2); - break; + // Apply an offset to better align with the visual grid. + // This should only be applied during placement, as during selection / drag operations the movement is relative + // to the initial point of interaction rather than the grid. + switch (col.ScrollingInfo.Direction.Value) + { + case ScrollingDirection.Down: + result.ScreenSpacePosition -= new Vector2(0, getNoteHeight(col) / 2); + break; - case ScrollingDirection.Up: - result.ScreenSpacePosition += new Vector2(0, getNoteHeight(playfield) / 2); - break; + case ScrollingDirection.Up: + result.ScreenSpacePosition += new Vector2(0, getNoteHeight(col) / 2); + break; + } + + if (PlacementActive == PlacementState.Waiting) + Column = col; } - - if (PlacementActive == PlacementState.Waiting) - Column = playfield; } private float getNoteHeight(Column resultPlayfield) => From b3d6f76cfacdca8ec3906c8f92d324b71f1e54fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 May 2022 15:57:17 +0900 Subject: [PATCH 1162/1276] Add "None" snap type to fix flags not working correctly --- osu.Game/Rulesets/Edit/SnapType.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/SnapType.cs b/osu.Game/Rulesets/Edit/SnapType.cs index 4ab67fee63..6761356331 100644 --- a/osu.Game/Rulesets/Edit/SnapType.cs +++ b/osu.Game/Rulesets/Edit/SnapType.cs @@ -8,8 +8,9 @@ namespace osu.Game.Rulesets.Edit [Flags] public enum SnapType { - NearbyObjects = 0, - Grids = 1, + None = 0, + NearbyObjects = 1 << 0, + Grids = 1 << 1, All = NearbyObjects | Grids, } } From 363e7a6f537b7feda0f8825192391d64e791a749 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 May 2022 16:42:43 +0900 Subject: [PATCH 1163/1276] Simplify toolbar hiding logic in `FirstRunSetupOverlay` Rather than fiddling around with the activation modes, this seems like a much cleaner way to make things work. Closes https://github.com/ppy/osu/issues/18277. --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 22 +++------------------- osu.Game/Screens/Menu/MainMenu.cs | 2 ++ 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 890f17b013..7b0de4affe 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -62,8 +62,6 @@ namespace osu.Game.Overlays private Container screenContent = null!; - private Bindable? overlayActivationMode; - private Container content = null!; private LoadingSpinner loading = null!; @@ -225,16 +223,9 @@ namespace osu.Game.Overlays // if we are valid for display, only do so after reaching the main menu. performer.PerformFromScreen(screen => { - MainMenu menu = (MainMenu)screen; - - // Eventually I'd like to replace this with a better method that doesn't access the screen. - // Either this dialog would be converted to its own screen, or at very least be "hosted" by a screen pushed to the main menu. - // Alternatively, another method of disabling notifications could be added to `INotificationOverlay`. - if (menu != null) - { - overlayActivationMode = menu.OverlayActivationMode.GetBoundCopy(); - overlayActivationMode.Value = OverlayActivation.UserTriggered; - } + // Hides the toolbar for us. + if (screen is MainMenu menu) + menu.ReturnToOsuLogo(); base.Show(); }, new[] { typeof(MainMenu) }); @@ -257,13 +248,6 @@ namespace osu.Game.Overlays content.ScaleTo(0.99f, 400, Easing.OutQuint); - if (overlayActivationMode != null) - { - // If this is non-null we are guaranteed to have come from the main menu. - overlayActivationMode.Value = OverlayActivation.All; - overlayActivationMode = null; - } - if (currentStepIndex != null) { notificationOverlay.Post(new SimpleNotification diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 4401ee93ec..6fc8039413 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -150,6 +150,8 @@ namespace osu.Game.Screens.Menu [Resolved(canBeNull: true)] private IPerformFromScreenRunner performer { get; set; } + public void ReturnToOsuLogo() => Buttons.State = ButtonSystemState.Initial; + private void confirmAndExit() { if (exitConfirmed) return; From 63c97763658c490de30239d0b1e41799dbed9d2e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 May 2022 19:24:18 +0900 Subject: [PATCH 1164/1276] Fix oversight in bindable logic in new overlay class --- osu.Game/Overlays/ChatOverlayV2.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 0307dcfdf3..42c928014a 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -62,6 +62,9 @@ namespace osu.Game.Overlays [Cached] private readonly Bindable currentChannel = new Bindable(); + private readonly IBindableList availableChannels = new BindableList(); + private readonly IBindableList joinedChannels = new BindableList(); + public ChatOverlayV2() { Height = default_chat_height; @@ -147,9 +150,13 @@ namespace osu.Game.Overlays chatHeight.BindValueChanged(height => { Height = height.NewValue; }, true); currentChannel.BindTo(channelManager.CurrentChannel); - channelManager.CurrentChannel.BindValueChanged(currentChannelChanged, true); - channelManager.JoinedChannels.BindCollectionChanged(joinedChannelsChanged, true); - channelManager.AvailableChannels.BindCollectionChanged(availableChannelsChanged, true); + currentChannel.BindValueChanged(currentChannelChanged, true); + + joinedChannels.BindTo(channelManager.JoinedChannels); + joinedChannels.BindCollectionChanged(joinedChannelsChanged, true); + + availableChannels.BindTo(channelManager.AvailableChannels); + availableChannels.BindCollectionChanged(availableChannelsChanged, true); channelList.OnRequestSelect += channel => channelManager.CurrentChannel.Value = channel; channelList.OnRequestLeave += channel => channelManager.LeaveChannel(channel); @@ -263,8 +270,8 @@ namespace osu.Game.Overlays switch (args.Action) { case NotifyCollectionChangedAction.Add: - IEnumerable joinedChannels = filterChannels(args.NewItems); - foreach (var channel in joinedChannels) + IEnumerable newChannels = filterChannels(args.NewItems); + foreach (var channel in newChannels) channelList.AddChannel(channel); break; From 136ecb45e241b6d0dd4358107ddb2d9b4299f3bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 May 2022 19:26:14 +0900 Subject: [PATCH 1165/1276] Rename dummy channel and move to a nested class inside the `ChannelListing` itself --- osu.Game.Tests/Visual/Online/TestSceneChannelList.cs | 3 ++- .../Visual/Online/TestSceneChatOverlayV2.cs | 2 +- osu.Game/Online/Chat/ChannelManager.cs | 4 ++-- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 5 +++-- osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs | 3 ++- osu.Game/Overlays/Chat/Listing/ChannelListing.cs | 9 +++++++++ osu.Game/Overlays/ChatOverlayV2.cs | 11 +---------- 7 files changed, 20 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs index 53a48fcc58..e4bc5645b6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -13,6 +13,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Chat; using osu.Game.Overlays; using osu.Game.Overlays.Chat.ChannelList; +using osu.Game.Overlays.Chat.Listing; namespace osu.Game.Tests.Visual.Online { @@ -147,7 +148,7 @@ namespace osu.Game.Tests.Visual.Online }); } - private bool validItem => selected.Value != null && !(selected.Value is DummySelectorChannel); + private bool validItem => selected.Value != null && !(selected.Value is ChannelListing.ChannelListingChannel); private Channel createRandomPublicChannel() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 48557a4ddb..3b8849f7c9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -408,7 +408,7 @@ namespace osu.Game.Tests.Visual.Online chatOverlay.ChildrenOfType().Single(); private ChannelListItem channelSelectorButton => - chatOverlay.ChildrenOfType().Single(item => item.Channel is DummySelectorChannel); + chatOverlay.ChildrenOfType().Single(item => item.Channel is ChannelListing.ChannelListingChannel); private void clickDrawable(Drawable d) { diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 9e85067f15..b7d67de04d 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -14,7 +14,7 @@ using osu.Game.Input; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Overlays; +using osu.Game.Overlays.Chat.Listing; using osu.Game.Overlays.Chat.Tabs; namespace osu.Game.Online.Chat @@ -134,7 +134,7 @@ namespace osu.Game.Online.Chat private void currentChannelChanged(ValueChangedEvent e) { - bool isSelectorChannel = e.NewValue is ChannelSelectorTabItem.ChannelSelectorTabChannel || e.NewValue is DummySelectorChannel; + bool isSelectorChannel = e.NewValue is ChannelSelectorTabItem.ChannelSelectorTabChannel || e.NewValue is ChannelListing.ChannelListingChannel; if (!isSelectorChannel) JoinChannel(e.NewValue); diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index c9f3a4d380..555b54155c 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; +using osu.Game.Overlays.Chat.Listing; namespace osu.Game.Overlays.Chat.ChannelList { @@ -23,7 +24,7 @@ namespace osu.Game.Overlays.Chat.ChannelList private readonly Dictionary channelMap = new Dictionary(); - private readonly DummySelectorChannel dummySelectorChannel = new DummySelectorChannel(); + private readonly ChannelListing.ChannelListingChannel channelListingChannel = new ChannelListing.ChannelListingChannel(); private ChannelListItemFlow publicChannelFlow = null!; private ChannelListItemFlow privateChannelFlow = null!; @@ -53,7 +54,7 @@ namespace osu.Game.Overlays.Chat.ChannelList Children = new Drawable[] { publicChannelFlow = new ChannelListItemFlow("CHANNELS"), - selector = new ChannelListItem(dummySelectorChannel) + selector = new ChannelListItem(channelListingChannel) { Margin = new MarginPadding { Bottom = 10 }, }, diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index eb2bdfdf8a..2967d9df11 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -15,6 +15,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; +using osu.Game.Overlays.Chat.Listing; using osu.Game.Users.Drawables; using osuTK; @@ -191,6 +192,6 @@ namespace osu.Game.Overlays.Chat.ChannelList text.FadeColour(colourProvider.Light3, 200, Easing.OutQuint); } - private bool isSelector => Channel is DummySelectorChannel; + private bool isSelector => Channel is ChannelListing.ChannelListingChannel; } } diff --git a/osu.Game/Overlays/Chat/Listing/ChannelListing.cs b/osu.Game/Overlays/Chat/Listing/ChannelListing.cs index 732c78de15..8a5bc18cbf 100644 --- a/osu.Game/Overlays/Chat/Listing/ChannelListing.cs +++ b/osu.Game/Overlays/Chat/Listing/ChannelListing.cs @@ -75,5 +75,14 @@ namespace osu.Game.Overlays.Chat.Listing protected override void PopIn() => this.FadeIn(); protected override void PopOut() => this.FadeOut(); + + public class ChannelListingChannel : Channel + { + public ChannelListingChannel() + { + Name = "Add more channels"; + Type = ChannelType.System; + } + } } } diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 42c928014a..202d70d99b 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -244,7 +244,7 @@ namespace osu.Game.Overlays { Channel? newChannel = channel.NewValue; - if (newChannel == null || newChannel is DummySelectorChannel) + if (newChannel == null || newChannel is ChannelListing.ChannelListingChannel) { // null channel denotes that we should be showing the listing. channelListing.State.Value = Visibility.Visible; @@ -300,13 +300,4 @@ namespace osu.Game.Overlays channelManager.PostMessage(message); } } - - public class DummySelectorChannel : Channel - { - public DummySelectorChannel() - { - Name = "Add more channels"; - Type = ChannelType.System; - } - } } From c37d1e4fae4fe827e7ae36b485407d280fd29ba5 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Thu, 19 May 2022 11:45:39 +0100 Subject: [PATCH 1166/1276] Ensure current channel is set to the `ChannelListingChannel` when it becomes `null` --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 6 +++--- osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs | 2 +- osu.Game/Overlays/ChatOverlayV2.cs | 10 ++++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 555b54155c..d1ceae604c 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -22,9 +22,9 @@ namespace osu.Game.Overlays.Chat.ChannelList public Action? OnRequestSelect; public Action? OnRequestLeave; - private readonly Dictionary channelMap = new Dictionary(); + public readonly ChannelListing.ChannelListingChannel ChannelListingChannel = new ChannelListing.ChannelListingChannel(); - private readonly ChannelListing.ChannelListingChannel channelListingChannel = new ChannelListing.ChannelListingChannel(); + private readonly Dictionary channelMap = new Dictionary(); private ChannelListItemFlow publicChannelFlow = null!; private ChannelListItemFlow privateChannelFlow = null!; @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Chat.ChannelList Children = new Drawable[] { publicChannelFlow = new ChannelListItemFlow("CHANNELS"), - selector = new ChannelListItem(channelListingChannel) + selector = new ChannelListItem(ChannelListingChannel) { Margin = new MarginPadding { Bottom = 10 }, }, diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index 2967d9df11..9ab0c2792a 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -179,7 +179,7 @@ namespace osu.Game.Overlays.Chat.ChannelList private void updateState() { - bool selected = selectedChannel.Value == Channel || (isSelector && selectedChannel.Value == null); + bool selected = selectedChannel.Value == Channel; if (selected) selectBox.FadeIn(300, Easing.OutQuint); diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 202d70d99b..fe3f8ba8cb 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -244,9 +244,15 @@ namespace osu.Game.Overlays { Channel? newChannel = channel.NewValue; - if (newChannel == null || newChannel is ChannelListing.ChannelListingChannel) + // null channel denotes that we should be showing the listing. + if (newChannel == null) + { + currentChannel.Value = channelList.ChannelListingChannel; + return; + } + + if (newChannel is ChannelListing.ChannelListingChannel) { - // null channel denotes that we should be showing the listing. channelListing.State.Value = Visibility.Visible; textBar.ShowSearch.Value = true; } From 3fdff7bbcb5a6f17a736f8e71e59a1fdacf8b3a7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 17 May 2022 15:11:22 +0900 Subject: [PATCH 1167/1276] Make Meh judgements not drain HP --- osu.Game/Rulesets/Judgements/Judgement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index fd576e9b9f..4fe34e15fd 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Judgements return -DEFAULT_MAX_HEALTH_INCREASE; case HitResult.Meh: - return -DEFAULT_MAX_HEALTH_INCREASE * 0.05; + return DEFAULT_MAX_HEALTH_INCREASE * 0.05; case HitResult.Ok: return DEFAULT_MAX_HEALTH_INCREASE * 0.5; From ce7be940e2dd4f220583318bdf623a63d0f6e4f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 14:34:33 +0900 Subject: [PATCH 1168/1276] Setup basics for control and test --- .../Editing/TestSceneTapTimingControl.cs | 94 +++++++++ .../UserInterfaceV2/LabelledDrawable.cs | 3 +- .../Screens/Edit/Timing/TapTimingControl.cs | 189 ++++++++++++++++++ osu.Game/Screens/Edit/Timing/TimingSection.cs | 2 + 4 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs create mode 100644 osu.Game/Screens/Edit/Timing/TapTimingControl.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs new file mode 100644 index 0000000000..c2889b2549 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs @@ -0,0 +1,94 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Timing; +using osuTK; + +namespace osu.Game.Tests.Visual.Editing +{ + [TestFixture] + public class TestSceneTapTimingControl : EditorClockTestScene + { + [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] + private readonly EditorBeatmap editorBeatmap; + + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + + [Cached] + private Bindable selectedGroup = new Bindable(); + + private TapTimingControl control; + + public TestSceneTapTimingControl() + { + editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)); + selectedGroup.Value = editorBeatmap.ControlPointInfo.Groups.First(); + } + + [Test] + public void TestTapThenReset() + { + AddStep("click tap button", () => + { + control.ChildrenOfType() + .First(b => b.Text == "Tap to beat") + .TriggerClick(); + }); + + AddUntilStep("wait for track playing", () => Clock.IsRunning); + + AddStep("click reset button", () => + { + control.ChildrenOfType() + .First(b => b.Text == "Reset") + .TriggerClick(); + }); + + AddUntilStep("wait for track stopped", () => !Clock.IsRunning); + } + + [Test] + public void TestBasic() + { + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + Beatmap.Disabled = true; + + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, + Width = 400, + Scale = new Vector2(1.5f), + Child = control = new TapTimingControl(), + }; + } + + protected override void Dispose(bool isDisposing) + { + Beatmap.Disabled = false; + base.Dispose(isDisposing); + } + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs index 1e6032c1d0..066e1a7978 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs @@ -41,7 +41,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected const float CONTENT_PADDING_VERTICAL = 10; protected const float CONTENT_PADDING_HORIZONTAL = 15; - protected const float CORNER_RADIUS = 15; + + public const float CORNER_RADIUS = 15; /// /// The component that is being displayed. diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs new file mode 100644 index 0000000000..3867a4bcec --- /dev/null +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -0,0 +1,189 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Screens.Edit.Timing +{ + public class TapTimingControl : CompositeDrawable + { + [Resolved] + private EditorClock editorClock { get; set; } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider, OsuColour colours) + { + Height = 200; + RelativeSizeAxes = Axes.X; + + CornerRadius = LabelledDrawable.CORNER_RADIUS; + Masking = true; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = colourProvider.Background4, + RelativeSizeAxes = Axes.Both, + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 60), + }, + Content = new[] + { + new Drawable[] + { + new Metronome() + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(10), + Children = new Drawable[] + { + new RoundedButton + { + Text = "Reset", + BackgroundColour = colours.Pink, + RelativeSizeAxes = Axes.X, + Width = 0.3f, + Action = reset, + }, + new RoundedButton + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Text = "Tap to beat", + RelativeSizeAxes = Axes.X, + BackgroundColour = colourProvider.Background1, + Width = 0.68f, + Action = tap, + } + } + }, + } + } + }, + }; + } + + private void tap() + { + if (!editorClock.IsRunning) + { + editorClock.Seek(0); + editorClock.Start(); + } + } + + private void reset() + { + editorClock.Stop(); + } + + private class Metronome : BeatSyncedContainer + { + private Container swing; + private Box weight; + private OsuSpriteText bpm; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider overlayColourProvider) + { + Margin = new MarginPadding(10); + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Triangle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(80, 120), + Colour = overlayColourProvider.Background1, + }, + new Circle + { + Y = -25, + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Content2, + Size = new Vector2(10) + }, + bpm = new OsuSpriteText + { + Colour = overlayColourProvider.Content1, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + swing = new Container + { + RelativeSizeAxes = Axes.Both, + Y = -25, + Height = 0.8f, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Children = new Drawable[] + { + new Box + { + Colour = overlayColourProvider.Content2, + RelativeSizeAxes = Axes.Y, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Width = 4, + }, + weight = new Box + { + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Content2, + Size = new Vector2(15), + RelativePositionAxes = Axes.Y, + Y = -0.4f, + }, + } + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + swing + .RotateTo(20, 500, Easing.InOutQuad) + .Then() + .RotateTo(-20, 500, Easing.InOutQuad) + .Loop(); + } + + protected override void Update() + { + base.Update(); + + if (CurrentTimingPoint == null) + return; + + weight.Y = Math.Clamp((float)CurrentTimingPoint.BPM / 480, 0, 0.95f); + bpm.Text = $"{CurrentTimingPoint.BPM:F0}"; + } + } + } +} diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs index 13af04cd4b..be719d0cb6 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -19,6 +19,7 @@ namespace osu.Game.Screens.Edit.Timing { Flow.AddRange(new Drawable[] { + new TapTimingControl(), bpmTextEntry = new BPMTextBox(), timeSignature = new LabelledTimeSignature { @@ -96,5 +97,6 @@ namespace osu.Game.Screens.Edit.Timing } private static double beatLengthToBpm(double beatLength) => 60000 / beatLength; + } } From 42179568f3aca7d67c574ae615dcf8c65920eae3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 15:56:39 +0900 Subject: [PATCH 1169/1276] Initial animation pass on metronome --- .../Editing/TestSceneTapTimingControl.cs | 58 +++++++--- .../Screens/Edit/Timing/TapTimingControl.cs | 105 +++++++++++++----- osu.Game/Screens/Edit/Timing/TimingSection.cs | 1 - 3 files changed, 117 insertions(+), 47 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs index c2889b2549..9b2618df5b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs @@ -36,10 +36,37 @@ namespace osu.Game.Tests.Visual.Editing public TestSceneTapTimingControl() { - editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)); + var playableBeatmap = CreateBeatmap(new OsuRuleset().RulesetInfo); + + // Ensure time doesn't end while testing + playableBeatmap.BeatmapInfo.Length = 1200000; + + editorBeatmap = new EditorBeatmap(playableBeatmap); + selectedGroup.Value = editorBeatmap.ControlPointInfo.Groups.First(); } + protected override void LoadComplete() + { + base.LoadComplete(); + + Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + Beatmap.Disabled = true; + + Children = new Drawable[] + { + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, + Width = 400, + Scale = new Vector2(1.5f), + Child = control = new TapTimingControl(), + } + }; + } + [Test] public void TestTapThenReset() { @@ -65,24 +92,19 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestBasic() { - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); - Beatmap.Disabled = true; - - Child = new Container + AddStep("set low bpm", () => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Y, - Width = 400, - Scale = new Vector2(1.5f), - Child = control = new TapTimingControl(), - }; + editorBeatmap.ControlPointInfo.TimingPoints.First().BeatLength = 1000; + }); + + AddStep("click tap button", () => + { + control.ChildrenOfType() + .First(b => b.Text == "Tap to beat") + .TriggerClick(); + }); + + AddSliderStep("BPM", 30, 400, 60, bpm => editorBeatmap.ControlPointInfo.TimingPoints.First().BeatLength = 60000f / bpm); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index 3867a4bcec..e23982ddc7 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -3,15 +3,19 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Audio.Track; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Edit.Timing { @@ -103,9 +107,13 @@ namespace osu.Game.Screens.Edit.Timing private Container swing; private Box weight; private OsuSpriteText bpm; + private Box stick; + + [Resolved] + private OverlayColourProvider overlayColourProvider { get; set; } [BackgroundDependencyLoader] - private void load(OverlayColourProvider overlayColourProvider) + private void load() { Margin = new MarginPadding(10); AutoSizeAxes = Axes.Both; @@ -117,21 +125,13 @@ namespace osu.Game.Screens.Edit.Timing Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(80, 120), - Colour = overlayColourProvider.Background1, - }, - new Circle - { - Y = -25, - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - Colour = overlayColourProvider.Content2, - Size = new Vector2(10) + Colour = overlayColourProvider.Background2, }, bpm = new OsuSpriteText { Colour = overlayColourProvider.Content1, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, }, swing = new Container { @@ -142,10 +142,10 @@ namespace osu.Game.Screens.Edit.Timing Origin = Anchor.BottomCentre, Children = new Drawable[] { - new Box + stick = new Box { - Colour = overlayColourProvider.Content2, RelativeSizeAxes = Axes.Y, + Colour = overlayColourProvider.Colour2, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Width = 4, @@ -154,35 +154,84 @@ namespace osu.Game.Screens.Edit.Timing { Anchor = Anchor.TopCentre, Origin = Anchor.Centre, - Colour = overlayColourProvider.Content2, + Colour = overlayColourProvider.Colour1, Size = new Vector2(15), RelativePositionAxes = Axes.Y, - Y = -0.4f, + Y = 0.4f, }, } }, + new Circle + { + Y = -25, + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Colour0, + Size = new Vector2(10) + }, }; } - protected override void LoadComplete() - { - base.LoadComplete(); - swing - .RotateTo(20, 500, Easing.InOutQuad) - .Then() - .RotateTo(-20, 500, Easing.InOutQuad) - .Loop(); - } + private double beatLength; + + private TimingControlPoint timingPoint; + + private float bpmRatio; + private bool isSwinging; protected override void Update() { base.Update(); - if (CurrentTimingPoint == null) + timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(BeatSyncClock.CurrentTime); + + if (beatLength != timingPoint.BeatLength) + { + beatLength = timingPoint.BeatLength; + bpm.Text = $"{timingPoint.BPM:F0}"; + + EarlyActivationMilliseconds = timingPoint.BeatLength / 2; + + bpmRatio = (float)Interpolation.ApplyEasing(Easing.OutQuad, Math.Clamp((timingPoint.BPM - 30) / 480, 0, 1)); + + weight.MoveToY((float)Interpolation.Lerp(0, 0.9f, bpmRatio), 600, Easing.OutQuint); + } + + if (BeatSyncClock?.IsRunning != true && isSwinging) + { + swing.ClearTransforms(true); + + using (swing.BeginDelayedSequence(350)) + { + swing.RotateTo(0, 1000, Easing.OutQuint); + stick.FadeColour(overlayColourProvider.Colour2, 1000, Easing.OutQuint); + } + + isSwinging = false; + } + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + float angle = (float)Interpolation.Lerp(25, 4, bpmRatio); + + if (!IsBeatSyncedWithTrack) return; - weight.Y = Math.Clamp((float)CurrentTimingPoint.BPM / 480, 0, 0.95f); - bpm.Text = $"{CurrentTimingPoint.BPM:F0}"; + isSwinging = true; + + float currentAngle = swing.Rotation; + float targetAngle = currentAngle > 0 ? -angle : angle; + + swing.RotateTo(targetAngle, beatLength, Easing.InOutQuad); + + if (currentAngle != 0 && Math.Abs(currentAngle - targetAngle) > angle * 1.8f && isSwinging) + { + using (stick.BeginDelayedSequence(beatLength / 2)) + stick.FlashColour(overlayColourProvider.Content1, beatLength, Easing.OutQuint); + } } } } diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs index be719d0cb6..a5abd96d72 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -97,6 +97,5 @@ namespace osu.Game.Screens.Edit.Timing } private static double beatLengthToBpm(double beatLength) => 60000 / beatLength; - } } From cf1ef28f72235a4f5d742e3d404ccfd94d2dcab1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 16:22:16 +0900 Subject: [PATCH 1170/1276] Add taper and more correctly shaped weight --- .../Screens/Edit/Timing/TapTimingControl.cs | 91 ++++++++++++++----- 1 file changed, 70 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index e23982ddc7..7507a7dd35 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -15,7 +15,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Timing { @@ -105,9 +104,11 @@ namespace osu.Game.Screens.Edit.Timing private class Metronome : BeatSyncedContainer { private Container swing; - private Box weight; + private OsuSpriteText bpm; - private Box stick; + + private Drawable weight; + private Drawable stick; [Resolved] private OverlayColourProvider overlayColourProvider { get; set; } @@ -116,33 +117,41 @@ namespace osu.Game.Screens.Edit.Timing private void load() { Margin = new MarginPadding(10); + + const float taper = 10; + + var triangleSize = new Vector2(80, 120); + AutoSizeAxes = Axes.Both; + const float stick_vertical_offset = -23; + InternalChildren = new Drawable[] { - new Triangle + new Container { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(80, 120), - Colour = overlayColourProvider.Background2, - }, - bpm = new OsuSpriteText - { - Colour = overlayColourProvider.Content1, + Masking = true, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, + Size = new Vector2(triangleSize.X * 1.2f, triangleSize.Y - taper), + Child = new Triangle + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = triangleSize, + Colour = overlayColourProvider.Background3, + }, }, swing = new Container { RelativeSizeAxes = Axes.Both, - Y = -25, + Y = stick_vertical_offset, Height = 0.8f, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, - Children = new Drawable[] + Children = new[] { - stick = new Box + stick = new Circle { RelativeSizeAxes = Axes.Y, Colour = overlayColourProvider.Colour2, @@ -150,24 +159,64 @@ namespace osu.Game.Screens.Edit.Timing Origin = Anchor.BottomCentre, Width = 4, }, - weight = new Box + weight = new Container { Anchor = Anchor.TopCentre, Origin = Anchor.Centre, Colour = overlayColourProvider.Colour1, - Size = new Vector2(15), + Size = new Vector2(10), + Rotation = 180, RelativePositionAxes = Axes.Y, Y = 0.4f, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(0.2f, 0), + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(-0.2f, 0), + }, + } }, } }, new Circle { - Y = -25, + Y = stick_vertical_offset, Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, Colour = overlayColourProvider.Colour0, - Size = new Vector2(10) + Size = new Vector2(8) + }, + new Container + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Height = 0.3f, + Children = new Drawable[] + { + new Triangle + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = triangleSize, + Colour = overlayColourProvider.Background2, + Alpha = 0.8f + }, + } + }, + bpm = new OsuSpriteText + { + Colour = overlayColourProvider.Content1, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -3, }, }; } @@ -194,7 +243,7 @@ namespace osu.Game.Screens.Edit.Timing bpmRatio = (float)Interpolation.ApplyEasing(Easing.OutQuad, Math.Clamp((timingPoint.BPM - 30) / 480, 0, 1)); - weight.MoveToY((float)Interpolation.Lerp(0, 0.9f, bpmRatio), 600, Easing.OutQuint); + weight.MoveToY((float)Interpolation.Lerp(0.07f, 0.9f, bpmRatio), 600, Easing.OutQuint); } if (BeatSyncClock?.IsRunning != true && isSwinging) @@ -215,7 +264,7 @@ namespace osu.Game.Screens.Edit.Timing { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - float angle = (float)Interpolation.Lerp(25, 4, bpmRatio); + float angle = (float)Interpolation.Lerp(30, 5, bpmRatio); if (!IsBeatSyncedWithTrack) return; From 79878a4ec21cab922d4ed10295fa0f9eac8ecda2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 16:25:24 +0900 Subject: [PATCH 1171/1276] Metrics adjust and labelling --- .../Screens/Edit/Timing/TapTimingControl.cs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index 7507a7dd35..3aff74d359 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -116,26 +116,27 @@ namespace osu.Game.Screens.Edit.Timing [BackgroundDependencyLoader] private void load() { + const float taper = 30; + const float swing_vertical_offset = -23; + + var triangleSize = new Vector2(90, 120 + taper); + Margin = new MarginPadding(10); - const float taper = 10; - - var triangleSize = new Vector2(80, 120); - AutoSizeAxes = Axes.Both; - const float stick_vertical_offset = -23; - InternalChildren = new Drawable[] { new Container { + Name = @"Taper adjust", Masking = true, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Size = new Vector2(triangleSize.X * 1.2f, triangleSize.Y - taper), Child = new Triangle { + Name = @"Main body", Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Size = triangleSize, @@ -144,15 +145,17 @@ namespace osu.Game.Screens.Edit.Timing }, swing = new Container { + Name = @"Swing", RelativeSizeAxes = Axes.Both, - Y = stick_vertical_offset, - Height = 0.8f, + Y = swing_vertical_offset, + Height = 0.80f, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Children = new[] { stick = new Circle { + Name = @"Stick", RelativeSizeAxes = Axes.Y, Colour = overlayColourProvider.Colour2, Anchor = Anchor.BottomCentre, @@ -161,6 +164,7 @@ namespace osu.Game.Screens.Edit.Timing }, weight = new Container { + Name = @"Weight", Anchor = Anchor.TopCentre, Origin = Anchor.Centre, Colour = overlayColourProvider.Colour1, @@ -186,7 +190,8 @@ namespace osu.Game.Screens.Edit.Timing }, new Circle { - Y = stick_vertical_offset, + Name = @"Swing connection point", + Y = swing_vertical_offset, Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, Colour = overlayColourProvider.Colour0, @@ -194,6 +199,7 @@ namespace osu.Game.Screens.Edit.Timing }, new Container { + Name = @"Lower cover", Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.Both, @@ -213,6 +219,7 @@ namespace osu.Game.Screens.Edit.Timing }, bpm = new OsuSpriteText { + Name = @"BPM display", Colour = overlayColourProvider.Content1, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, From 21072a26ef30547bbbee7b802bcd918caac4296c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 16:33:27 +0900 Subject: [PATCH 1172/1276] Add locking wedge --- .../Screens/Edit/Timing/TapTimingControl.cs | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index 3aff74d359..323561f0a8 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -133,14 +133,17 @@ namespace osu.Game.Screens.Edit.Timing Masking = true, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, - Size = new Vector2(triangleSize.X * 1.2f, triangleSize.Y - taper), - Child = new Triangle + Size = new Vector2(triangleSize.X, triangleSize.Y - taper), + Children = new Drawable[] { - Name = @"Main body", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = triangleSize, - Colour = overlayColourProvider.Background3, + new Triangle + { + Name = @"Main body", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = triangleSize, + Colour = overlayColourProvider.Background3, + }, }, }, swing = new Container @@ -188,6 +191,25 @@ namespace osu.Game.Screens.Edit.Timing }, } }, + new Container + { + Name = @"Taper adjust", + Masking = true, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(triangleSize.X, triangleSize.Y - taper), + Children = new Drawable[] + { + new Circle + { + Name = @"Locking wedge", + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Background1, + Size = new Vector2(8), + } + }, + }, new Circle { Name = @"Swing connection point", From 8487d2c48a41010245ba0b1a527ab821935520ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 16:45:01 +0900 Subject: [PATCH 1173/1276] Interpolate bpm label and fix incorrect angle logic --- .../Screens/Edit/Timing/TapTimingControl.cs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index 323561f0a8..4ab731c897 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -105,7 +106,7 @@ namespace osu.Game.Screens.Edit.Timing { private Container swing; - private OsuSpriteText bpm; + private OsuSpriteText bpmText; private Drawable weight; private Drawable stick; @@ -116,7 +117,7 @@ namespace osu.Game.Screens.Edit.Timing [BackgroundDependencyLoader] private void load() { - const float taper = 30; + const float taper = 25; const float swing_vertical_offset = -23; var triangleSize = new Vector2(90, 120 + taper); @@ -226,7 +227,7 @@ namespace osu.Game.Screens.Edit.Timing Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.Both, Masking = true, - Height = 0.3f, + Height = 0.28f, Children = new Drawable[] { new Triangle @@ -239,7 +240,7 @@ namespace osu.Game.Screens.Edit.Timing }, } }, - bpm = new OsuSpriteText + bpmText = new OsuSpriteText { Name = @"BPM display", Colour = overlayColourProvider.Content1, @@ -254,9 +255,17 @@ namespace osu.Game.Screens.Edit.Timing private TimingControlPoint timingPoint; - private float bpmRatio; private bool isSwinging; + private readonly BindableInt interpolatedBpm = new BindableInt(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + interpolatedBpm.BindValueChanged(bpm => bpmText.Text = bpm.NewValue.ToString()); + } + protected override void Update() { base.Update(); @@ -266,13 +275,13 @@ namespace osu.Game.Screens.Edit.Timing if (beatLength != timingPoint.BeatLength) { beatLength = timingPoint.BeatLength; - bpm.Text = $"{timingPoint.BPM:F0}"; EarlyActivationMilliseconds = timingPoint.BeatLength / 2; - bpmRatio = (float)Interpolation.ApplyEasing(Easing.OutQuad, Math.Clamp((timingPoint.BPM - 30) / 480, 0, 1)); + float bpmRatio = (float)Interpolation.ApplyEasing(Easing.OutQuad, Math.Clamp((timingPoint.BPM - 30) / 480, 0, 1)); - weight.MoveToY((float)Interpolation.Lerp(0.07f, 0.9f, bpmRatio), 600, Easing.OutQuint); + weight.MoveToY((float)Interpolation.Lerp(0.1f, 0.83f, bpmRatio), 600, Easing.OutQuint); + this.TransformBindableTo(interpolatedBpm, (int)timingPoint.BPM, 600, Easing.OutQuint); } if (BeatSyncClock?.IsRunning != true && isSwinging) @@ -293,7 +302,7 @@ namespace osu.Game.Screens.Edit.Timing { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - float angle = (float)Interpolation.Lerp(30, 5, bpmRatio); + const float angle = 27.5f; if (!IsBeatSyncedWithTrack) return; From 4712e512d71810e0be81428275382372eca91fac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 16:48:07 +0900 Subject: [PATCH 1174/1276] Apply edge smoothness and make weight more accented --- osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index 4ab731c897..bdab427c58 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -140,6 +140,7 @@ namespace osu.Game.Screens.Edit.Timing new Triangle { Name = @"Main body", + EdgeSmoothness = new Vector2(1), Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Size = triangleSize, @@ -171,7 +172,7 @@ namespace osu.Game.Screens.Edit.Timing Name = @"Weight", Anchor = Anchor.TopCentre, Origin = Anchor.Centre, - Colour = overlayColourProvider.Colour1, + Colour = overlayColourProvider.Colour0, Size = new Vector2(10), Rotation = 180, RelativePositionAxes = Axes.Y, @@ -182,11 +183,13 @@ namespace osu.Game.Screens.Edit.Timing { RelativeSizeAxes = Axes.Both, Shear = new Vector2(0.2f, 0), + EdgeSmoothness = new Vector2(1), }, new Box { RelativeSizeAxes = Axes.Both, Shear = new Vector2(-0.2f, 0), + EdgeSmoothness = new Vector2(1), }, } }, @@ -236,6 +239,7 @@ namespace osu.Game.Screens.Edit.Timing Origin = Anchor.BottomCentre, Size = triangleSize, Colour = overlayColourProvider.Background2, + EdgeSmoothness = new Vector2(1), Alpha = 0.8f }, } From cf97f4e409aa57e7883b0a36d037ae588c4683b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 16:55:07 +0900 Subject: [PATCH 1175/1276] Add centre marker --- osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index bdab427c58..a99468fbff 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -119,6 +119,7 @@ namespace osu.Game.Screens.Edit.Timing { const float taper = 25; const float swing_vertical_offset = -23; + const float lower_cover_height = 32; var triangleSize = new Vector2(90, 120 + taper); @@ -148,6 +149,17 @@ namespace osu.Game.Screens.Edit.Timing }, }, }, + new Circle + { + Name = "Centre marker", + Colour = overlayColourProvider.Background5, + RelativeSizeAxes = Axes.Y, + Width = 2, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -(lower_cover_height + 3), + Height = 0.65f, + }, swing = new Container { Name = @"Swing", @@ -228,9 +240,9 @@ namespace osu.Game.Screens.Edit.Timing Name = @"Lower cover", Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, Masking = true, - Height = 0.28f, + Height = lower_cover_height, Children = new Drawable[] { new Triangle From 2e21d75b101d4aabb3a2dba05ef2e7306c6976ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 17:12:35 +0900 Subject: [PATCH 1176/1276] Move metronome into own class and rename to avoid conflict with mod sounds --- .../Mods/TestSceneOsuModMuted.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- .../Editing/TestSceneTapTimingControl.cs | 6 +- .../Mods/{Metronome.cs => MetronomeBeat.cs} | 4 +- osu.Game/Rulesets/Mods/ModMuted.cs | 6 +- .../Screens/Edit/Timing/MetronomeDisplay.cs | 255 +++++++++++++++++ .../Screens/Edit/Timing/TapTimingControl.cs | 261 +----------------- 7 files changed, 277 insertions(+), 259 deletions(-) rename osu.Game/Rulesets/Mods/{Metronome.cs => MetronomeBeat.cs} (95%) create mode 100644 osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs index c14dc78f38..e08d66fa31 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods MuteComboCount = { Value = 0 }, }, PassCondition = () => Beatmap.Value.Track.AggregateVolume.Value == 0.0 && - Player.ChildrenOfType().SingleOrDefault()?.AggregateVolume.Value == 1.0, + Player.ChildrenOfType().SingleOrDefault()?.AggregateVolume.Value == 1.0, }); /// diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 5b121f4673..c58c624f5c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -339,7 +339,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + drawableRuleset.Overlays.Add(new MetronomeBeat(drawableRuleset.Beatmap.HitObjects.First().StartTime)); } #endregion diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs index 9b2618df5b..de441995b5 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("click tap button", () => { control.ChildrenOfType() - .First(b => b.Text == "Tap to beat") + .Last() .TriggerClick(); }); @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("click reset button", () => { control.ChildrenOfType() - .First(b => b.Text == "Reset") + .First() .TriggerClick(); }); @@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("click tap button", () => { control.ChildrenOfType() - .First(b => b.Text == "Tap to beat") + .Last() .TriggerClick(); }); diff --git a/osu.Game/Rulesets/Mods/Metronome.cs b/osu.Game/Rulesets/Mods/MetronomeBeat.cs similarity index 95% rename from osu.Game/Rulesets/Mods/Metronome.cs rename to osu.Game/Rulesets/Mods/MetronomeBeat.cs index b85a341577..c7a8b02130 100644 --- a/osu.Game/Rulesets/Mods/Metronome.cs +++ b/osu.Game/Rulesets/Mods/MetronomeBeat.cs @@ -11,14 +11,14 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Mods { - public class Metronome : BeatSyncedContainer, IAdjustableAudioComponent + public class MetronomeBeat : BeatSyncedContainer, IAdjustableAudioComponent { private readonly double firstHitTime; private readonly PausableSkinnableSound sample; /// Start time of the first hit object, used for providing a count down. - public Metronome(double firstHitTime) + public MetronomeBeat(double firstHitTime) { this.firstHitTime = firstHitTime; AllowMistimedEventFiring = false; diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index a7d3114f2b..84341faab7 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -79,11 +79,11 @@ namespace osu.Game.Rulesets.Mods { if (EnableMetronome.Value) { - Metronome metronome; + MetronomeBeat metronomeBeat; - drawableRuleset.Overlays.Add(metronome = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + drawableRuleset.Overlays.Add(metronomeBeat = new MetronomeBeat(drawableRuleset.Beatmap.HitObjects.First().StartTime)); - metronome.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); + metronomeBeat.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); } if (AffectsHitSounds.Value) diff --git a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs new file mode 100644 index 0000000000..fdd5bd1e4e --- /dev/null +++ b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs @@ -0,0 +1,255 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Screens.Edit.Timing +{ + public class MetronomeDisplay : BeatSyncedContainer + { + private Container swing; + + private OsuSpriteText bpmText; + + private Drawable weight; + private Drawable stick; + + [Resolved] + private OverlayColourProvider overlayColourProvider { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + const float taper = 25; + const float swing_vertical_offset = -23; + const float lower_cover_height = 32; + + var triangleSize = new Vector2(90, 120 + taper); + + Margin = new MarginPadding(10); + + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Container + { + Name = @"Taper adjust", + Masking = true, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(triangleSize.X, triangleSize.Y - taper), + Children = new Drawable[] + { + new Triangle + { + Name = @"Main body", + EdgeSmoothness = new Vector2(1), + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = triangleSize, + Colour = overlayColourProvider.Background3, + }, + }, + }, + new Circle + { + Name = "Centre marker", + Colour = overlayColourProvider.Background5, + RelativeSizeAxes = Axes.Y, + Width = 2, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -(lower_cover_height + 3), + Height = 0.65f, + }, + swing = new Container + { + Name = @"Swing", + RelativeSizeAxes = Axes.Both, + Y = swing_vertical_offset, + Height = 0.80f, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Children = new[] + { + stick = new Circle + { + Name = @"Stick", + RelativeSizeAxes = Axes.Y, + Colour = overlayColourProvider.Colour2, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Width = 4, + }, + weight = new Container + { + Name = @"Weight", + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Colour0, + Size = new Vector2(10), + Rotation = 180, + RelativePositionAxes = Axes.Y, + Y = 0.4f, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(0.2f, 0), + EdgeSmoothness = new Vector2(1), + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(-0.2f, 0), + EdgeSmoothness = new Vector2(1), + }, + } + }, + } + }, + new Container + { + Name = @"Taper adjust", + Masking = true, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(triangleSize.X, triangleSize.Y - taper), + Children = new Drawable[] + { + new Circle + { + Name = @"Locking wedge", + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Background1, + Size = new Vector2(8), + } + }, + }, + new Circle + { + Name = @"Swing connection point", + Y = swing_vertical_offset, + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Colour0, + Size = new Vector2(8) + }, + new Container + { + Name = @"Lower cover", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Masking = true, + Height = lower_cover_height, + Children = new Drawable[] + { + new Triangle + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = triangleSize, + Colour = overlayColourProvider.Background2, + EdgeSmoothness = new Vector2(1), + Alpha = 0.8f + }, + } + }, + bpmText = new OsuSpriteText + { + Name = @"BPM display", + Colour = overlayColourProvider.Content1, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -3, + }, + }; + } + + private double beatLength; + + private TimingControlPoint timingPoint; + + private bool isSwinging; + + private readonly BindableInt interpolatedBpm = new BindableInt(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + interpolatedBpm.BindValueChanged(bpm => bpmText.Text = bpm.NewValue.ToString()); + } + + protected override void Update() + { + base.Update(); + + timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(BeatSyncClock.CurrentTime); + + if (beatLength != timingPoint.BeatLength) + { + beatLength = timingPoint.BeatLength; + + EarlyActivationMilliseconds = timingPoint.BeatLength / 2; + + float bpmRatio = (float)Interpolation.ApplyEasing(Easing.OutQuad, Math.Clamp((timingPoint.BPM - 30) / 480, 0, 1)); + + weight.MoveToY((float)Interpolation.Lerp(0.1f, 0.83f, bpmRatio), 600, Easing.OutQuint); + this.TransformBindableTo(interpolatedBpm, (int)timingPoint.BPM, 600, Easing.OutQuint); + } + + if (BeatSyncClock?.IsRunning != true && isSwinging) + { + swing.ClearTransforms(true); + + using (swing.BeginDelayedSequence(350)) + { + swing.RotateTo(0, 1000, Easing.OutQuint); + stick.FadeColour(overlayColourProvider.Colour2, 1000, Easing.OutQuint); + } + + isSwinging = false; + } + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + const float angle = 27.5f; + + if (!IsBeatSyncedWithTrack) + return; + + isSwinging = true; + + float currentAngle = swing.Rotation; + float targetAngle = currentAngle > 0 ? -angle : angle; + + swing.RotateTo(targetAngle, beatLength, Easing.InOutQuad); + + if (currentAngle != 0 && Math.Abs(currentAngle - targetAngle) > angle * 1.8f && isSwinging) + { + using (stick.BeginDelayedSequence(beatLength / 2)) + stick.FlashColour(overlayColourProvider.Content1, beatLength, Easing.OutQuint); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index a99468fbff..1b0f0a3f5e 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -1,21 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; -using osuTK; namespace osu.Game.Screens.Edit.Timing { @@ -24,6 +18,9 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private EditorClock editorClock { get; set; } + [Resolved] + private Bindable selectedGroup { get; set; } + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, OsuColour colours) { @@ -52,7 +49,11 @@ namespace osu.Game.Screens.Edit.Timing { new Drawable[] { - new Metronome() + new MetronomeDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } }, new Drawable[] { @@ -74,7 +75,7 @@ namespace osu.Game.Screens.Edit.Timing { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Text = "Tap to beat", + Text = "Play from start", RelativeSizeAxes = Axes.X, BackgroundColour = colourProvider.Background1, Width = 0.68f, @@ -90,252 +91,14 @@ namespace osu.Game.Screens.Edit.Timing private void tap() { - if (!editorClock.IsRunning) - { - editorClock.Seek(0); - editorClock.Start(); - } + editorClock.Seek(selectedGroup.Value.Time); + editorClock.Start(); } private void reset() { editorClock.Stop(); - } - - private class Metronome : BeatSyncedContainer - { - private Container swing; - - private OsuSpriteText bpmText; - - private Drawable weight; - private Drawable stick; - - [Resolved] - private OverlayColourProvider overlayColourProvider { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - const float taper = 25; - const float swing_vertical_offset = -23; - const float lower_cover_height = 32; - - var triangleSize = new Vector2(90, 120 + taper); - - Margin = new MarginPadding(10); - - AutoSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - new Container - { - Name = @"Taper adjust", - Masking = true, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(triangleSize.X, triangleSize.Y - taper), - Children = new Drawable[] - { - new Triangle - { - Name = @"Main body", - EdgeSmoothness = new Vector2(1), - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = triangleSize, - Colour = overlayColourProvider.Background3, - }, - }, - }, - new Circle - { - Name = "Centre marker", - Colour = overlayColourProvider.Background5, - RelativeSizeAxes = Axes.Y, - Width = 2, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Y = -(lower_cover_height + 3), - Height = 0.65f, - }, - swing = new Container - { - Name = @"Swing", - RelativeSizeAxes = Axes.Both, - Y = swing_vertical_offset, - Height = 0.80f, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Children = new[] - { - stick = new Circle - { - Name = @"Stick", - RelativeSizeAxes = Axes.Y, - Colour = overlayColourProvider.Colour2, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Width = 4, - }, - weight = new Container - { - Name = @"Weight", - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Colour = overlayColourProvider.Colour0, - Size = new Vector2(10), - Rotation = 180, - RelativePositionAxes = Axes.Y, - Y = 0.4f, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Shear = new Vector2(0.2f, 0), - EdgeSmoothness = new Vector2(1), - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Shear = new Vector2(-0.2f, 0), - EdgeSmoothness = new Vector2(1), - }, - } - }, - } - }, - new Container - { - Name = @"Taper adjust", - Masking = true, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(triangleSize.X, triangleSize.Y - taper), - Children = new Drawable[] - { - new Circle - { - Name = @"Locking wedge", - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Colour = overlayColourProvider.Background1, - Size = new Vector2(8), - } - }, - }, - new Circle - { - Name = @"Swing connection point", - Y = swing_vertical_offset, - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - Colour = overlayColourProvider.Colour0, - Size = new Vector2(8) - }, - new Container - { - Name = @"Lower cover", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Masking = true, - Height = lower_cover_height, - Children = new Drawable[] - { - new Triangle - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = triangleSize, - Colour = overlayColourProvider.Background2, - EdgeSmoothness = new Vector2(1), - Alpha = 0.8f - }, - } - }, - bpmText = new OsuSpriteText - { - Name = @"BPM display", - Colour = overlayColourProvider.Content1, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Y = -3, - }, - }; - } - - private double beatLength; - - private TimingControlPoint timingPoint; - - private bool isSwinging; - - private readonly BindableInt interpolatedBpm = new BindableInt(); - - protected override void LoadComplete() - { - base.LoadComplete(); - - interpolatedBpm.BindValueChanged(bpm => bpmText.Text = bpm.NewValue.ToString()); - } - - protected override void Update() - { - base.Update(); - - timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(BeatSyncClock.CurrentTime); - - if (beatLength != timingPoint.BeatLength) - { - beatLength = timingPoint.BeatLength; - - EarlyActivationMilliseconds = timingPoint.BeatLength / 2; - - float bpmRatio = (float)Interpolation.ApplyEasing(Easing.OutQuad, Math.Clamp((timingPoint.BPM - 30) / 480, 0, 1)); - - weight.MoveToY((float)Interpolation.Lerp(0.1f, 0.83f, bpmRatio), 600, Easing.OutQuint); - this.TransformBindableTo(interpolatedBpm, (int)timingPoint.BPM, 600, Easing.OutQuint); - } - - if (BeatSyncClock?.IsRunning != true && isSwinging) - { - swing.ClearTransforms(true); - - using (swing.BeginDelayedSequence(350)) - { - swing.RotateTo(0, 1000, Easing.OutQuint); - stick.FadeColour(overlayColourProvider.Colour2, 1000, Easing.OutQuint); - } - - isSwinging = false; - } - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - const float angle = 27.5f; - - if (!IsBeatSyncedWithTrack) - return; - - isSwinging = true; - - float currentAngle = swing.Rotation; - float targetAngle = currentAngle > 0 ? -angle : angle; - - swing.RotateTo(targetAngle, beatLength, Easing.InOutQuad); - - if (currentAngle != 0 && Math.Abs(currentAngle - targetAngle) > angle * 1.8f && isSwinging) - { - using (stick.BeginDelayedSequence(beatLength / 2)) - stick.FlashColour(overlayColourProvider.Content1, beatLength, Easing.OutQuint); - } - } + editorClock.Seek(selectedGroup.Value.Time); } } } From 82eb5fd2c9007945d7432e22433ebeded94b7066 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 17:21:56 +0900 Subject: [PATCH 1177/1276] Add centre line on weight --- osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs index fdd5bd1e4e..5429165fe0 100644 --- a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Utils; @@ -99,7 +100,6 @@ namespace osu.Game.Screens.Edit.Timing Name = @"Weight", Anchor = Anchor.TopCentre, Origin = Anchor.Centre, - Colour = overlayColourProvider.Colour0, Size = new Vector2(10), Rotation = 180, RelativePositionAxes = Axes.Y, @@ -110,14 +110,25 @@ namespace osu.Game.Screens.Edit.Timing { RelativeSizeAxes = Axes.Both, Shear = new Vector2(0.2f, 0), + Colour = overlayColourProvider.Colour1, EdgeSmoothness = new Vector2(1), }, new Box { RelativeSizeAxes = Axes.Both, Shear = new Vector2(-0.2f, 0), + Colour = overlayColourProvider.Colour1, EdgeSmoothness = new Vector2(1), }, + new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = ColourInfo.GradientVertical(overlayColourProvider.Colour1, overlayColourProvider.Colour0), + RelativeSizeAxes = Axes.Y, + Width = 1, + Height = 0.9f + }, } }, } From b29172ea45fea63d8e48b85aee346d9d607f0dfa Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 19 May 2022 19:55:51 +0900 Subject: [PATCH 1178/1276] Increase HP lost for misses --- osu.Game/Rulesets/Judgements/Judgement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index 4fe34e15fd..99dce82ec2 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Judgements return -DEFAULT_MAX_HEALTH_INCREASE; case HitResult.Miss: - return -DEFAULT_MAX_HEALTH_INCREASE; + return -DEFAULT_MAX_HEALTH_INCREASE * 2; case HitResult.Meh: return DEFAULT_MAX_HEALTH_INCREASE * 0.05; From c78d90ccbdc92bb090c9840934ce70986c409a08 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 May 2022 14:43:07 +0300 Subject: [PATCH 1179/1276] Refactor track transferring logic for ability to override and disallow --- osu.Game/Beatmaps/WorkingBeatmap.cs | 16 ++++++++++++---- osu.Game/Overlays/MusicController.cs | 9 +-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index bb64ec796c..a1a8306b76 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -126,11 +126,19 @@ namespace osu.Game.Beatmaps } /// - /// Transfer a valid audio track into this working beatmap. Used as an optimisation to avoid reload / track swap - /// across difficulties in the same beatmap set. + /// Attempts to transfer the audio track to a target working beatmap, if valid for transferring. + /// Used as an optimisation to avoid reload / track swap across difficulties in the same beatmap set. /// - /// The track to transfer. - public void TransferTrack([NotNull] Track track) => this.track = track ?? throw new ArgumentNullException(nameof(track)); + /// The target working beatmap to transfer this track to. + /// Whether the track is valid and has been transferred to this working beatmap. + public virtual bool TryTransferTrack([NotNull] WorkingBeatmap target) + { + if (BeatmapInfo?.AudioEquals(target.BeatmapInfo) != true) + return false; + + target.track = track; + return true; + } /// /// Get the loaded audio track instance. must have first been called. diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 65b06eb864..509da4302d 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -290,15 +290,8 @@ namespace osu.Game.Overlays current = newWorking; - if (!audioEquals || CurrentTrack.IsDummyDevice) - { + if (lastWorking == null || !lastWorking.TryTransferTrack(current)) changeTrack(); - } - else - { - // transfer still valid track to new working beatmap - current.TransferTrack(lastWorking.Track); - } TrackChanged?.Invoke(current, direction); From cef99fd0205ba17f515a95530adb2ad0a6296bed Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 May 2022 14:43:50 +0300 Subject: [PATCH 1180/1276] Disallow transferring track from test `WorkingBeatmap`s which have local stores --- osu.Game.Tests/WaveformTestBeatmap.cs | 7 +++++++ osu.Game/Tests/Visual/OsuTestScene.cs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index 9c85fa0c9c..ab7bf7fb73 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -59,6 +59,13 @@ namespace osu.Game.Tests protected override Track GetBeatmapTrack() => trackStore.Get(firstAudioFile); + public override bool TryTransferTrack(WorkingBeatmap target) + { + // Our track comes from a local track store that's disposed on finalizer, + // therefore it's unsafe to transfer it to another working beatmap. + return false; + } + private string firstAudioFile { get diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index f2d280417e..1582bdfca4 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -373,6 +373,13 @@ namespace osu.Game.Tests.Visual protected override Track GetBeatmapTrack() => track; + public override bool TryTransferTrack(WorkingBeatmap target) + { + // Our track comes from a local track store that's disposed on finalizer, + // therefore it's unsafe to transfer it to another working beatmap. + return false; + } + public class TrackVirtualStore : AudioCollectionManager, ITrackStore { private readonly IFrameBasedClock referenceClock; From abf9039109389dd8b869fed1c2778dedd0c76b5c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 May 2022 14:44:07 +0300 Subject: [PATCH 1181/1276] Use `==` instead of `??` --- osu.Game/Overlays/MusicController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 509da4302d..aa09ff6b97 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -267,7 +267,7 @@ namespace osu.Game.Overlays TrackChangeDirection direction = TrackChangeDirection.None; - bool audioEquals = newWorking?.BeatmapInfo?.AudioEquals(current?.BeatmapInfo) ?? false; + bool audioEquals = newWorking?.BeatmapInfo?.AudioEquals(current?.BeatmapInfo) == true; if (current != null) { From c3bfbe888003f733137617e8629725bdadd1c506 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 23:23:51 +0900 Subject: [PATCH 1182/1276] Allow `BeatSyncedContainer` to prefer `EditorBeatmap` when available --- .../TestSceneBeatSyncedContainer.cs | 19 +------------ .../Containers/BeatSyncedContainer.cs | 28 +++++++++++++------ 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index ede89c6096..1881f6d718 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -258,24 +258,7 @@ namespace osu.Game.Tests.Visual.UserInterface }; } - protected override void LoadComplete() - { - base.LoadComplete(); - - Beatmap.BindValueChanged(_ => - { - timingPointCount.Value = 0; - currentTimingPoint.Value = 0; - beatCount.Value = 0; - currentBeat.Value = 0; - beatsPerMinute.Value = 0; - adjustedBeatLength.Value = 0; - timeUntilNextBeat.Value = 0; - timeSinceLastBeat.Value = 0; - }, true); - } - - private List timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ToList(); + private List timingPoints => Beatmap.ControlPointInfo.TimingPoints.ToList(); private TimingControlPoint getNextTimingPoint(TimingControlPoint current) { diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 2024d18570..48936f7aa9 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Screens.Edit; using osu.Game.Screens.Play; namespace osu.Game.Graphics.Containers @@ -79,20 +80,31 @@ namespace osu.Game.Graphics.Containers } [Resolved] - protected IBindable Beatmap { get; private set; } + private IBindable beatmap { get; set; } [Resolved(canBeNull: true)] protected GameplayClock GameplayClock { get; private set; } + [Resolved(canBeNull: true)] + protected EditorBeatmap EditorBeatmap { get; private set; } + + [Resolved(canBeNull: true)] + protected EditorClock EditorClock { get; private set; } + + protected IBeatmap Beatmap => EditorBeatmap ?? beatmap?.Value.Beatmap; + protected IClock BeatSyncClock { get { + if (EditorClock != null) + return EditorClock; + if (GameplayClock != null) return GameplayClock; - if (Beatmap.Value.TrackLoaded) - return Beatmap.Value.Track; + if (beatmap.Value.TrackLoaded) + return beatmap.Value.Track; return null; } @@ -101,7 +113,6 @@ namespace osu.Game.Graphics.Containers protected override void Update() { ITrack track = null; - IBeatmap beatmap = null; TimingControlPoint timingPoint; EffectControlPoint effectPoint; @@ -113,10 +124,9 @@ namespace osu.Game.Graphics.Containers double currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; - if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded) + if (this.beatmap.Value.TrackLoaded && this.beatmap.Value.BeatmapLoaded) { - track = Beatmap.Value.Track; - beatmap = Beatmap.Value.Beatmap; + track = this.beatmap.Value.Track; } IsBeatSyncedWithTrack = beatmap != null && clock.IsRunning && track?.Length > 0; @@ -125,8 +135,8 @@ namespace osu.Game.Graphics.Containers { Debug.Assert(beatmap != null); - timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); - effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); + timingPoint = Beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); + effectPoint = Beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); } else { From 8db80b92bb04f642e58e95de70c6309e2be44876 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 23:24:21 +0900 Subject: [PATCH 1183/1276] Fix metronome not using `EditorBeatmap` causing editor updates to not propagate immediately --- osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs index 5429165fe0..d51d7b9c58 100644 --- a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Globalization; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Bindables; @@ -30,6 +31,9 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private OverlayColourProvider overlayColourProvider { get; set; } + [Resolved] + private EditorBeatmap editorBeatmap { get; set; } + [BackgroundDependencyLoader] private void load() { @@ -205,14 +209,14 @@ namespace osu.Game.Screens.Edit.Timing { base.LoadComplete(); - interpolatedBpm.BindValueChanged(bpm => bpmText.Text = bpm.NewValue.ToString()); + interpolatedBpm.BindValueChanged(bpm => bpmText.Text = bpm.NewValue.ToString(CultureInfo.CurrentCulture)); } protected override void Update() { base.Update(); - timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(BeatSyncClock.CurrentTime); + timingPoint = editorBeatmap.ControlPointInfo.TimingPointAt(BeatSyncClock.CurrentTime); if (beatLength != timingPoint.BeatLength) { @@ -223,7 +227,7 @@ namespace osu.Game.Screens.Edit.Timing float bpmRatio = (float)Interpolation.ApplyEasing(Easing.OutQuad, Math.Clamp((timingPoint.BPM - 30) / 480, 0, 1)); weight.MoveToY((float)Interpolation.Lerp(0.1f, 0.83f, bpmRatio), 600, Easing.OutQuint); - this.TransformBindableTo(interpolatedBpm, (int)timingPoint.BPM, 600, Easing.OutQuint); + this.TransformBindableTo(interpolatedBpm, (int)Math.Round(timingPoint.BPM), 600, Easing.OutQuint); } if (BeatSyncClock?.IsRunning != true && isSwinging) From a2950d9d4e096ca6d09506e0ab01736651e9100f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 May 2022 23:49:03 +0900 Subject: [PATCH 1184/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b984f528fe..27fcdd4f6e 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7f149b4e35..53a4753223 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 932421a705..2e861faa1f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From de8aedf3484229b27920873abd4bc02a50bc81cd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 May 2022 17:29:37 +0300 Subject: [PATCH 1185/1276] Add failing test case --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 644a333fcf..995274e722 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Globalization; using System.Linq; using JetBrains.Annotations; using NUnit.Framework; @@ -127,6 +126,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("check info labels count", () => infoWedge.Info.ChildrenOfType().Count() == expectedCount); } + [SetUpSteps] + public void SetUpSteps() + { + AddStep("reset mods", () => SelectedMods.SetDefault()); + } + [Test] public void TestNullBeatmap() { @@ -147,24 +152,43 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestBPMUpdates() { - const float bpm = 120; + const double bpm = 120; IBeatmap beatmap = createTestBeatmap(new OsuRuleset().RulesetInfo); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 60 * 1000 / bpm }); OsuModDoubleTime doubleTime = null; selectBeatmap(beatmap); - checkDisplayedBPM(bpm); + checkDisplayedBPM($"{bpm}"); AddStep("select DT", () => SelectedMods.Value = new[] { doubleTime = new OsuModDoubleTime() }); - checkDisplayedBPM(bpm * 1.5f); + checkDisplayedBPM($"{bpm * 1.5f}"); AddStep("change DT rate", () => doubleTime.SpeedChange.Value = 2); - checkDisplayedBPM(bpm * 2); + checkDisplayedBPM($"{bpm * 2}"); + } - void checkDisplayedBPM(float target) => - AddUntilStep($"displayed bpm is {target}", () => this.ChildrenOfType().Any( - label => label.Statistic.Name == "BPM" && label.Statistic.Content == target.ToString(CultureInfo.InvariantCulture))); + [TestCase(120, 125, "120-125 (mostly 120)")] + [TestCase(120, 120.6, "120-121 (mostly 120)")] + [TestCase(120, 120.4, "120")] + public void TestVaryingBPM(double commonBpm, double otherBpm, string expectedDisplay) + { + IBeatmap beatmap = createTestBeatmap(new OsuRuleset().RulesetInfo); + beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 60 * 1000 / commonBpm }); + beatmap.ControlPointInfo.Add(100, new TimingControlPoint { BeatLength = 60 * 1000 / otherBpm }); + beatmap.ControlPointInfo.Add(200, new TimingControlPoint { BeatLength = 60 * 1000 / commonBpm }); + + selectBeatmap(beatmap); + checkDisplayedBPM(expectedDisplay); + } + + private void checkDisplayedBPM(string target) + { + AddUntilStep($"displayed bpm is {target}", () => + { + var label = this.ChildrenOfType().Single(l => l.Statistic.Name == "BPM"); + return label.Statistic.Content == target; + }); } private void setRuleset(RulesetInfo rulesetInfo) From 596853da8feb59a5f2c7252e1f0de11763979ad0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 May 2022 17:43:40 +0300 Subject: [PATCH 1186/1276] Fix song select potentially displaying BPM range with equal min/max values --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 7db1016f62..55ade6fa33 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -12,7 +12,6 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; @@ -416,13 +415,13 @@ namespace osu.Game.Screens.Select foreach (var mod in mods.Value.OfType()) rate = mod.ApplyToRate(0, rate); - double bpmMax = beatmap.ControlPointInfo.BPMMaximum * rate; - double bpmMin = beatmap.ControlPointInfo.BPMMinimum * rate; - double mostCommonBPM = 60000 / beatmap.GetMostCommonBeatLength() * rate; + int bpmMax = (int)Math.Round(beatmap.ControlPointInfo.BPMMaximum * rate); + int bpmMin = (int)Math.Round(beatmap.ControlPointInfo.BPMMinimum * rate); + int mostCommonBPM = (int)Math.Round(60000 / beatmap.GetMostCommonBeatLength() * rate); - string labelText = Precision.AlmostEquals(bpmMin, bpmMax) - ? $"{bpmMin:0}" - : $"{bpmMin:0}-{bpmMax:0} (mostly {mostCommonBPM:0})"; + string labelText = bpmMin == bpmMax + ? $"{bpmMin}" + : $"{bpmMin}-{bpmMax} (mostly {mostCommonBPM})"; bpmLabelContainer.Child = new InfoLabel(new BeatmapStatistic { From 84a3cee452364173ed73d405e3a6caade62263b7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 May 2022 17:47:45 +0300 Subject: [PATCH 1187/1276] Apply rate multiplier outside BPM rounding --- .../Visual/SongSelect/TestSceneBeatmapInfoWedge.cs | 13 +++++++++---- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 995274e722..030055903f 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -168,16 +168,21 @@ namespace osu.Game.Tests.Visual.SongSelect checkDisplayedBPM($"{bpm * 2}"); } - [TestCase(120, 125, "120-125 (mostly 120)")] - [TestCase(120, 120.6, "120-121 (mostly 120)")] - [TestCase(120, 120.4, "120")] - public void TestVaryingBPM(double commonBpm, double otherBpm, string expectedDisplay) + [TestCase(120, 125, null, "120-125 (mostly 120)")] + [TestCase(120, 120.6, null, "120-121 (mostly 120)")] + [TestCase(120, 120.4, null, "120")] + [TestCase(120, 120.6, "DT", "180-182 (mostly 180)")] + [TestCase(120, 120.4, "DT", "180")] + public void TestVaryingBPM(double commonBpm, double otherBpm, string mod, string expectedDisplay) { IBeatmap beatmap = createTestBeatmap(new OsuRuleset().RulesetInfo); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 60 * 1000 / commonBpm }); beatmap.ControlPointInfo.Add(100, new TimingControlPoint { BeatLength = 60 * 1000 / otherBpm }); beatmap.ControlPointInfo.Add(200, new TimingControlPoint { BeatLength = 60 * 1000 / commonBpm }); + if (mod != null) + AddStep($"select {mod}", () => SelectedMods.Value = new[] { Ruleset.Value.CreateInstance().CreateModFromAcronym(mod) }); + selectBeatmap(beatmap); checkDisplayedBPM(expectedDisplay); } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 55ade6fa33..d98238f518 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -415,9 +415,9 @@ namespace osu.Game.Screens.Select foreach (var mod in mods.Value.OfType()) rate = mod.ApplyToRate(0, rate); - int bpmMax = (int)Math.Round(beatmap.ControlPointInfo.BPMMaximum * rate); - int bpmMin = (int)Math.Round(beatmap.ControlPointInfo.BPMMinimum * rate); - int mostCommonBPM = (int)Math.Round(60000 / beatmap.GetMostCommonBeatLength() * rate); + int bpmMax = (int)Math.Round(Math.Round(beatmap.ControlPointInfo.BPMMaximum) * rate); + int bpmMin = (int)Math.Round(Math.Round(beatmap.ControlPointInfo.BPMMinimum) * rate); + int mostCommonBPM = (int)Math.Round(Math.Round(60000 / beatmap.GetMostCommonBeatLength()) * rate); string labelText = bpmMin == bpmMax ? $"{bpmMin}" From 7dcb88759afcf00ce46e930ce117729fe9d7416f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 May 2022 00:28:17 +0900 Subject: [PATCH 1188/1276] Remove unnecessary using statement --- osu.Game/Graphics/ScreenshotManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 1fc2633143..53f7d5791b 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.IO; using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; From 1f17652c1d55d0f42a9394af259f668961982077 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 May 2022 19:08:41 +0300 Subject: [PATCH 1189/1276] Fix test failure due to async-loading of content --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 030055903f..ef04baefa2 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -191,7 +191,7 @@ namespace osu.Game.Tests.Visual.SongSelect { AddUntilStep($"displayed bpm is {target}", () => { - var label = this.ChildrenOfType().Single(l => l.Statistic.Name == "BPM"); + var label = infoWedge.DisplayedContent.ChildrenOfType().Single(l => l.Statistic.Name == "BPM"); return label.Statistic.Content == target; }); } From d3f115bfe2b321a9309be155995d44e6fc0423a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 May 2022 13:53:58 +0900 Subject: [PATCH 1190/1276] Remove redundant qualifiers --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 48936f7aa9..bd46a20434 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -124,9 +124,9 @@ namespace osu.Game.Graphics.Containers double currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; - if (this.beatmap.Value.TrackLoaded && this.beatmap.Value.BeatmapLoaded) + if (beatmap.Value.TrackLoaded && beatmap.Value.BeatmapLoaded) { - track = this.beatmap.Value.Track; + track = beatmap.Value.Track; } IsBeatSyncedWithTrack = beatmap != null && clock.IsRunning && track?.Length > 0; From 5bf17dde9fd244c76a61960f6689741128ed6a60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 May 2022 14:35:07 +0900 Subject: [PATCH 1191/1276] Fix missing `MutatePath` calls --- osu.Game/IO/WrappedStorage.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index d4d9d531fa..a465c215e2 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -67,12 +67,12 @@ namespace osu.Game.IO public override IEnumerable GetFiles(string path, string pattern = "*") => ToLocalRelative(UnderlyingStorage.GetFiles(MutatePath(path), pattern)); - public override Stream CreateFileSafely(string path) => UnderlyingStorage.CreateFileSafely(path); + public override Stream CreateFileSafely(string path) => UnderlyingStorage.CreateFileSafely(MutatePath(path)); public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) => UnderlyingStorage.GetStream(MutatePath(path), access, mode); - public override void Move(string from, string to) => UnderlyingStorage.Move(from, to); + public override void Move(string from, string to) => UnderlyingStorage.Move(MutatePath(from), MutatePath(to)); public override bool OpenFileExternally(string filename) => UnderlyingStorage.OpenFileExternally(MutatePath(filename)); From 72386d9377412eb2ba5adef5e06ade4b389cb2ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 May 2022 15:15:52 +0900 Subject: [PATCH 1192/1276] Fix incorrect storage being used in `CreateFileSafely` implementation --- osu.Game/IO/WrappedStorage.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index a465c215e2..5887704768 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -11,7 +11,7 @@ namespace osu.Game.IO /// /// A storage which wraps another storage and delegates implementation, potentially mutating the lookup path. /// - public class WrappedStorage : Storage + public class WrappedStorage : NativeStorage // TODO: Revert to Storage inheritance after https://github.com/ppy/osu-framework/pull/5189 { protected Storage UnderlyingStorage { get; private set; } @@ -67,8 +67,6 @@ namespace osu.Game.IO public override IEnumerable GetFiles(string path, string pattern = "*") => ToLocalRelative(UnderlyingStorage.GetFiles(MutatePath(path), pattern)); - public override Stream CreateFileSafely(string path) => UnderlyingStorage.CreateFileSafely(MutatePath(path)); - public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) => UnderlyingStorage.GetStream(MutatePath(path), access, mode); From ba21556d4e9271b7f7e9185eb19b5ccec2b2901c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 May 2022 15:54:43 +0900 Subject: [PATCH 1193/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 7910100b1f..aaea784852 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 17bc056b59..5bc65ca507 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 191d1bd5ca..3597e7e5c0 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From c587d10cc3dcef9be569b2b6d453bb2b1c79b1cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 May 2022 15:55:07 +0900 Subject: [PATCH 1194/1276] Revert storage workaround --- osu.Game/IO/WrappedStorage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index 5887704768..7205ea3adb 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -11,7 +11,7 @@ namespace osu.Game.IO /// /// A storage which wraps another storage and delegates implementation, potentially mutating the lookup path. /// - public class WrappedStorage : NativeStorage // TODO: Revert to Storage inheritance after https://github.com/ppy/osu-framework/pull/5189 + public class WrappedStorage : Storage { protected Storage UnderlyingStorage { get; private set; } From a7aa36a825cc9589ee6f3cf5bf2f5792d8df09cd Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 21 May 2022 04:55:42 -0300 Subject: [PATCH 1195/1276] Add OnPressed to button --- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 13b2c37ded..61798b5413 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -15,9 +15,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.Mods; @@ -473,8 +476,19 @@ namespace osu.Game.Screens.OnlinePlay.Match /// The room to change the settings of. protected abstract RoomSettingsOverlay CreateRoomSettingsOverlay(Room room); - public class UserModSelectButton : PurpleTriangleButton + public class UserModSelectButton : PurpleTriangleButton, IKeyBindingHandler { + public virtual bool OnPressed(KeyBindingPressEvent e) + { + if (e.Action == GlobalAction.ToggleModSelection && !e.Repeat) + { + TriggerClick(); + return true; + } + return false; + } + + public virtual void OnReleased(KeyBindingReleaseEvent e) { } } protected override void Dispose(bool isDisposing) From 2bd4c126d39223990260535b2f4874ef8229e0b8 Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 21 May 2022 05:07:24 -0300 Subject: [PATCH 1196/1276] Blank line Forgot to save. --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 61798b5413..0dc6e52770 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -485,6 +485,7 @@ namespace osu.Game.Screens.OnlinePlay.Match TriggerClick(); return true; } + return false; } From 376549ce09a2570811ff8b7ea1e6ce3b1636b812 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 May 2022 21:48:51 +0900 Subject: [PATCH 1197/1276] Use `ToLocalisableString` instead of `CurrentCulture` --- osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs index d51d7b9c58..98ce9f0f46 100644 --- a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs @@ -6,6 +6,7 @@ using System.Globalization; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -209,7 +210,7 @@ namespace osu.Game.Screens.Edit.Timing { base.LoadComplete(); - interpolatedBpm.BindValueChanged(bpm => bpmText.Text = bpm.NewValue.ToString(CultureInfo.CurrentCulture)); + interpolatedBpm.BindValueChanged(bpm => bpmText.Text = bpm.NewValue.ToLocalisableString()); } protected override void Update() From 9a780bcad3b6e7e4b14fe11f3bc1f2c23c13c6d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 May 2022 21:56:05 +0900 Subject: [PATCH 1198/1276] Remove unused using --- osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs index 98ce9f0f46..b59865ac1a 100644 --- a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Globalization; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Bindables; From 466ed3c791507796151fb504ad353902a71c1f32 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 21 May 2022 16:43:31 +0300 Subject: [PATCH 1199/1276] Fix wrong return xmldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index a1a8306b76..5551e0b3e5 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -130,7 +130,7 @@ namespace osu.Game.Beatmaps /// Used as an optimisation to avoid reload / track swap across difficulties in the same beatmap set. /// /// The target working beatmap to transfer this track to. - /// Whether the track is valid and has been transferred to this working beatmap. + /// Whether the track has been transferred to the . public virtual bool TryTransferTrack([NotNull] WorkingBeatmap target) { if (BeatmapInfo?.AudioEquals(target.BeatmapInfo) != true) From a42f5ea34ec5c88b002037692029422931a4dcb8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 21 May 2022 16:50:40 +0300 Subject: [PATCH 1200/1276] Bring back virtual track condition given its cheapness Will still keep the override in `ClockBackedTestWorkingBeatmap` because it still relies on a local track store and will fail the moment it uses a non-virtual track. --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 5551e0b3e5..5fc579b47c 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -133,7 +133,7 @@ namespace osu.Game.Beatmaps /// Whether the track has been transferred to the . public virtual bool TryTransferTrack([NotNull] WorkingBeatmap target) { - if (BeatmapInfo?.AudioEquals(target.BeatmapInfo) != true) + if (BeatmapInfo?.AudioEquals(target.BeatmapInfo) != true || track.IsDummyDevice) return false; target.track = track; From a17eed64f96c2557bbf632d894694fd57bc1d00c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 21 May 2022 16:51:04 +0300 Subject: [PATCH 1201/1276] Use `Track` to ensure its loaded before transferring --- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 5fc579b47c..09072ec897 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -133,10 +133,10 @@ namespace osu.Game.Beatmaps /// Whether the track has been transferred to the . public virtual bool TryTransferTrack([NotNull] WorkingBeatmap target) { - if (BeatmapInfo?.AudioEquals(target.BeatmapInfo) != true || track.IsDummyDevice) + if (BeatmapInfo?.AudioEquals(target.BeatmapInfo) != true || Track.IsDummyDevice) return false; - target.track = track; + target.track = Track; return true; } From d73afcaf484185ac1d1102b8b2c4df4a118c98b5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 12 May 2022 17:29:28 +0300 Subject: [PATCH 1202/1276] Fix existing test coverage false-passing Also improves general test coverage to test more realisticly, in order to produce accurate results. --- .../TestSceneMultiSpectatorScreen.cs | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 703b526e8c..c048722804 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -15,13 +16,14 @@ using osu.Game.Configuration; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; -using osu.Game.Online.Rooms; using osu.Game.Rulesets.UI; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Storyboards; using osu.Game.Tests.Beatmaps.IO; +using osuTK; using osuTK.Graphics; namespace osu.Game.Tests.Visual.Multiplayer @@ -349,15 +351,27 @@ namespace osu.Game.Tests.Visual.Multiplayer } /// - /// Tests spectating with a gameplay start time set to a negative value. - /// Simulating beatmaps with high or negative time storyboard elements. + /// Tests spectating with a beatmap that has a high value. /// [Test] - public void TestNegativeGameplayStartTime() + public void TestAudioLeadIn() => testLeadIn(b => b.BeatmapInfo.AudioLeadIn = 2000); + + /// + /// Tests spectating with a beatmap that has a storyboard element with a negative start time (i.e. intro storyboard element). + /// + [Test] + public void TestIntroStoryboardElement() => testLeadIn(b => + { + var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); + sprite.TimelineGroup.Alpha.Add(Easing.None, -2000, 0, 0, 1); + b.Storyboard.GetLayer("Background").Add(sprite); + }); + + private void testLeadIn(Action applyToBeatmap = null) { start(PLAYER_1_ID); - loadSpectateScreen(false, -500); + loadSpectateScreen(false, applyToBeatmap); // to ensure negative gameplay start time does not affect spectator, send frames exactly after StartGameplay(). // (similar to real spectating sessions in which the first frames get sent between StartGameplay() and player load complete) @@ -371,14 +385,16 @@ namespace osu.Game.Tests.Visual.Multiplayer assertRunning(PLAYER_1_ID); } - private void loadSpectateScreen(bool waitForPlayerLoad = true, double? gameplayStartTime = null) + private void loadSpectateScreen(bool waitForPlayerLoad = true, Action applyToBeatmap = null) { - AddStep(!gameplayStartTime.HasValue ? "load screen" : $"load screen (start = {gameplayStartTime}ms)", () => + AddStep("load screen", () => { Beatmap.Value = beatmapManager.GetWorkingBeatmap(importedBeatmap); Ruleset.Value = importedBeatmap.Ruleset; - LoadScreen(spectatorScreen = new TestMultiSpectatorScreen(SelectedRoom.Value, playingUsers.ToArray(), gameplayStartTime)); + applyToBeatmap?.Invoke(Beatmap.Value); + + LoadScreen(spectatorScreen = new MultiSpectatorScreen(SelectedRoom.Value, playingUsers.ToArray())); }); AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded)); @@ -461,19 +477,5 @@ namespace osu.Game.Tests.Visual.Multiplayer private GameplayLeaderboardScore getLeaderboardScore(int userId) => spectatorScreen.ChildrenOfType().Single(s => s.User?.Id == userId); private int[] getPlayerIds(int count) => Enumerable.Range(PLAYER_1_ID, count).ToArray(); - - private class TestMultiSpectatorScreen : MultiSpectatorScreen - { - private readonly double? startTime; - - public TestMultiSpectatorScreen(Room room, MultiplayerRoomUser[] users, double? startTime = null) - : base(room, users) - { - this.startTime = startTime; - } - - protected override MasterGameplayClockContainer CreateMasterGameplayClockContainer(WorkingBeatmap beatmap) - => new MasterGameplayClockContainer(beatmap, 0) { StartTime = startTime ?? 0 }; - } } } From 27da293b4053d9fce0ca0cd161f32c1e7127d535 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 May 2022 00:07:11 +0300 Subject: [PATCH 1203/1276] Make catch-up spectator clocks running state immutable externally --- .../Spectate/CatchUpSpectatorPlayerClock.cs | 10 ++++++++++ .../Multiplayer/Spectate/CatchUpSyncManager.cs | 2 ++ .../Multiplayer/Spectate/ISpectatorPlayerClock.cs | 10 ++++++++++ 3 files changed, 22 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSpectatorPlayerClock.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSpectatorPlayerClock.cs index 20d12d62a3..48d0b063ff 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSpectatorPlayerClock.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSpectatorPlayerClock.cs @@ -34,6 +34,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public void Stop() => IsRunning = false; + void IAdjustableClock.Start() + { + // Our running state should only be managed by an ISyncManager, ignore calls from external sources. + } + + void IAdjustableClock.Stop() + { + // Our running state should only be managed by an ISyncManager, ignore calls from external sources. + } + public bool Seek(double position) { CurrentTime = position; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs index b8f47c16ff..b0fdeddd56 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs @@ -144,6 +144,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate // Make sure the player clock is running if it can. if (!clock.WaitingOnFrames.Value) clock.Start(); + else + clock.Stop(); if (clock.IsCatchingUp) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs index de23b4fef7..b2ecb105c2 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs @@ -11,6 +11,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate /// public interface ISpectatorPlayerClock : IFrameBasedClock, IAdjustableClock { + /// + /// Starts this . + /// + new void Start(); + + /// + /// Stops this . + /// + new void Stop(); + /// /// Whether this clock is waiting on frames to continue playback. /// From efae934e0199713368a4e9cf563cb77a6202f6a6 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 21 May 2022 17:35:31 +0100 Subject: [PATCH 1204/1276] Fix slow loading channel test --- osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 4f916eec18..e27db00003 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -397,6 +397,7 @@ namespace osu.Game.Tests.Visual.Online chatOverlay.SlowLoading = true; }); AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1)); + AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1))); AddAssert("Channel 1 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel1).LoadState == LoadState.Loading); AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2)); From 21e1576b2fccaf00eaf980265c8b6590103d4767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 21 May 2022 20:49:29 +0200 Subject: [PATCH 1205/1276] Fix appearance of sheared button borders after click The border would previously get brighter after click, but then dim instantly when the flash layer has fully faded out. The underlying issue there is https://github.com/ppy/osu-framework/issues/5191, but `ShearedButton` was placing the flashing layer incorrectly anyway, as the intent was that it should also apply to the border. --- .../Graphics/UserInterface/ShearedButton.cs | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index dea44e6d99..66c6eedd0c 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Localisation; @@ -68,6 +69,7 @@ namespace osu.Game.Graphics.UserInterface private Colour4? lighterColour; private Colour4? textColour; + private readonly Container backgroundLayer; private readonly Box flashLayer; /// @@ -85,24 +87,35 @@ namespace osu.Game.Graphics.UserInterface Height = 50; Padding = new MarginPadding { Horizontal = shear * 50 }; - Content.CornerRadius = 7; + const float corner_radius = 7; + + Content.CornerRadius = corner_radius; Content.Shear = new Vector2(shear, 0); Content.Masking = true; - Content.BorderThickness = 2; Content.Anchor = Content.Origin = Anchor.Centre; Children = new Drawable[] { - background = new Box + backgroundLayer = new Container { - RelativeSizeAxes = Axes.Both - }, - text = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.TorusAlternate.With(size: 17), - Shear = new Vector2(-shear, 0) + RelativeSizeAxes = Axes.Both, + CornerRadius = corner_radius, + Masking = true, + BorderThickness = 2, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.TorusAlternate.With(size: 17), + Shear = new Vector2(-shear, 0) + }, + } }, flashLayer = new Box { @@ -186,7 +199,7 @@ namespace osu.Game.Graphics.UserInterface } background.FadeColour(colourDark, 150, Easing.OutQuint); - Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(colourDark, colourLight), 150, Easing.OutQuint); + backgroundLayer.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(colourDark, colourLight), 150, Easing.OutQuint); if (!Enabled.Value) colourText = colourText.Opacity(0.6f); From fc25d248ad18d4af683347fe0a3b444504e4161c Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 21 May 2022 18:16:29 -0300 Subject: [PATCH 1206/1276] Test coverage + no virtual --- .../TestSceneMultiplayerMatchSubScreen.cs | 42 +++++++++++++++++++ .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index ca79fa9cb8..dd281e7738 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -178,6 +178,48 @@ namespace osu.Game.Tests.Visual.Multiplayer .SingleOrDefault(panel => !panel.Filtered.Value)?.Mod is OsuModDoubleTime); } + [Test] + public void TestModSelectKeyWithAllowedMods() + { + AddStep("add playlist item with allowed mod", () => + { + SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, + AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) } + }); + }); + + ClickButtonWhenEnabled(); + + AddUntilStep("wait for join", () => RoomJoined); + + AddStep("press toggle mod select key", () => InputManager.Key(osuTK.Input.Key.F1)); + + AddUntilStep("mod select contents loaded", + () => this.ChildrenOfType().Any() && this.ChildrenOfType().All(col => col.IsLoaded && col.ItemsLoaded)); + } + + [Test] + public void TestModSelectKeyWithNoAllowedMods() + { + AddStep("add playlist item with no allowed mods", () => + { + SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, + }); + }); + ClickButtonWhenEnabled(); + + AddUntilStep("wait for join", () => RoomJoined); + + AddStep("press toggle mod select key", () => InputManager.Key(osuTK.Input.Key.F1)); + + AddAssert("mod select contents not loaded", + () => !(this.ChildrenOfType().Any() && this.ChildrenOfType().All(col => col.IsLoaded && col.ItemsLoaded))); + } + [Test] public void TestNextPlaylistItemSelectedAfterCompletion() { diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 0dc6e52770..a612b6a0e6 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -478,7 +478,7 @@ namespace osu.Game.Screens.OnlinePlay.Match public class UserModSelectButton : PurpleTriangleButton, IKeyBindingHandler { - public virtual bool OnPressed(KeyBindingPressEvent e) + public bool OnPressed(KeyBindingPressEvent e) { if (e.Action == GlobalAction.ToggleModSelection && !e.Repeat) { @@ -489,7 +489,7 @@ namespace osu.Game.Screens.OnlinePlay.Match return false; } - public virtual void OnReleased(KeyBindingReleaseEvent e) { } + public void OnReleased(KeyBindingReleaseEvent e) { } } protected override void Dispose(bool isDisposing) From eabf57828276ce8ce2b2ad16305e54df271b34ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 22 May 2022 22:15:53 +0900 Subject: [PATCH 1207/1276] Use interface to convey beat sync information --- .../TestSceneBeatSyncedContainer.cs | 22 +++++-- .../Containers/BeatSyncedContainer.cs | 65 ++++--------------- .../Graphics/Containers/IBeatSyncProvider.cs | 26 ++++++++ osu.Game/OsuGameBase.cs | 10 ++- osu.Game/Rulesets/Mods/MetronomeBeat.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 10 ++- .../Screens/Edit/Timing/MetronomeDisplay.cs | 12 ++-- .../Play/MasterGameplayClockContainer.cs | 8 ++- osu.Game/Screens/Play/Player.cs | 9 ++- 9 files changed, 96 insertions(+), 68 deletions(-) create mode 100644 osu.Game/Graphics/Containers/IBeatSyncProvider.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 1881f6d718..3cbb7daf51 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using NUnit.Framework; using osu.Framework.Audio.Track; @@ -82,11 +83,15 @@ namespace osu.Game.Tests.Visual.UserInterface if (!allowMistimed) { - AddAssert("trigger is near beat length", () => lastActuationTime != null && lastBeatIndex != null && Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, BeatSyncedContainer.MISTIMED_ALLOWANCE)); + AddAssert("trigger is near beat length", + () => lastActuationTime != null && lastBeatIndex != null && Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, + BeatSyncedContainer.MISTIMED_ALLOWANCE)); } else { - AddAssert("trigger is not near beat length", () => lastActuationTime != null && lastBeatIndex != null && !Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, BeatSyncedContainer.MISTIMED_ALLOWANCE)); + AddAssert("trigger is not near beat length", + () => lastActuationTime != null && lastBeatIndex != null && !Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, + lastActuationTime.Value, BeatSyncedContainer.MISTIMED_ALLOWANCE)); } } @@ -258,7 +263,7 @@ namespace osu.Game.Tests.Visual.UserInterface }; } - private List timingPoints => Beatmap.ControlPointInfo.TimingPoints.ToList(); + private List timingPoints => BeatSyncSource.ControlPoints?.TimingPoints.ToList(); private TimingControlPoint getNextTimingPoint(TimingControlPoint current) { @@ -275,7 +280,11 @@ namespace osu.Game.Tests.Visual.UserInterface if (timingPoints.Count == 0) return 0; if (timingPoints[^1] == current) - return (int)Math.Ceiling((BeatSyncClock.CurrentTime - current.Time) / current.BeatLength); + { + Debug.Assert(BeatSyncSource.Clock != null); + + return (int)Math.Ceiling((BeatSyncSource.Clock.CurrentTime - current.Time) / current.BeatLength); + } return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength); } @@ -283,9 +292,12 @@ namespace osu.Game.Tests.Visual.UserInterface protected override void Update() { base.Update(); + + Debug.Assert(BeatSyncSource.Clock != null); + timeUntilNextBeat.Value = TimeUntilNextBeat; timeSinceLastBeat.Value = TimeSinceLastBeat; - currentTime.Value = BeatSyncClock.CurrentTime; + currentTime.Value = BeatSyncSource.Clock.CurrentTime; } public Action NewBeat; diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index bd46a20434..953731244d 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -5,12 +5,9 @@ using System; using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Audio.Track; -using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Screens.Edit; using osu.Game.Screens.Play; namespace osu.Game.Graphics.Containers @@ -75,74 +72,38 @@ namespace osu.Game.Graphics.Containers /// protected bool IsBeatSyncedWithTrack { get; private set; } + [Resolved] + protected IBeatSyncProvider BeatSyncSource { get; private set; } + protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) { } - [Resolved] - private IBindable beatmap { get; set; } - - [Resolved(canBeNull: true)] - protected GameplayClock GameplayClock { get; private set; } - - [Resolved(canBeNull: true)] - protected EditorBeatmap EditorBeatmap { get; private set; } - - [Resolved(canBeNull: true)] - protected EditorClock EditorClock { get; private set; } - - protected IBeatmap Beatmap => EditorBeatmap ?? beatmap?.Value.Beatmap; - - protected IClock BeatSyncClock - { - get - { - if (EditorClock != null) - return EditorClock; - - if (GameplayClock != null) - return GameplayClock; - - if (beatmap.Value.TrackLoaded) - return beatmap.Value.Track; - - return null; - } - } - protected override void Update() { - ITrack track = null; - TimingControlPoint timingPoint; EffectControlPoint effectPoint; - IClock clock = BeatSyncClock; + IsBeatSyncedWithTrack = BeatSyncSource.Clock?.IsRunning == true; - if (clock == null) - return; - - double currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; - - if (beatmap.Value.TrackLoaded && beatmap.Value.BeatmapLoaded) - { - track = beatmap.Value.Track; - } - - IsBeatSyncedWithTrack = beatmap != null && clock.IsRunning && track?.Length > 0; + double currentTrackTime; if (IsBeatSyncedWithTrack) { - Debug.Assert(beatmap != null); + Debug.Assert(BeatSyncSource.ControlPoints != null); + Debug.Assert(BeatSyncSource.Clock != null); - timingPoint = Beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); - effectPoint = Beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); + currentTrackTime = BeatSyncSource.Clock.CurrentTime + EarlyActivationMilliseconds; + + timingPoint = BeatSyncSource.ControlPoints.TimingPointAt(currentTrackTime); + effectPoint = BeatSyncSource.ControlPoints.EffectPointAt(currentTrackTime); } else { // this may be the case where the beat syncing clock has been paused. // we still want to show an idle animation, so use this container's time instead. currentTrackTime = Clock.CurrentTime + EarlyActivationMilliseconds; + timingPoint = TimingControlPoint.DEFAULT; effectPoint = EffectControlPoint.DEFAULT; } @@ -172,7 +133,7 @@ namespace osu.Game.Graphics.Containers if (AllowMistimedEventFiring || Math.Abs(TimeSinceLastBeat) < MISTIMED_ALLOWANCE) { using (BeginDelayedSequence(-TimeSinceLastBeat)) - OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty); + OnNewBeat(beatIndex, timingPoint, effectPoint, BeatSyncSource.Amplitudes ?? ChannelAmplitudes.Empty); } lastBeat = beatIndex; diff --git a/osu.Game/Graphics/Containers/IBeatSyncProvider.cs b/osu.Game/Graphics/Containers/IBeatSyncProvider.cs new file mode 100644 index 0000000000..d5be50297c --- /dev/null +++ b/osu.Game/Graphics/Containers/IBeatSyncProvider.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Timing; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Graphics.Containers +{ + /// + /// Provides various data sources which allow for synchronising visuals to a known beat. + /// Primarily intended for use with . + /// + [Cached(typeof(IBeatSyncProvider))] + public interface IBeatSyncProvider + { + ControlPointInfo? ControlPoints { get; } + + IClock? Clock { get; } + + ChannelAmplitudes? Amplitudes { get; } + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 2e4758a134..52052efd5d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Development; using osu.Framework.Extensions; @@ -21,12 +22,15 @@ using osu.Framework.Input; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Framework.Platform; +using osu.Framework.Timing; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Input; using osu.Game.Input.Bindings; @@ -52,7 +56,7 @@ namespace osu.Game /// Unlike , this class will not load any kind of UI, allowing it to be used /// for provide dependencies to test cases without interfering with them. /// - public partial class OsuGameBase : Framework.Game, ICanAcceptFiles + public partial class OsuGameBase : Framework.Game, ICanAcceptFiles, IBeatSyncProvider { public const string OSU_PROTOCOL = "osu://"; @@ -552,5 +556,9 @@ namespace osu.Game if (Host != null) Host.ExceptionThrown -= onExceptionThrown; } + + ControlPointInfo IBeatSyncProvider.ControlPoints => Beatmap.Value.Beatmap.ControlPointInfo; + IClock IBeatSyncProvider.Clock => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track : (IClock)null; + ChannelAmplitudes? IBeatSyncProvider.Amplitudes => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track.CurrentAmplitudes : (ChannelAmplitudes?)null; } } diff --git a/osu.Game/Rulesets/Mods/MetronomeBeat.cs b/osu.Game/Rulesets/Mods/MetronomeBeat.cs index c7a8b02130..149af1e30a 100644 --- a/osu.Game/Rulesets/Mods/MetronomeBeat.cs +++ b/osu.Game/Rulesets/Mods/MetronomeBeat.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Mods int timeSignature = timingPoint.TimeSignature.Numerator; // play metronome from one measure before the first object. - if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) + if (BeatSyncSource.Clock?.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) return; sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 3a2b195eed..a50a70374b 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -8,6 +8,7 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,11 +20,14 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Screens; +using osu.Framework.Timing; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; @@ -53,7 +57,7 @@ namespace osu.Game.Screens.Edit { [Cached(typeof(IBeatSnapProvider))] [Cached] - public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IKeyBindingHandler, IBeatSnapProvider, ISamplePlaybackDisabler + public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IKeyBindingHandler, IBeatSnapProvider, ISamplePlaybackDisabler, IBeatSyncProvider { public override float BackgroundParallaxAmount => 0.1f; @@ -954,5 +958,9 @@ namespace osu.Game.Screens.Edit public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime); public int BeatDivisor => beatDivisor.Value; + + ControlPointInfo IBeatSyncProvider.ControlPoints => editorBeatmap.ControlPointInfo; + IClock IBeatSyncProvider.Clock => clock; + ChannelAmplitudes? IBeatSyncProvider.Amplitudes => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track.CurrentAmplitudes : (ChannelAmplitudes?)null; } } diff --git a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs index b59865ac1a..7b0b8440e6 100644 --- a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs @@ -31,9 +31,6 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private OverlayColourProvider overlayColourProvider { get; set; } - [Resolved] - private EditorBeatmap editorBeatmap { get; set; } - [BackgroundDependencyLoader] private void load() { @@ -216,7 +213,10 @@ namespace osu.Game.Screens.Edit.Timing { base.Update(); - timingPoint = editorBeatmap.ControlPointInfo.TimingPointAt(BeatSyncClock.CurrentTime); + if (BeatSyncSource.ControlPoints == null || BeatSyncSource.Clock == null) + return; + + timingPoint = BeatSyncSource.ControlPoints.TimingPointAt(BeatSyncSource.Clock.CurrentTime); if (beatLength != timingPoint.BeatLength) { @@ -230,7 +230,7 @@ namespace osu.Game.Screens.Edit.Timing this.TransformBindableTo(interpolatedBpm, (int)Math.Round(timingPoint.BPM), 600, Easing.OutQuint); } - if (BeatSyncClock?.IsRunning != true && isSwinging) + if (BeatSyncSource.Clock?.IsRunning != true && isSwinging) { swing.ClearTransforms(true); @@ -258,7 +258,7 @@ namespace osu.Game.Screens.Edit.Timing float currentAngle = swing.Rotation; float targetAngle = currentAngle > 0 ? -angle : angle; - swing.RotateTo(targetAngle, beatLength, Easing.InOutQuad); + swing.RotateTo(targetAngle, beatLength, Easing.InOutSine); if (currentAngle != 0 && Math.Abs(currentAngle - targetAngle) > angle * 1.8f && isSwinging) { diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index ea43fb1546..4ca5541362 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -12,8 +12,10 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Graphics.Containers; namespace osu.Game.Screens.Play { @@ -27,7 +29,7 @@ namespace osu.Game.Screens.Play /// /// This is intended to be used as a single controller for gameplay, or as a reference source for other s. /// - public class MasterGameplayClockContainer : GameplayClockContainer + public class MasterGameplayClockContainer : GameplayClockContainer, IBeatSyncProvider { /// /// Duration before gameplay start time required before skip button displays. @@ -250,6 +252,10 @@ namespace osu.Game.Screens.Play removeSourceClockAdjustments(); } + ControlPointInfo IBeatSyncProvider.ControlPoints => beatmap.Beatmap.ControlPointInfo; + IClock IBeatSyncProvider.Clock => GameplayClock; + ChannelAmplitudes? IBeatSyncProvider.Amplitudes => beatmap.TrackLoaded ? beatmap.Track.CurrentAmplitudes : (ChannelAmplitudes?)null; + private class HardwareCorrectionOffsetClock : FramedOffsetClock { private readonly BindableDouble pauseRateAdjust; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 51c1e6b43b..cbd9b03c32 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; @@ -16,8 +17,10 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Threading; +using osu.Framework.Timing; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.IO.Archives; @@ -38,7 +41,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play { [Cached] - public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler, ILocalUserPlayInfo + public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler, ILocalUserPlayInfo, IBeatSyncProvider { /// /// The delay upon completion of the beatmap before displaying the results screen. @@ -1108,5 +1111,9 @@ namespace osu.Game.Screens.Play IBindable ISamplePlaybackDisabler.SamplePlaybackDisabled => samplePlaybackDisabled; IBindable ILocalUserPlayInfo.IsPlaying => LocalUserPlaying; + + ControlPointInfo IBeatSyncProvider.ControlPoints => Beatmap.Value.Beatmap.ControlPointInfo; + IClock IBeatSyncProvider.Clock => GameplayClockContainer.GameplayClock; + ChannelAmplitudes? IBeatSyncProvider.Amplitudes => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track.CurrentAmplitudes : (ChannelAmplitudes?)null; } } From 3a7233bd6ed5e2a3784de1825ac58ea1ec7ae840 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 22 May 2022 22:49:24 +0900 Subject: [PATCH 1208/1276] Move interface to more appropriate namespace --- .../{Graphics/Containers => Beatmaps}/IBeatSyncProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename osu.Game/{Graphics/Containers => Beatmaps}/IBeatSyncProvider.cs (91%) diff --git a/osu.Game/Graphics/Containers/IBeatSyncProvider.cs b/osu.Game/Beatmaps/IBeatSyncProvider.cs similarity index 91% rename from osu.Game/Graphics/Containers/IBeatSyncProvider.cs rename to osu.Game/Beatmaps/IBeatSyncProvider.cs index d5be50297c..573f06ef9f 100644 --- a/osu.Game/Graphics/Containers/IBeatSyncProvider.cs +++ b/osu.Game/Beatmaps/IBeatSyncProvider.cs @@ -7,8 +7,9 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Timing; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; -namespace osu.Game.Graphics.Containers +namespace osu.Game.Beatmaps { /// /// Provides various data sources which allow for synchronising visuals to a known beat. From 007582afb9082782b1c18a61401a673198fe3370 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 22 May 2022 23:55:33 +0900 Subject: [PATCH 1209/1276] Remove unused usings resulting from namespace move --- osu.Game/OsuGameBase.cs | 1 - osu.Game/Screens/Edit/Editor.cs | 1 - osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 52052efd5d..ed2bfbc6e3 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -30,7 +30,6 @@ using osu.Game.Beatmaps.Formats; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Input; using osu.Game.Input.Bindings; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index a50a70374b..0bb3f51903 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -27,7 +27,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 4ca5541362..d87d57ec49 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -15,7 +15,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Database; -using osu.Game.Graphics.Containers; namespace osu.Game.Screens.Play { From c9ea87e6be1885fdb18bcc4e93bf21b5d0ad11e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 May 2022 00:03:42 +0900 Subject: [PATCH 1210/1276] Revert easing for now --- osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs index 7b0b8440e6..4dd7a75d4a 100644 --- a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs @@ -258,7 +258,7 @@ namespace osu.Game.Screens.Edit.Timing float currentAngle = swing.Rotation; float targetAngle = currentAngle > 0 ? -angle : angle; - swing.RotateTo(targetAngle, beatLength, Easing.InOutSine); + swing.RotateTo(targetAngle, beatLength, Easing.InOutQuad); if (currentAngle != 0 && Math.Abs(currentAngle - targetAngle) > angle * 1.8f && isSwinging) { From 267bef959fd7049dcf80557196cd211e3f81686f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 May 2022 02:00:04 +0900 Subject: [PATCH 1211/1276] Remove unnecessary cache type specification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Beatmaps/IBeatSyncProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/IBeatSyncProvider.cs b/osu.Game/Beatmaps/IBeatSyncProvider.cs index 573f06ef9f..cc1cfc3cb5 100644 --- a/osu.Game/Beatmaps/IBeatSyncProvider.cs +++ b/osu.Game/Beatmaps/IBeatSyncProvider.cs @@ -15,7 +15,7 @@ namespace osu.Game.Beatmaps /// Provides various data sources which allow for synchronising visuals to a known beat. /// Primarily intended for use with . /// - [Cached(typeof(IBeatSyncProvider))] + [Cached] public interface IBeatSyncProvider { ControlPointInfo? ControlPoints { get; } From 9c3d0dafbdba852857197bf13dbc2879328dceb3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 May 2022 02:00:20 +0900 Subject: [PATCH 1212/1276] Remove implementation at `Player` level Turns out this isn't required in the end due to implementation at `MasterGameplayClockContainer`. --- osu.Game/Screens/Play/Player.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cbd9b03c32..51c1e6b43b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; @@ -17,10 +16,8 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Threading; -using osu.Framework.Timing; using osu.Game.Audio; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.IO.Archives; @@ -41,7 +38,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play { [Cached] - public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler, ILocalUserPlayInfo, IBeatSyncProvider + public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler, ILocalUserPlayInfo { /// /// The delay upon completion of the beatmap before displaying the results screen. @@ -1111,9 +1108,5 @@ namespace osu.Game.Screens.Play IBindable ISamplePlaybackDisabler.SamplePlaybackDisabled => samplePlaybackDisabled; IBindable ILocalUserPlayInfo.IsPlaying => LocalUserPlaying; - - ControlPointInfo IBeatSyncProvider.ControlPoints => Beatmap.Value.Beatmap.ControlPointInfo; - IClock IBeatSyncProvider.Clock => GameplayClockContainer.GameplayClock; - ChannelAmplitudes? IBeatSyncProvider.Amplitudes => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track.CurrentAmplitudes : (ChannelAmplitudes?)null; } } From 0bef2ca7526295767db063b0b9e92035411cda83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 May 2022 19:27:20 +0200 Subject: [PATCH 1213/1276] Adjust test slightly * Import `osuTK.Input` instead of using full qualified name * Use some more straightforward assertions --- .../TestSceneMultiplayerMatchSubScreen.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index dd281e7738..7ae81c9800 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Graphics.Containers; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -28,6 +29,7 @@ using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Resources; +using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { @@ -194,10 +196,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for join", () => RoomJoined); - AddStep("press toggle mod select key", () => InputManager.Key(osuTK.Input.Key.F1)); + AddStep("press toggle mod select key", () => InputManager.Key(Key.F1)); - AddUntilStep("mod select contents loaded", - () => this.ChildrenOfType().Any() && this.ChildrenOfType().All(col => col.IsLoaded && col.ItemsLoaded)); + AddUntilStep("mod select shown", () => this.ChildrenOfType().Single().State.Value == Visibility.Visible); } [Test] @@ -214,10 +215,10 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for join", () => RoomJoined); - AddStep("press toggle mod select key", () => InputManager.Key(osuTK.Input.Key.F1)); + AddStep("press toggle mod select key", () => InputManager.Key(Key.F1)); - AddAssert("mod select contents not loaded", - () => !(this.ChildrenOfType().Any() && this.ChildrenOfType().All(col => col.IsLoaded && col.ItemsLoaded))); + AddWaitStep("wait some", 3); + AddAssert("mod select not shown", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); } [Test] From afbb1fa750e8560481fbf43509ae166e822d87ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 May 2022 14:06:00 +0900 Subject: [PATCH 1214/1276] Fix tournament user stat population potentially using wrong ruleset `OsuGameBase.Ruleset` is bound [late](https://github.com/ppy/osu/blob/7d0470794bcd454d3a93954ec4fe3415933627ef/osu.Game.Tournament/TournamentGameBase.cs#L169) so we must use the ladder one during user retrieval. Closes https://github.com/ppy/osu/issues/18363. --- osu.Game.Tournament/TournamentGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 363baccb37..6ae0312cce 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -259,7 +259,7 @@ namespace osu.Game.Tournament public void PopulateUser(APIUser user, Action success = null, Action failure = null, bool immediate = false) { - var req = new GetUserRequest(user.Id, Ruleset.Value); + var req = new GetUserRequest(user.Id, ladder.Ruleset.Value); if (immediate) { From 63a3829768be608c125076767538910104b4fe89 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 23 May 2022 14:19:25 +0900 Subject: [PATCH 1215/1276] Split out SingleKeyStamina class --- .../Difficulty/Skills/SingleKeyStamina.cs | 42 +++++++++++++++++++ .../Difficulty/Skills/Stamina.cs | 34 --------------- 2 files changed, 42 insertions(+), 34 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs new file mode 100644 index 0000000000..cabfd231d8 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/SingleKeyStamina.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Skills +{ + /// + /// Stamina of a single key, calculated based on repetition speed. + /// + public class SingleKeyStamina + { + private double? previousHitTime; + + /// + /// Similar to + /// + public double StrainValueOf(DifficultyHitObject current) + { + if (previousHitTime == null) + { + previousHitTime = current.StartTime; + return 0; + } + + double objectStrain = 0.5; + objectStrain += speedBonus(current.StartTime - previousHitTime.Value); + previousHitTime = current.StartTime; + return objectStrain; + } + + /// + /// Applies a speed bonus dependent on the time since the last hit performed using this key. + /// + /// The duration between the current and previous note hit using the same key. + private double speedBonus(double notePairDuration) + { + return 175 / (notePairDuration + 100); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 744dafd57e..32d8b70485 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -9,40 +9,6 @@ using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty.Skills { - /// - /// Stamina of a single key, calculated based on repetition speed. - /// - public class SingleKeyStamina - { - private double? previousHitTime; - - /// - /// Similar to - /// - public double StrainValueOf(DifficultyHitObject current) - { - if (previousHitTime == null) - { - previousHitTime = current.StartTime; - return 0; - } - - double objectStrain = 0.5; - objectStrain += speedBonus(current.StartTime - previousHitTime.Value); - previousHitTime = current.StartTime; - return objectStrain; - } - - /// - /// Applies a speed bonus dependent on the time since the last hit performed using this key. - /// - /// The duration between the current and previous note hit using the same key. - private double speedBonus(double notePairDuration) - { - return 175 / (notePairDuration + 100); - } - } - /// /// Calculates the stamina coefficient of taiko difficulty. /// From 6cfe35360a6ed2e666047dce18a45570b098041a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 23 May 2022 14:36:06 +0900 Subject: [PATCH 1216/1276] Refactor key indexing --- .../Difficulty/Skills/Stamina.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 32d8b70485..61bcbfa59d 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -20,26 +20,27 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.4; - /// - /// Stamina of each individual keys, calculated based on repetition speed. - /// - private readonly SingleKeyStamina[] keyStamina = + private readonly SingleKeyStamina[] centreKeyStamina = { new SingleKeyStamina(), - new SingleKeyStamina(), + new SingleKeyStamina() + }; + + private readonly SingleKeyStamina[] rimKeyStamina = + { new SingleKeyStamina(), new SingleKeyStamina() }; /// - /// Current index to for a don hit. + /// Current index into for a centre hit. /// - private int donIndex = 1; + private int centreKeyIndex; /// - /// Current index to for a kat hit. + /// Current index into for a rim hit. /// - private int katIndex = 3; + private int rimKeyIndex; /// /// Creates a skill. @@ -59,12 +60,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills // Alternate key for the same color. if (current.HitType == HitType.Centre) { - donIndex = donIndex == 0 ? 1 : 0; - return keyStamina[donIndex]; + centreKeyIndex = (centreKeyIndex + 1) % 2; + return centreKeyStamina[centreKeyIndex]; } - katIndex = katIndex == 2 ? 3 : 2; - return keyStamina[katIndex]; + rimKeyIndex = (rimKeyIndex + 1) % 2; + return rimKeyStamina[rimKeyIndex]; } protected override double StrainValueOf(DifficultyHitObject current) From 58d39734d07c0fdd90c2310b68186b82df4ac6c0 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 23 May 2022 20:23:50 +0100 Subject: [PATCH 1217/1276] Integrate `ChatOverlayV2` into main game --- osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs | 2 +- osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs | 2 +- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/OsuGame.cs | 4 ++-- osu.Game/Overlays/ChatOverlayV2.cs | 10 ++++++++-- .../Profile/Header/Components/MessageUserButton.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarChatButton.cs | 2 +- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index e4871f611e..c8ea692bb2 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Navigation typeof(DashboardOverlay), typeof(NewsOverlay), typeof(ChannelManager), - typeof(ChatOverlay), + typeof(ChatOverlayV2), typeof(SettingsOverlay), typeof(UserProfileOverlay), typeof(BeatmapSetOverlay), diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index e27db00003..7268dd31f6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Online Children = new Drawable[] { channelManager, - chatOverlay = new TestChatOverlayV2 { RelativeSizeAxes = Axes.Both }, + chatOverlay = new TestChatOverlayV2(), }, }; }); diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 20d555c16c..69e7dee1a5 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -46,7 +46,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); - SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f); + SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlayV2.DEFAULT_HEIGHT, 0.2f, 1f); SetDefault(OsuSetting.BeatmapListingCardSize, BeatmapCardSize.Normal); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 785881d97a..402bd94f31 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -75,7 +75,7 @@ namespace osu.Game public Toolbar Toolbar; - private ChatOverlay chatOverlay; + private ChatOverlayV2 chatOverlay; private ChannelManager channelManager; @@ -848,7 +848,7 @@ namespace osu.Game loadComponentSingleFile(news = new NewsOverlay(), overlayContent.Add, true); var rankingsOverlay = loadComponentSingleFile(new RankingsOverlay(), overlayContent.Add, true); loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true); - loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add, true); + loadComponentSingleFile(chatOverlay = new ChatOverlayV2(), overlayContent.Add, true); loadComponentSingleFile(new MessageNotifier(), AddInternal, true); loadComponentSingleFile(Settings = new SettingsOverlay(), leftFloatingOverlayContent.Add, true); loadComponentSingleFile(changelogOverlay = new ChangelogOverlay(), overlayContent.Add, true); diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index ef479ea21b..441042432a 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -47,8 +47,9 @@ namespace osu.Game.Overlays private bool isDraggingTopBar; private float dragStartChatHeight; + public const float DEFAULT_HEIGHT = 0.4f; + private const int transition_length = 500; - private const float default_chat_height = 0.4f; private const float top_bar_height = 40; private const float side_bar_width = 190; private const float chat_bar_height = 60; @@ -70,7 +71,7 @@ namespace osu.Game.Overlays public ChatOverlayV2() { - Height = default_chat_height; + Height = DEFAULT_HEIGHT; Masking = true; @@ -82,6 +83,7 @@ namespace osu.Game.Overlays Margin = new MarginPadding { Bottom = -corner_radius }; Padding = new MarginPadding { Bottom = corner_radius }; + RelativeSizeAxes = Axes.Both; Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; } @@ -294,6 +296,10 @@ namespace osu.Game.Overlays }); } } + + // Mark channel as read when channel switched + if (newChannel.Messages.Any()) + channelManager.MarkChannelAsRead(newChannel); } protected virtual ChatOverlayDrawableChannel CreateDrawableChannel(Channel newChannel) => new ChatOverlayDrawableChannel(newChannel); diff --git a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs index e3dc5f818a..eafb453f75 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private UserProfileOverlay userOverlay { get; set; } [Resolved(CanBeNull = true)] - private ChatOverlay chatOverlay { get; set; } + private ChatOverlayV2 chatOverlay { get; set; } [Resolved] private IAPIProvider apiProvider { get; set; } diff --git a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs index 2d3b33e9bc..20f405aae2 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Toolbar } [BackgroundDependencyLoader(true)] - private void load(ChatOverlay chat) + private void load(ChatOverlayV2 chat) { StateContainer = chat; } From d2a49ca266087d5342fb1c9aa65b45db9204a7a1 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 23 May 2022 20:24:17 +0100 Subject: [PATCH 1218/1276] Use `ChatOverlayV2` in message notifier and tests --- osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs | 2 +- osu.Game/Online/Chat/MessageNotifier.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs b/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs index 79f62a16e3..46f426597a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs @@ -208,7 +208,7 @@ namespace osu.Game.Tests.Visual.Online }; [Cached] - public ChatOverlay ChatOverlay { get; } = new ChatOverlay(); + public ChatOverlayV2 ChatOverlay { get; } = new ChatOverlayV2(); private readonly MessageNotifier messageNotifier = new MessageNotifier(); diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index ca6082e19b..cea3e321fa 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -27,7 +27,7 @@ namespace osu.Game.Online.Chat private INotificationOverlay notifications { get; set; } [Resolved] - private ChatOverlay chatOverlay { get; set; } + private ChatOverlayV2 chatOverlay { get; set; } [Resolved] private ChannelManager channelManager { get; set; } @@ -170,7 +170,7 @@ namespace osu.Game.Online.Chat public override bool IsImportant => false; [BackgroundDependencyLoader] - private void load(OsuColour colours, ChatOverlay chatOverlay, INotificationOverlay notificationOverlay) + private void load(OsuColour colours, ChatOverlayV2 chatOverlay, INotificationOverlay notificationOverlay) { IconBackground.Colour = colours.PurpleDark; From 606f3b2bd18bc2ed780441a02f6698ba4bcbe6f0 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 23 May 2022 20:24:49 +0100 Subject: [PATCH 1219/1276] Use `ChatOverlayV2` in screen navigation tests --- .../Visual/Navigation/TestScenePerformFromScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 2ce914ba3d..5d0116f80e 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -86,9 +86,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestOverlaysAlwaysClosed() { - ChatOverlay chat = null; + ChatOverlayV2 chat = null; AddUntilStep("is at menu", () => Game.ScreenStack.CurrentScreen is MainMenu); - AddUntilStep("wait for chat load", () => (chat = Game.ChildrenOfType().SingleOrDefault()) != null); + AddUntilStep("wait for chat load", () => (chat = Game.ChildrenOfType().SingleOrDefault()) != null); AddStep("show chat", () => InputManager.Key(Key.F8)); From 60b10fca4ec969997f77861beb12d724f0f15297 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 23 May 2022 20:25:01 +0100 Subject: [PATCH 1220/1276] Remove redundant caching of overlays in `ChatLink` test --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 6818147da4..a28de3be1e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Chat; -using osu.Game.Overlays; using osu.Game.Overlays.Chat; using osuTK.Graphics; @@ -22,12 +21,10 @@ namespace osu.Game.Tests.Visual.Online public class TestSceneChatLink : OsuTestScene { private readonly TestChatLineContainer textContainer; - private readonly DialogOverlay dialogOverlay; private Color4 linkColour; public TestSceneChatLink() { - Add(dialogOverlay = new DialogOverlay { Depth = float.MinValue }); Add(textContainer = new TestChatLineContainer { Padding = new MarginPadding { Left = 20, Right = 20 }, @@ -47,9 +44,6 @@ namespace osu.Game.Tests.Visual.Online availableChannels.Add(new Channel { Name = "#english" }); availableChannels.Add(new Channel { Name = "#japanese" }); Dependencies.Cache(chatManager); - - Dependencies.Cache(new ChatOverlay()); - Dependencies.CacheAs(dialogOverlay); } [SetUp] From e990a247230d086f14a88e3976230055c5fd331a Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Mon, 23 May 2022 20:40:01 +0100 Subject: [PATCH 1221/1276] Remove redundant installation of `libavformat` in Linux CI runs Linux `libavformat` native libraries are now packaged with framework. --- .github/workflows/ci.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d5c565d60..320197b88e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,15 +78,6 @@ jobs: with: dotnet-version: "6.0.x" - # FIXME: libavformat is not included in Ubuntu. Let's fix that. - # https://github.com/ppy/osu-framework/issues/4349 - # Remove this once https://github.com/actions/virtual-environments/issues/3306 has been resolved. - - name: Install libavformat-dev - if: ${{matrix.os.fullname == 'ubuntu-latest'}} - run: | - sudo apt-get update && \ - sudo apt-get -y install libavformat-dev - - name: Compile run: dotnet build -c Debug -warnaserror osu.Desktop.slnf From 14f14b431154f9f86d14f794ad64d60e1bd9b76a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 23 May 2022 23:37:05 +0200 Subject: [PATCH 1222/1276] Add failing test case for back button behaviour in song select --- .../Navigation/TestSceneScreenNavigation.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index a3e0caedb9..256f4a7423 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -8,9 +8,11 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Collections; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Leaderboards; @@ -54,6 +56,39 @@ namespace osu.Game.Tests.Visual.Navigation exitViaEscapeAndConfirm(); } + [Test] + public void TestSongSelectBackActionHandling() + { + TestPlaySongSelect songSelect = null; + + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); + + AddStep("set filter", () => songSelect.ChildrenOfType().Single().Current.Value = "test"); + AddStep("press back", () => InputManager.Click(MouseButton.Button1)); + + AddAssert("still at song select", () => Game.ScreenStack.CurrentScreen == songSelect); + AddAssert("filter cleared", () => string.IsNullOrEmpty(songSelect.ChildrenOfType().Single().Current.Value)); + + AddStep("set filter again", () => songSelect.ChildrenOfType().Single().Current.Value = "test"); + AddStep("open collections dropdown", () => + { + InputManager.MoveMouseTo(songSelect.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + AddStep("press back once", () => InputManager.Click(MouseButton.Button1)); + AddAssert("still at song select", () => Game.ScreenStack.CurrentScreen == songSelect); + AddAssert("collections dropdown closed", () => songSelect + .ChildrenOfType().Single() + .ChildrenOfType.DropdownMenu>().Single().State == MenuState.Closed); + + AddStep("press back a second time", () => InputManager.Click(MouseButton.Button1)); + AddAssert("filter cleared", () => string.IsNullOrEmpty(songSelect.ChildrenOfType().Single().Current.Value)); + + AddStep("press back a third time", () => InputManager.Click(MouseButton.Button1)); + ConfirmAtMainMenu(); + } + /// /// This tests that the F1 key will open the mod select overlay, and not be handled / blocked by the music controller (which has the same default binding /// but should be handled *after* song select). From 3847a586f110fec99f82645582a373da98776bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 23 May 2022 23:37:40 +0200 Subject: [PATCH 1223/1276] Make `GlobalAction.Back` close all opened dropdown menus --- .../Graphics/UserInterface/OsuDropdown.cs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 4e391c8221..23e05c7ccc 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -12,9 +12,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; using osu.Game.Overlays; using osuTK; using osuTK.Graphics; @@ -31,7 +34,7 @@ namespace osu.Game.Graphics.UserInterface #region OsuDropdownMenu - protected class OsuDropdownMenu : DropdownMenu + protected class OsuDropdownMenu : DropdownMenu, IKeyBindingHandler { public override bool HandleNonPositionalInput => State == MenuState.Open; @@ -275,6 +278,23 @@ namespace osu.Game.Graphics.UserInterface } #endregion + + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Repeat) return false; + + if (e.Action == GlobalAction.Back) + { + State = MenuState.Closed; + return true; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } } #endregion From 4cf8df16286337e07294a7e30565a204abccdcc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 23 May 2022 23:58:12 +0200 Subject: [PATCH 1224/1276] Fix test inteference from `TestOverlayClosing` --- osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 256f4a7423..c1f5f110d1 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -522,6 +522,9 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("move cursor to background", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomRight)); AddStep("click left mouse button", () => InputManager.Click(MouseButton.Left)); AddAssert("now playing is hidden", () => nowPlayingOverlay.State.Value == Visibility.Hidden); + + // move the mouse firmly inside game bounds to avoid interfering with other tests. + AddStep("center cursor", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre)); } [Test] From eae23784662e89b9d1bdc4d5b0710afe77d41303 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Tue, 24 May 2022 01:49:07 +0100 Subject: [PATCH 1225/1276] Add WIP to search filters --- osu.Game/Overlays/BeatmapListing/SearchCategory.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListing/SearchCategory.cs b/osu.Game/Overlays/BeatmapListing/SearchCategory.cs index d6ae41aba1..b52df6234f 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchCategory.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchCategory.cs @@ -32,6 +32,9 @@ namespace osu.Game.Overlays.BeatmapListing [Description("Pending & WIP")] Pending, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusWip))] + Wip, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusGraveyard))] Graveyard, From ed0b841df0ea3bd95315fd79f658e83446239e60 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 23 May 2022 13:19:01 -0700 Subject: [PATCH 1226/1276] Fix incorrect left/right clicking area of mod panels --- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- osu.Game/Overlays/Mods/ModPanel.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 9bb3f8bd9e..42f9daec4d 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -267,7 +267,7 @@ namespace osu.Game.Overlays.Mods { cancellationTokenSource?.Cancel(); - var panels = availableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0))); + var panels = availableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = Vector2.Zero)); Task? loadTask; diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index 7010342bd8..358bdd3202 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -70,7 +70,7 @@ namespace osu.Game.Overlays.Mods Content.Masking = true; Content.CornerRadius = CORNER_RADIUS; Content.BorderThickness = 2; - Content.Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); Children = new Drawable[] { From 1f935cacf452b3a9f212551082d4a14e4a286972 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 23 May 2022 18:45:53 -0700 Subject: [PATCH 1227/1276] Add spotlighted beatmaps filter to beatmap listing --- osu.Game/Overlays/BeatmapListing/SearchGeneral.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs b/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs index 9387020bdf..34ff5b9840 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs @@ -21,6 +21,9 @@ namespace osu.Game.Overlays.BeatmapListing [Description("Subscribed mappers")] Follows, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GeneralSpotlights))] + Spotlights, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GeneralFeaturedArtists))] [Description("Featured artists")] FeaturedArtists From 9259aa94f10897d044e590ad61e0aa6e5506bf7a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 24 May 2022 11:47:42 +0900 Subject: [PATCH 1228/1276] Fix Catmull slider parsing with duplicate initial point --- .../Formats/LegacyBeatmapDecoderTest.cs | 19 +++++++++++++++++++ ...catmull-duplicate-initial-controlpoint.osu | 2 ++ .../Objects/Legacy/ConvertHitObjectParser.cs | 6 ++++-- 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Resources/catmull-duplicate-initial-controlpoint.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 89baaf228d..e2d9910b82 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -898,5 +898,24 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(controlPoints[3].Type, Is.Null); } } + + [Test] + public void TestLegacyDuplicateInitialCatmullPointIsMerged() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("catmull-duplicate-initial-controlpoint.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var decoded = decoder.Decode(stream); + var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints; + + Assert.That(controlPoints.Count, Is.EqualTo(4)); + Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull)); + Assert.That(controlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(controlPoints[1].Type, Is.Null); + Assert.That(controlPoints[1].Position, Is.Not.EqualTo(Vector2.Zero)); + } + } } } diff --git a/osu.Game.Tests/Resources/catmull-duplicate-initial-controlpoint.osu b/osu.Game.Tests/Resources/catmull-duplicate-initial-controlpoint.osu new file mode 100644 index 0000000000..7062229eed --- /dev/null +++ b/osu.Game.Tests/Resources/catmull-duplicate-initial-controlpoint.osu @@ -0,0 +1,2 @@ +[HitObjects] +200,304,23875,6,0,C|200:304|288:304|288:208|352:208,1,260,8|0 \ No newline at end of file diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 7cf68a2df7..d3d1196eae 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -340,8 +340,10 @@ namespace osu.Game.Rulesets.Objects.Legacy if (vertices[endIndex].Position != vertices[endIndex - 1].Position) continue; - // Adjacent legacy Catmull segments should be treated as a single segment. - if (FormatVersion < LegacyBeatmapEncoder.FIRST_LAZER_VERSION && type == PathType.Catmull) + // Legacy Catmull sliders don't support multiple segments, so adjacent Catmull segments should be treated as a single one. + // Importantly, this is not applied to the first control point, which may duplicate the slider path's position + // resulting in a duplicate (0,0) control point in the resultant list. + if (type == PathType.Catmull && endIndex > 1 && FormatVersion < LegacyBeatmapEncoder.FIRST_LAZER_VERSION) continue; // The last control point of each segment is not allowed to start a new implicit segment. From 4bcf15f63257eb741775623ce5605bc5fb31220c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 May 2022 17:56:32 +0900 Subject: [PATCH 1229/1276] Refactor test to allow waveform testing --- .../Editing/TestSceneTapTimingControl.cs | 76 +++++++++++-------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs index de441995b5..46b45979ea 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs @@ -4,15 +4,15 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Timing; using osuTK; @@ -22,9 +22,9 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneTapTimingControl : EditorClockTestScene { - [Cached(typeof(EditorBeatmap))] - [Cached(typeof(IBeatSnapProvider))] - private readonly EditorBeatmap editorBeatmap; + private EditorBeatmap editorBeatmap => editorBeatmapContainer?.EditorBeatmap; + + private TestSceneHitObjectComposer.EditorBeatmapContainer editorBeatmapContainer; [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); @@ -33,38 +33,48 @@ namespace osu.Game.Tests.Visual.Editing private Bindable selectedGroup = new Bindable(); private TapTimingControl control; + private OsuSpriteText timingInfo; - public TestSceneTapTimingControl() + [Resolved] + private AudioManager audio { get; set; } + + [SetUpSteps] + public void SetUpSteps() { - var playableBeatmap = CreateBeatmap(new OsuRuleset().RulesetInfo); + AddStep("create beatmap", () => + { + Beatmap.Value = new WaveformTestBeatmap(audio); + }); - // Ensure time doesn't end while testing - playableBeatmap.BeatmapInfo.Length = 1200000; + AddStep("Create component", () => + { + Child = editorBeatmapContainer = new TestSceneHitObjectComposer.EditorBeatmapContainer(Beatmap.Value) + { + Children = new Drawable[] + { + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, + Width = 400, + Scale = new Vector2(1.5f), + Child = control = new TapTimingControl(), + }, + timingInfo = new OsuSpriteText(), + } + }; - editorBeatmap = new EditorBeatmap(playableBeatmap); - - selectedGroup.Value = editorBeatmap.ControlPointInfo.Groups.First(); + selectedGroup.Value = editorBeatmap.ControlPointInfo.Groups.First(); + }); } - protected override void LoadComplete() + protected override void Update() { - base.LoadComplete(); + base.Update(); - Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); - Beatmap.Disabled = true; - - Children = new Drawable[] - { - new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Y, - Width = 400, - Scale = new Vector2(1.5f), - Child = control = new TapTimingControl(), - } - }; + if (selectedGroup.Value != null) + timingInfo.Text = $"offset: {selectedGroup.Value.Time:N2} bpm: {selectedGroup.Value.ControlPoints.OfType().First().BPM:N2}"; } [Test] @@ -104,7 +114,13 @@ namespace osu.Game.Tests.Visual.Editing .TriggerClick(); }); - AddSliderStep("BPM", 30, 400, 60, bpm => editorBeatmap.ControlPointInfo.TimingPoints.First().BeatLength = 60000f / bpm); + AddSliderStep("BPM", 30, 400, 128, bpm => + { + if (editorBeatmap == null) + return; + + editorBeatmap.ControlPointInfo.TimingPoints.First().BeatLength = 60000f / bpm; + }); } protected override void Dispose(bool isDisposing) From 588c5d158320bf5ad26e85337a46084b92d5bda8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 May 2022 19:52:01 +0900 Subject: [PATCH 1230/1276] Add initial waveform comparison display --- .../ControlPoints/TimingControlPoint.cs | 2 + .../Screens/Edit/Timing/TapTimingControl.cs | 36 ++++- .../Edit/Timing/WaveformComparisonDisplay.cs | 152 ++++++++++++++++++ 3 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 922439fcb8..3a7c8b2ec0 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using osu.Framework.Bindables; using osu.Game.Beatmaps.Timing; using osu.Game.Graphics; diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index 1b0f0a3f5e..d0ab4d1f98 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -24,8 +24,8 @@ namespace osu.Game.Screens.Edit.Timing [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, OsuColour colours) { - Height = 200; RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; CornerRadius = LabelledDrawable.CORNER_RADIUS; Masking = true; @@ -39,20 +39,44 @@ namespace osu.Game.Screens.Edit.Timing }, new GridContainer { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, RowDimensions = new[] { - new Dimension(), + new Dimension(GridSizeMode.Absolute, 200), new Dimension(GridSizeMode.Absolute, 60), }, Content = new[] { new Drawable[] { - new MetronomeDisplay + new Container { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension() + }, + Content = new[] + { + new Drawable[] + { + new MetronomeDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + new WaveformComparisonDisplay(), + } + }, + } + } } }, new Drawable[] diff --git a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs new file mode 100644 index 0000000000..67a1d30ebe --- /dev/null +++ b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs @@ -0,0 +1,152 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Audio; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Edit.Timing +{ + internal class WaveformComparisonDisplay : CompositeDrawable + { + private const int total_waveforms = 8; + + private OsuSpriteText beatIndexText = null!; + + private readonly BindableNumber beatLength = new BindableDouble(); + + [Resolved] + private IBindable beatmap { get; set; } = null!; + + [Resolved] + private Bindable selectedGroup { get; set; } = null!; + + [Resolved] + private EditorClock editorClock { get; set; } = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + private TimingControlPoint timingPoint = TimingControlPoint.DEFAULT; + + private int lastDisplayedBeatIndex; + + public WaveformComparisonDisplay() + { + RelativeSizeAxes = Axes.Both; + + CornerRadius = LabelledDrawable.CORNER_RADIUS; + Masking = true; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + for (int i = 0; i < total_waveforms; i++) + { + AddInternal(new WaveformGraph + { + RelativeSizeAxes = Axes.Both, + BaseColour = colourProvider.Colour0, + LowColour = colourProvider.Colour1, + MidColour = colourProvider.Colour2, + HighColour = colourProvider.Colour4, + Waveform = beatmap.Value.Waveform, + Resolution = 1, + RelativePositionAxes = Axes.Both, + Height = 1f / total_waveforms, + Y = (float)i / total_waveforms, + }); + } + + AddInternal(new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.White, + RelativeSizeAxes = Axes.Y, + Width = 3, + }); + + AddInternal(beatIndexText = new OsuSpriteText + { + Margin = new MarginPadding(5), + }); + + selectedGroup.BindValueChanged(selectedGroupChanged, true); + beatLength.BindValueChanged(_ => showFrom(lastDisplayedBeatIndex), true); + } + + private void selectedGroupChanged(ValueChangedEvent group) + { + timingPoint = selectedGroup.Value?.ControlPoints.OfType().FirstOrDefault() + ?? TimingControlPoint.DEFAULT; + + beatLength.UnbindBindings(); + beatLength.BindTo(timingPoint.BeatLengthBindable); + } + + protected override bool OnHover(HoverEvent e) => true; + + protected override bool OnMouseMove(MouseMoveEvent e) + { + float trackLength = (float)beatmap.Value.Track.Length; + int totalBeatsAvailable = (int)(trackLength / timingPoint.BeatLength); + + Scheduler.AddOnce(showFrom, (int)(e.MousePosition.X / DrawWidth * totalBeatsAvailable)); + + return base.OnMouseMove(e); + } + + protected override void Update() + { + base.Update(); + + if (!IsHovered) + { + int beatOffset = (int)Math.Max(0, ((editorClock.CurrentTime - selectedGroup.Value.Time) / timingPoint.BeatLength)); + + showFrom(beatOffset); + } + } + + private void showFrom(int beatIndex) + { + const float visible_width = 300; + + float trackLength = (float)beatmap.Value.Track.Length; + float scale = trackLength / visible_width; + + beatIndexText.Text = beatIndex.ToString(); + + foreach (var waveform in InternalChildren.OfType()) + { + // offset to the required beat index. + float offset = (float)(selectedGroup.Value.Time + (beatIndex * timingPoint.BeatLength - (visible_width / 2))) / trackLength * scale; + + waveform.X = -offset; + waveform.Scale = new Vector2(scale, 1); + + beatIndex++; + } + + lastDisplayedBeatIndex = beatIndex; + } + } +} From 7040dec013d1c050d7d1d068af770f337f2be5f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 May 2022 20:01:25 +0900 Subject: [PATCH 1231/1276] Fix disabled bindable woes by not using `TimingControlPoint.DEFAULT` --- .../Screens/Edit/Timing/WaveformComparisonDisplay.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs index 67a1d30ebe..e410d380d6 100644 --- a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Edit.Timing private IBindable beatmap { get; set; } = null!; [Resolved] - private Bindable selectedGroup { get; set; } = null!; + private Bindable selectedGroup { get; set; } = null!; [Resolved] private EditorClock editorClock { get; set; } = null!; @@ -46,6 +46,8 @@ namespace osu.Game.Screens.Edit.Timing private int lastDisplayedBeatIndex; + private double offsetZeroTime => selectedGroup.Value?.Time ?? 0; + public WaveformComparisonDisplay() { RelativeSizeAxes = Axes.Both; @@ -93,10 +95,10 @@ namespace osu.Game.Screens.Edit.Timing beatLength.BindValueChanged(_ => showFrom(lastDisplayedBeatIndex), true); } - private void selectedGroupChanged(ValueChangedEvent group) + private void selectedGroupChanged(ValueChangedEvent group) { timingPoint = selectedGroup.Value?.ControlPoints.OfType().FirstOrDefault() - ?? TimingControlPoint.DEFAULT; + ?? new TimingControlPoint(); beatLength.UnbindBindings(); beatLength.BindTo(timingPoint.BeatLengthBindable); @@ -120,7 +122,7 @@ namespace osu.Game.Screens.Edit.Timing if (!IsHovered) { - int beatOffset = (int)Math.Max(0, ((editorClock.CurrentTime - selectedGroup.Value.Time) / timingPoint.BeatLength)); + int beatOffset = (int)Math.Max(0, ((editorClock.CurrentTime - offsetZeroTime) / timingPoint.BeatLength)); showFrom(beatOffset); } @@ -138,7 +140,7 @@ namespace osu.Game.Screens.Edit.Timing foreach (var waveform in InternalChildren.OfType()) { // offset to the required beat index. - float offset = (float)(selectedGroup.Value.Time + (beatIndex * timingPoint.BeatLength - (visible_width / 2))) / trackLength * scale; + float offset = (float)(offsetZeroTime + (beatIndex * timingPoint.BeatLength - (visible_width / 2))) / trackLength * scale; waveform.X = -offset; waveform.Scale = new Vector2(scale, 1); From c1720c128d1f591281194b4b76744ea7dcefd7cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 May 2022 21:00:21 +0900 Subject: [PATCH 1232/1276] Dim rows which are not part of the active control point (and offset centre) --- .../Edit/Timing/WaveformComparisonDisplay.cs | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs index e410d380d6..2667ae5396 100644 --- a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs @@ -33,6 +33,9 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private IBindable beatmap { get; set; } = null!; + [Resolved] + private EditorBeatmap editorBeatmap { get; set; } = null!; + [Resolved] private Bindable selectedGroup { get; set; } = null!; @@ -46,7 +49,10 @@ namespace osu.Game.Screens.Edit.Timing private int lastDisplayedBeatIndex; - private double offsetZeroTime => selectedGroup.Value?.Time ?? 0; + private double selectedGroupStartTime; + private double selectedGroupEndTime; + + private readonly BindableList controlPointGroups = new BindableList(); public WaveformComparisonDisplay() { @@ -88,20 +94,45 @@ namespace osu.Game.Screens.Edit.Timing AddInternal(beatIndexText = new OsuSpriteText { - Margin = new MarginPadding(5), + Anchor = Anchor.CentreLeft, + Margin = new MarginPadding(4), }); - selectedGroup.BindValueChanged(selectedGroupChanged, true); + selectedGroup.BindValueChanged(_ => updateTimingGroup(), true); + + ((IBindableList)controlPointGroups).BindTo(editorBeatmap.ControlPointInfo.Groups); + controlPointGroups.BindCollectionChanged((_, __) => updateTimingGroup()); + beatLength.BindValueChanged(_ => showFrom(lastDisplayedBeatIndex), true); } - private void selectedGroupChanged(ValueChangedEvent group) + private void updateTimingGroup() { - timingPoint = selectedGroup.Value?.ControlPoints.OfType().FirstOrDefault() - ?? new TimingControlPoint(); - beatLength.UnbindBindings(); + + selectedGroupStartTime = 0; + selectedGroupEndTime = beatmap.Value.Track.Length; + + var tcp = selectedGroup.Value?.ControlPoints.OfType().FirstOrDefault(); + + if (tcp == null) + { + timingPoint = new TimingControlPoint(); + return; + } + + timingPoint = tcp; beatLength.BindTo(timingPoint.BeatLengthBindable); + + selectedGroupStartTime = selectedGroup.Value?.Time ?? 0; + + var nextGroup = editorBeatmap.ControlPointInfo.TimingPoints + .SkipWhile(g => g != tcp) + .Skip(1) + .FirstOrDefault(); + + if (nextGroup != null) + selectedGroupEndTime = nextGroup.Time; } protected override bool OnHover(HoverEvent e) => true; @@ -122,7 +153,7 @@ namespace osu.Game.Screens.Edit.Timing if (!IsHovered) { - int beatOffset = (int)Math.Max(0, ((editorClock.CurrentTime - offsetZeroTime) / timingPoint.BeatLength)); + int beatOffset = (int)Math.Floor((editorClock.CurrentTimeAccurate - selectedGroupStartTime) / timingPoint.BeatLength); showFrom(beatOffset); } @@ -137,11 +168,17 @@ namespace osu.Game.Screens.Edit.Timing beatIndexText.Text = beatIndex.ToString(); + // Start displaying from before the current beat + beatIndex -= total_waveforms / 2; + foreach (var waveform in InternalChildren.OfType()) { // offset to the required beat index. - float offset = (float)(offsetZeroTime + (beatIndex * timingPoint.BeatLength - (visible_width / 2))) / trackLength * scale; + double time = selectedGroupStartTime + beatIndex * timingPoint.BeatLength; + float offset = (float)(time - visible_width / 2) / trackLength * scale; + + waveform.Alpha = time < selectedGroupStartTime || time > selectedGroupEndTime ? 0.2f : 1; waveform.X = -offset; waveform.Scale = new Vector2(scale, 1); From 4fcdcef166b5089fd9403011d2f8ae3405308059 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 May 2022 21:32:55 +0900 Subject: [PATCH 1233/1276] Add indexing on each wavefrom row --- .../Edit/Timing/WaveformComparisonDisplay.cs | 72 ++++++++++++------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs index 2667ae5396..ad4e1e737d 100644 --- a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs @@ -26,8 +26,6 @@ namespace osu.Game.Screens.Edit.Timing { private const int total_waveforms = 8; - private OsuSpriteText beatIndexText = null!; - private readonly BindableNumber beatLength = new BindableDouble(); [Resolved] @@ -42,9 +40,6 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private EditorClock editorClock { get; set; } = null!; - [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; - private TimingControlPoint timingPoint = TimingControlPoint.DEFAULT; private int lastDisplayedBeatIndex; @@ -68,15 +63,9 @@ namespace osu.Game.Screens.Edit.Timing for (int i = 0; i < total_waveforms; i++) { - AddInternal(new WaveformGraph + AddInternal(new WaveformRow { RelativeSizeAxes = Axes.Both, - BaseColour = colourProvider.Colour0, - LowColour = colourProvider.Colour1, - MidColour = colourProvider.Colour2, - HighColour = colourProvider.Colour4, - Waveform = beatmap.Value.Waveform, - Resolution = 1, RelativePositionAxes = Axes.Both, Height = 1f / total_waveforms, Y = (float)i / total_waveforms, @@ -92,12 +81,6 @@ namespace osu.Game.Screens.Edit.Timing Width = 3, }); - AddInternal(beatIndexText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Margin = new MarginPadding(4), - }); - selectedGroup.BindValueChanged(_ => updateTimingGroup(), true); ((IBindableList)controlPointGroups).BindTo(editorBeatmap.ControlPointInfo.Groups); @@ -166,26 +149,63 @@ namespace osu.Game.Screens.Edit.Timing float trackLength = (float)beatmap.Value.Track.Length; float scale = trackLength / visible_width; - beatIndexText.Text = beatIndex.ToString(); - // Start displaying from before the current beat beatIndex -= total_waveforms / 2; - foreach (var waveform in InternalChildren.OfType()) + foreach (var row in InternalChildren.OfType()) { // offset to the required beat index. double time = selectedGroupStartTime + beatIndex * timingPoint.BeatLength; float offset = (float)(time - visible_width / 2) / trackLength * scale; - waveform.Alpha = time < selectedGroupStartTime || time > selectedGroupEndTime ? 0.2f : 1; - waveform.X = -offset; - waveform.Scale = new Vector2(scale, 1); - - beatIndex++; + row.Alpha = time < selectedGroupStartTime || time > selectedGroupEndTime ? 0.2f : 1; + row.WaveformOffset = -offset; + row.WaveformScale = new Vector2(scale, 1); + row.BeatIndex = beatIndex++; } lastDisplayedBeatIndex = beatIndex; } + + internal class WaveformRow : CompositeDrawable + { + private OsuSpriteText beatIndexText = null!; + private WaveformGraph waveformGraph = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load(IBindable beatmap) + { + InternalChildren = new Drawable[] + { + waveformGraph = new WaveformGraph + { + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, + Waveform = beatmap.Value.Waveform, + Resolution = 1, + + BaseColour = colourProvider.Colour0, + LowColour = colourProvider.Colour1, + MidColour = colourProvider.Colour2, + HighColour = colourProvider.Colour4, + }, + beatIndexText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Padding = new MarginPadding(5), + Colour = colourProvider.Content2 + } + }; + } + + public int BeatIndex { set => beatIndexText.Text = value.ToString(); } + public Vector2 WaveformScale { set => waveformGraph.Scale = value; } + public float WaveformOffset { set => waveformGraph.X = value; } + } } } From 49a21736464330baf0333ff5bfec2461885edd50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 14:33:05 +0900 Subject: [PATCH 1234/1276] Avoid needlessly updating display (and add better documentation on chosen scale) --- .../Screens/Edit/Timing/WaveformComparisonDisplay.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs index ad4e1e737d..0830ba8cf4 100644 --- a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs @@ -136,14 +136,21 @@ namespace osu.Game.Screens.Edit.Timing if (!IsHovered) { - int beatOffset = (int)Math.Floor((editorClock.CurrentTimeAccurate - selectedGroupStartTime) / timingPoint.BeatLength); + int currentBeat = (int)Math.Floor((editorClock.CurrentTimeAccurate - selectedGroupStartTime) / timingPoint.BeatLength); - showFrom(beatOffset); + showFrom(currentBeat); } } private void showFrom(int beatIndex) { + if (lastDisplayedBeatIndex == beatIndex) + return; + + // Chosen as a pretty usable number across all BPMs. + // Optimally we'd want this to scale with the BPM in question, but performing + // scaling of the display is both expensive in resampling, and decreases usability + // (as it is harder to track the waveform when making realtime adjustments). const float visible_width = 300; float trackLength = (float)beatmap.Value.Track.Length; From 1137545d4ab4d59e58891a5171932d900120cd2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 16:02:01 +0900 Subject: [PATCH 1235/1276] Fix `Timeline` potentially not updating visuals to correct state on first display --- .../Screens/Edit/Compose/Components/Timeline/Timeline.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 992ab7947e..6812bbb72d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -163,10 +163,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { base.LoadComplete(); + WaveformVisible.BindValueChanged(_ => updateWaveformOpacity()); waveformOpacity.BindValueChanged(_ => updateWaveformOpacity(), true); - WaveformVisible.ValueChanged += _ => updateWaveformOpacity(); - TicksVisible.ValueChanged += visible => ticks.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint); + TicksVisible.BindValueChanged(visible => ticks.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint), true); + ControlPointsVisible.BindValueChanged(visible => { if (visible.NewValue) From f00dd27fcdd762eccdec2ec780d427c0da023518 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 17:50:23 +0900 Subject: [PATCH 1236/1276] Move `OverlayColourProvider` provisioning of `RoundedButton` to `SettingsButton` for now --- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 9 +++++---- osu.Game/Overlays/Settings/SettingsButton.cs | 9 +++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index 3c0c3b69e8..cb8c63371d 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -2,13 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -27,9 +25,12 @@ namespace osu.Game.Graphics.UserInterfaceV2 } [BackgroundDependencyLoader(true)] - private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) + private void load(OsuColour colours) { - DefaultBackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; + // According to flyte, buttons are supposed to have explicit colours for now. + // Not sure this is the correct direction, but we haven't decided on an `OverlayColourProvider` stand-in yet. + // This is a better default. See `SettingsButton` for an override which uses `OverlayColourProvider`. + DefaultBackgroundColour = colours.Blue3; } protected override void LoadComplete() diff --git a/osu.Game/Overlays/Settings/SettingsButton.cs b/osu.Game/Overlays/Settings/SettingsButton.cs index 10aea92b22..9e4dc763ec 100644 --- a/osu.Game/Overlays/Settings/SettingsButton.cs +++ b/osu.Game/Overlays/Settings/SettingsButton.cs @@ -3,9 +3,12 @@ using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterfaceV2; namespace osu.Game.Overlays.Settings @@ -18,6 +21,12 @@ namespace osu.Game.Overlays.Settings Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS }; } + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) + { + DefaultBackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; + } + public LocalisableString TooltipText { get; set; } public override IEnumerable FilterTerms From a346990a8c7a3b9986d8c712ea4be568e6cf9bec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 16:02:08 +0900 Subject: [PATCH 1237/1276] Remove `TriangleButton` usage in editor --- osu.Game/Screens/Edit/Timing/GroupSection.cs | 4 ++-- osu.Game/Screens/Edit/Timing/TimingScreen.cs | 5 +++-- osu.Game/Screens/Edit/Verify/IssueList.cs | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/GroupSection.cs b/osu.Game/Screens/Edit/Timing/GroupSection.cs index 03059ff6e1..bb2dd35a9c 100644 --- a/osu.Game/Screens/Edit/Timing/GroupSection.cs +++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Edit.Timing { private LabelledTextBox textBox; - private TriangleButton button; + private OsuButton button; [Resolved] protected Bindable SelectedGroup { get; private set; } @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Edit.Timing { Label = "Time" }, - button = new TriangleButton + button = new RoundedButton { Text = "Use current time", RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Screens/Edit/Timing/TimingScreen.cs b/osu.Game/Screens/Edit/Timing/TimingScreen.cs index a4193d5084..5f1fd14617 100644 --- a/osu.Game/Screens/Edit/Timing/TimingScreen.cs +++ b/osu.Game/Screens/Edit/Timing/TimingScreen.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; using osuTK; @@ -100,7 +101,7 @@ namespace osu.Game.Screens.Edit.Timing Spacing = new Vector2(5), Children = new Drawable[] { - deleteButton = new OsuButton + deleteButton = new RoundedButton { Text = "-", Size = new Vector2(30, 30), @@ -108,7 +109,7 @@ namespace osu.Game.Screens.Edit.Timing Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, }, - new OsuButton + new RoundedButton { Text = "+ Add at current time", Action = addNew, diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 5fe43199cc..415acc0e22 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks.Components; @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Edit.Verify Margin = new MarginPadding(20), Children = new Drawable[] { - new TriangleButton + new RoundedButton { Text = "Refresh", Action = refresh, From 4a88affd036f505c67e2ddbebdbb5e41ed1db339 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 17:02:19 +0900 Subject: [PATCH 1238/1276] Move beat divisor tick size retrieval to static methods --- osu.Game/Screens/Edit/BindableBeatDivisor.cs | 27 +++++++++ .../Timeline/TimelineTickDisplay.cs | 58 +++---------------- 2 files changed, 35 insertions(+), 50 deletions(-) diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index af958e3448..8f430dce77 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -5,6 +5,7 @@ using System.Linq; using osu.Framework.Bindables; using osu.Game.Graphics; using osu.Game.Screens.Edit.Compose.Components; +using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.Edit @@ -100,6 +101,32 @@ namespace osu.Game.Screens.Edit } } + /// + /// Get a relative display size for the specified divisor. + /// + /// The beat divisor. + /// A relative size which can be used to display ticks. + public static Vector2 GetSize(int beatDivisor) + { + switch (beatDivisor) + { + case 1: + case 2: + return new Vector2(0.6f, 0.9f); + + case 3: + case 4: + return new Vector2(0.5f, 0.8f); + + case 6: + case 8: + return new Vector2(0.4f, 0.7f); + + default: + return new Vector2(0.3f, 0.6f); + } + } + /// /// Retrieves the applicable divisor for a specific beat index. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index 3a32dc18e5..fda8416ecd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; +using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { @@ -132,10 +133,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline // even though "bar lines" take up the full vertical space, we render them in two pieces because it allows for less anchor/origin churn. + Vector2 size = Vector2.One; + + if (indexInBar != 1) + size = BindableBeatDivisor.GetSize(divisor); + var line = getNextUsableLine(); line.X = xPos; - line.Width = PointVisualisation.MAX_WIDTH * getWidth(indexInBar, divisor); - line.Height = 0.9f * getHeight(indexInBar, divisor); + line.Width = PointVisualisation.MAX_WIDTH * size.X; + line.Height = 0.9f * size.Y; line.Colour = colour; } @@ -170,54 +176,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - private static float getWidth(int indexInBar, int divisor) - { - if (indexInBar == 0) - return 1; - - switch (divisor) - { - case 1: - case 2: - return 0.6f; - - case 3: - case 4: - return 0.5f; - - case 6: - case 8: - return 0.4f; - - default: - return 0.3f; - } - } - - private static float getHeight(int indexInBar, int divisor) - { - if (indexInBar == 0) - return 1; - - switch (divisor) - { - case 1: - case 2: - return 0.9f; - - case 3: - case 4: - return 0.8f; - - case 6: - case 8: - return 0.7f; - - default: - return 0.6f; - } - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From e6087f5f5be6c6d2038a8909564ae2bd3fc23386 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 16:51:21 +0900 Subject: [PATCH 1239/1276] Handle beat divisor input on mouse down, rather than mouse up It felt way too unresponsive. --- osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 370c9016c7..9eda540b70 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -453,6 +453,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { marker.Active = true; + handleMouseInput(e.ScreenSpaceMousePosition); return base.OnMouseDown(e); } From 3c2e57bf00f04cbe2ad74ba786674c986298ff7a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 16:50:16 +0900 Subject: [PATCH 1240/1276] Update `BeatDivisorControl` to better match new designs and metrics --- .../Editing/TestSceneBeatDivisorControl.cs | 5 ++ .../Compose/Components/BeatDivisorControl.cs | 78 ++++++------------- 2 files changed, 28 insertions(+), 55 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index 6a0950c6dd..79be8a02c8 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -5,11 +5,13 @@ using System; using System.Diagnostics; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; +using osu.Game.Overlays; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -25,6 +27,9 @@ namespace osu.Game.Tests.Visual.Editing private SliderBar tickSliderBar => beatDivisorControl.ChildrenOfType>().Single(); private EquilateralTriangle tickMarkerHead => tickSliderBar.ChildrenOfType().Single(); + [Cached] + private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Green); + [SetUp] public void SetUp() => Schedule(() => { diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 370c9016c7..47f7f4b388 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -8,9 +8,7 @@ using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; @@ -22,6 +20,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -38,18 +37,17 @@ namespace osu.Game.Screens.Edit.Compose.Components } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider) { Masking = true; - CornerRadius = 5; InternalChildren = new Drawable[] { new Box { - Name = "Gray Background", + Name = "Main background", RelativeSizeAxes = Axes.Both, - Colour = colours.Gray4 + Colour = colourProvider.Background3, }, new GridContainer { @@ -65,9 +63,9 @@ namespace osu.Game.Screens.Edit.Compose.Components { new Box { - Name = "Black Background", + Name = "Tick area background", RelativeSizeAxes = Axes.Both, - Colour = Color4.Black + Colour = colourProvider.Background5, }, new TickSliderBar(beatDivisor) { @@ -86,7 +84,7 @@ namespace osu.Game.Screens.Edit.Compose.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.Gray4 + Colour = colourProvider.Background3 }, new Container { @@ -139,11 +137,6 @@ namespace osu.Game.Screens.Edit.Compose.Components RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray4 - }, new Container { RelativeSizeAxes = Axes.Both, @@ -402,15 +395,15 @@ namespace osu.Game.Screens.Edit.Compose.Components ClearInternal(); CurrentNumber.ValueChanged -= moveMarker; - foreach (int t in beatDivisor.ValidDivisors.Value.Presets) + foreach (int divisor in beatDivisor.ValidDivisors.Value.Presets) { - AddInternal(new Tick + AddInternal(new Tick(divisor) { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopCentre, - RelativePositionAxes = Axes.X, - Colour = BindableBeatDivisor.GetColourFor(t, colours), - X = getMappedPosition(t) + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Both, + Colour = BindableBeatDivisor.GetColourFor(divisor, colours), + X = getMappedPosition(divisor), }); } @@ -422,7 +415,6 @@ namespace osu.Game.Screens.Edit.Compose.Components private void moveMarker(ValueChangedEvent divisor) { marker.MoveToX(getMappedPosition(divisor.NewValue), 100, Easing.OutQuint); - marker.Flash(); } protected override void UpdateValue(float value) @@ -489,29 +481,26 @@ namespace osu.Game.Screens.Edit.Compose.Components private float getMappedPosition(float divisor) => MathF.Pow((divisor - 1) / (beatDivisor.ValidDivisors.Value.Presets.Last() - 1), 0.90f); - private class Tick : CompositeDrawable + private class Tick : Circle { - public Tick() + public Tick(int divisor) { - Size = new Vector2(2.5f, 10); - + Size = new Vector2(6f, 12) * BindableBeatDivisor.GetSize(divisor); InternalChild = new Box { RelativeSizeAxes = Axes.Both }; - - CornerRadius = 0.5f; - Masking = true; } } private class Marker : CompositeDrawable { - private Color4 defaultColour; + [Resolved] + private OverlayColourProvider colourProvider { get; set; } - private const float size = 7; + private const float size = 10; [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { - Colour = defaultColour = colours.Gray4; + Colour = colourProvider.Background3; Anchor = Anchor.TopLeft; Origin = Anchor.TopCentre; @@ -521,15 +510,6 @@ namespace osu.Game.Screens.Edit.Compose.Components InternalChildren = new Drawable[] { - new Box - { - Width = 2, - RelativeSizeAxes = Axes.Y, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.2f), Color4.White), - Blending = BlendingParameters.Additive, - }, new EquilateralTriangle { Origin = Anchor.BottomCentre, @@ -548,22 +528,10 @@ namespace osu.Game.Screens.Edit.Compose.Components get => active; set { - this.FadeColour(value ? Color4.White : defaultColour, 500, Easing.OutQuint); + this.FadeColour(value ? colourProvider.Background1 : colourProvider.Background3, 500, Easing.OutQuint); active = value; } } - - public void Flash() - { - bool wasActive = active; - - Active = true; - - if (wasActive) return; - - using (BeginDelayedSequence(50)) - Active = false; - } } } } From 3ace5f135e826ae73e7e5630ae7a369f5ee7d6fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 18:25:57 +0900 Subject: [PATCH 1241/1276] Adjust marker to match design proportions --- .../Compose/Components/BeatDivisorControl.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 47f7f4b388..e3c862f91b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -495,26 +495,22 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved] private OverlayColourProvider colourProvider { get; set; } - private const float size = 10; - [BackgroundDependencyLoader] private void load() { Colour = colourProvider.Background3; - Anchor = Anchor.TopLeft; - Origin = Anchor.TopCentre; + Anchor = Anchor.BottomLeft; + Origin = Anchor.BottomCentre; + + Size = new Vector2(8, 6.5f); - Width = size; - RelativeSizeAxes = Axes.Y; RelativePositionAxes = Axes.X; InternalChildren = new Drawable[] { - new EquilateralTriangle + new Triangle { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Height = size, + RelativeSizeAxes = Axes.Both, EdgeSmoothness = new Vector2(1), Colour = Color4.White, } From bb82abe48d81d596946d1781d0621c1bbf5f18da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 18:38:08 +0900 Subject: [PATCH 1242/1276] Update test to use new triangle type resolution --- osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index 79be8a02c8..cdd1175692 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Editing private BindableBeatDivisor bindableBeatDivisor; private SliderBar tickSliderBar => beatDivisorControl.ChildrenOfType>().Single(); - private EquilateralTriangle tickMarkerHead => tickSliderBar.ChildrenOfType().Single(); + private Triangle tickMarkerHead => tickSliderBar.ChildrenOfType().Single(); [Cached] private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Green); From 33fd1555f23a45c4d325c4db4d4d2aa643a6c0c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 18:42:40 +0900 Subject: [PATCH 1243/1276] Update `TestSceneRoundedButton` with new colour assertions --- .../UserInterface/TestSceneRoundedButton.cs | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs index f45c55d912..454a71e6d2 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs @@ -5,9 +5,12 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Testing; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; +using osu.Game.Overlays.Settings; namespace osu.Game.Tests.Visual.UserInterface { @@ -15,14 +18,31 @@ namespace osu.Game.Tests.Visual.UserInterface { private readonly BindableBool enabled = new BindableBool(true); - protected override Drawable CreateContent() => new RoundedButton + protected override Drawable CreateContent() { - Width = 400, - Text = "Test button", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Enabled = { BindTarget = enabled }, - }; + return new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new RoundedButton + { + Width = 400, + Text = "Test button", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Enabled = { BindTarget = enabled }, + }, + new SettingsButton + { + Text = "Test button", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Enabled = { BindTarget = enabled }, + }, + } + }; + } [Test] public void TestDisabled() @@ -34,7 +54,8 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestBackgroundColour() { AddStep("set red scheme", () => CreateThemedContent(OverlayColourScheme.Red)); - AddAssert("first button has correct colour", () => Cell(0, 1).ChildrenOfType().First().BackgroundColour == new OverlayColourProvider(OverlayColourScheme.Red).Highlight1); + AddAssert("rounded button has correct colour", () => Cell(0, 1).ChildrenOfType().First().BackgroundColour == new OsuColour().Blue3); + AddAssert("settings button has correct colour", () => Cell(0, 1).ChildrenOfType().First().BackgroundColour == new OverlayColourProvider(OverlayColourScheme.Red).Highlight1); } } } From 17206bbebf4635a635b612255ec8112466dd14c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 16:15:24 +0900 Subject: [PATCH 1244/1276] Update screen padding and colouring to better match new designs --- .../Edit/DistancedHitObjectComposer.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 +-- .../Screens/Edit/EditorScreenWithTimeline.cs | 13 ++----- osu.Game/Screens/Edit/Timing/TimingScreen.cs | 36 +++++++++---------- 4 files changed, 22 insertions(+), 33 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 5e6d9dbe34..7019dad803 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Edit { AddInternal(RightSideToolboxContainer = new ExpandingToolboxContainer(130, 250) { - Padding = new MarginPadding { Right = 10 }, + Padding = new MarginPadding(10), Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index f8d796a778..f6fdb228ce 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -114,9 +114,9 @@ namespace osu.Game.Rulesets.Edit .WithChild(BlueprintContainer = CreateBlueprintContainer()) } }, - new ExpandingToolboxContainer(80, 200) + new ExpandingToolboxContainer(90, 200) { - Padding = new MarginPadding { Left = 10 }, + Padding = new MarginPadding(10), Children = new Drawable[] { new EditorToolboxGroup("toolbox (1-9)") diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 0d59a7a1a8..5482aa4ff0 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -3,14 +3,13 @@ using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components.Timeline; -using osuTK.Graphics; namespace osu.Game.Screens.Edit { @@ -33,7 +32,7 @@ namespace osu.Game.Screens.Edit private LoadingSpinner spinner; [BackgroundDependencyLoader(true)] - private void load([CanBeNull] BindableBeatDivisor beatDivisor) + private void load(OverlayColourProvider colourProvider, [CanBeNull] BindableBeatDivisor beatDivisor) { if (beatDivisor != null) this.beatDivisor.BindTo(beatDivisor); @@ -60,7 +59,7 @@ namespace osu.Game.Screens.Edit new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f) + Colour = colourProvider.Background4 }, new Container { @@ -106,12 +105,6 @@ namespace osu.Game.Screens.Edit Name = "Main content", RelativeSizeAxes = Axes.Both, Depth = float.MaxValue, - Padding = new MarginPadding - { - Horizontal = horizontal_margins, - Top = vertical_margins, - Bottom = vertical_margins - }, Child = spinner = new LoadingSpinner(true) { State = { Value = Visibility.Visible }, diff --git a/osu.Game/Screens/Edit/Timing/TimingScreen.cs b/osu.Game/Screens/Edit/Timing/TimingScreen.cs index 5f1fd14617..f71a8d7d22 100644 --- a/osu.Game/Screens/Edit/Timing/TimingScreen.cs +++ b/osu.Game/Screens/Edit/Timing/TimingScreen.cs @@ -16,7 +16,7 @@ using osuTK; namespace osu.Game.Screens.Edit.Timing { - public class TimingScreen : EditorRoundedScreen + public class TimingScreen : EditorScreenWithTimeline { [Cached] private Bindable selectedGroup = new Bindable(); @@ -26,27 +26,23 @@ namespace osu.Game.Screens.Edit.Timing { } - [BackgroundDependencyLoader] - private void load() + protected override Drawable CreateMainContent() => new GridContainer { - Add(new GridContainer + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] + new Dimension(), + new Dimension(GridSizeMode.Absolute, 350), + }, + Content = new[] + { + new Drawable[] { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 350), + new ControlPointList(), + new ControlPointSettings(), }, - Content = new[] - { - new Drawable[] - { - new ControlPointList(), - new ControlPointSettings(), - }, - } - }); - } + } + }; public class ControlPointList : CompositeDrawable { @@ -77,12 +73,12 @@ namespace osu.Game.Screens.Edit.Timing { new Box { - Colour = colours.Background3, + Colour = colours.Background4, RelativeSizeAxes = Axes.Both, }, new Box { - Colour = colours.Background2, + Colour = colours.Background3, RelativeSizeAxes = Axes.Y, Width = ControlPointTable.TIMING_COLUMN_WIDTH + margins, }, From a905731053c2dfbcdab9466f0da2fe97a2cb1925 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 16:32:44 +0900 Subject: [PATCH 1245/1276] Update timeline colours and paddings to match new designs --- .../Compose/Components/Timeline/TimelineArea.cs | 13 +++++++------ osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 5 ++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 1541ceade5..4cffebc57c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -2,12 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline @@ -27,10 +27,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } [BackgroundDependencyLoader] - private void load() + private void load(OverlayColourProvider colourProvider) { Masking = true; - CornerRadius = 5; OsuCheckbox waveformCheckbox; OsuCheckbox controlPointsCheckbox; @@ -41,7 +40,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex("111") + Colour = colourProvider.Background5 }, new GridContainer { @@ -55,12 +54,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, + Name = @"Toggle controls", Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex("222") + Colour = colourProvider.Background2, }, new FillFlowContainer { @@ -94,12 +94,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, + Name = @"Zoom controls", Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex("333") + Colour = colourProvider.Background3, }, new Container { diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 5482aa4ff0..186a4bddb3 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -15,8 +15,7 @@ namespace osu.Game.Screens.Edit { public abstract class EditorScreenWithTimeline : EditorScreen { - private const float vertical_margins = 10; - private const float horizontal_margins = 20; + private const float padding = 10; private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); @@ -66,7 +65,7 @@ namespace osu.Game.Screens.Edit Name = "Timeline content", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, + Padding = new MarginPadding { Horizontal = padding, Top = padding }, Child = new GridContainer { RelativeSizeAxes = Axes.X, From 1d196262d0a06e5d03db25ca1c99082a255c341e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 16:33:20 +0900 Subject: [PATCH 1246/1276] Remove unused `OnTimelineLoaded` flow --- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 186a4bddb3..0b80af68f2 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -125,18 +125,10 @@ namespace osu.Game.Screens.Edit mainContent.Add(content); content.FadeInFromZero(300, Easing.OutQuint); - LoadComponentAsync(new TimelineArea(CreateTimelineContent()), t => - { - timelineContainer.Add(t); - OnTimelineLoaded(t); - }); + LoadComponentAsync(new TimelineArea(CreateTimelineContent()), timelineContainer.Add); }); } - protected virtual void OnTimelineLoaded(TimelineArea timelineArea) - { - } - protected abstract Drawable CreateMainContent(); protected virtual Drawable CreateTimelineContent() => new Container(); From c02ec050569e86811c1ad937a41edcbc95900451 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 17:13:44 +0900 Subject: [PATCH 1247/1276] Remove remaining usage of `EditorRoundedScreen` --- osu.Game/Screens/Edit/EditorRoundedScreen.cs | 53 ------------------- osu.Game/Screens/Edit/Setup/SetupScreen.cs | 12 ++++- .../Screens/Edit/Setup/SetupScreenHeader.cs | 2 +- osu.Game/Screens/Edit/Setup/SetupSection.cs | 2 +- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 5 +- 5 files changed, 14 insertions(+), 60 deletions(-) delete mode 100644 osu.Game/Screens/Edit/EditorRoundedScreen.cs diff --git a/osu.Game/Screens/Edit/EditorRoundedScreen.cs b/osu.Game/Screens/Edit/EditorRoundedScreen.cs deleted file mode 100644 index 62f40f0325..0000000000 --- a/osu.Game/Screens/Edit/EditorRoundedScreen.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Overlays; - -namespace osu.Game.Screens.Edit -{ - public class EditorRoundedScreen : EditorScreen - { - public const int HORIZONTAL_PADDING = 100; - - private Container roundedContent; - - protected override Container Content => roundedContent; - - public EditorRoundedScreen(EditorScreenMode mode) - : base(mode) - { - } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - base.Content.Add(new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(50), - Child = new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 10, - Children = new Drawable[] - { - new Box - { - Colour = colourProvider.Background3, - RelativeSizeAxes = Axes.Both, - }, - roundedContent = new Container - { - RelativeSizeAxes = Axes.Both, - }, - } - } - }); - } - } -} diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 231d977aab..b95aabc1c4 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -4,11 +4,13 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; +using osu.Game.Overlays; namespace osu.Game.Screens.Edit.Setup { - public class SetupScreen : EditorRoundedScreen + public class SetupScreen : EditorScreen { [Cached] private SectionsContainer sections { get; } = new SetupScreenSectionsContainer(); @@ -22,7 +24,7 @@ namespace osu.Game.Screens.Edit.Setup } [BackgroundDependencyLoader] - private void load(EditorBeatmap beatmap) + private void load(EditorBeatmap beatmap, OverlayColourProvider colourProvider) { var sectionsEnumerable = new List { @@ -37,6 +39,12 @@ namespace osu.Game.Screens.Edit.Setup if (rulesetSpecificSection != null) sectionsEnumerable.Add(rulesetSpecificSection); + Add(new Box + { + Colour = colourProvider.Background2, + RelativeSizeAxes = Axes.Both, + }); + Add(sections.With(s => { s.RelativeSizeAxes = Axes.Both; diff --git a/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs b/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs index 2d0afda001..2412f1c4ed 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs @@ -93,7 +93,7 @@ namespace osu.Game.Screens.Edit.Setup public SetupScreenTabControl() { - TabContainer.Margin = new MarginPadding { Horizontal = EditorRoundedScreen.HORIZONTAL_PADDING }; + TabContainer.Margin = new MarginPadding { Horizontal = 100 }; AddInternal(background = new Box { diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs index 1dde6fb926..02bb05d227 100644 --- a/osu.Game/Screens/Edit/Setup/SetupSection.cs +++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs @@ -40,7 +40,7 @@ namespace osu.Game.Screens.Edit.Setup Padding = new MarginPadding { Vertical = 10, - Horizontal = EditorRoundedScreen.HORIZONTAL_PADDING + Horizontal = 100 }; InternalChild = new FillFlowContainer diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 08643eb8c1..9dc5a53907 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -11,7 +11,7 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Screens.Edit.Verify { [Cached] - public class VerifyScreen : EditorRoundedScreen + public class VerifyScreen : EditorScreen { public readonly Bindable SelectedIssue = new Bindable(); @@ -32,7 +32,6 @@ namespace osu.Game.Screens.Edit.Verify InterpretedDifficulty.Default = BeatmapDifficultyCache.GetDifficultyRating(EditorBeatmap.BeatmapInfo.StarRating); InterpretedDifficulty.SetDefault(); - IssueList = new IssueList(); Child = new Container { RelativeSizeAxes = Axes.Both, @@ -48,7 +47,7 @@ namespace osu.Game.Screens.Edit.Verify { new Drawable[] { - IssueList, + IssueList = new IssueList(), new IssueSettings(), }, } From 3f5ccd4db8c5aa6ae027b4c00a1df344891d3896 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 18:02:37 +0900 Subject: [PATCH 1248/1276] Update timing screen sections to newer design metrics --- osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs | 2 +- osu.Game/Screens/Edit/EditorRoundedScreenSettings.cs | 5 ++++- osu.Game/Screens/Edit/Timing/Section.cs | 11 ++++++----- .../Screens/Edit/Timing/SliderWithTextBoxInput.cs | 2 ++ 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs index 066e1a7978..2353d9e0e8 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs @@ -154,7 +154,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 [BackgroundDependencyLoader(true)] private void load(OverlayColourProvider? colourProvider, OsuColour osuColour) { - background.Colour = colourProvider?.Background4 ?? Color4Extensions.FromHex(@"1c2125"); + background.Colour = colourProvider?.Background5 ?? Color4Extensions.FromHex(@"1c2125"); descriptionText.Colour = osuColour.Yellow; } diff --git a/osu.Game/Screens/Edit/EditorRoundedScreenSettings.cs b/osu.Game/Screens/Edit/EditorRoundedScreenSettings.cs index cb17484d27..94a83a82aa 100644 --- a/osu.Game/Screens/Edit/EditorRoundedScreenSettings.cs +++ b/osu.Game/Screens/Edit/EditorRoundedScreenSettings.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; using osu.Game.Overlays; +using osuTK; namespace osu.Game.Screens.Edit { @@ -22,7 +23,7 @@ namespace osu.Game.Screens.Edit { new Box { - Colour = colours.Background4, + Colour = colours.Background6, RelativeSizeAxes = Axes.Both, }, new OsuScrollContainer @@ -33,6 +34,8 @@ namespace osu.Game.Screens.Edit RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, + Padding = new MarginPadding(10), + Spacing = new Vector2(10), Children = CreateSections() }, } diff --git a/osu.Game/Screens/Edit/Timing/Section.cs b/osu.Game/Screens/Edit/Timing/Section.cs index 8659b7aff6..139abfb187 100644 --- a/osu.Game/Screens/Edit/Timing/Section.cs +++ b/osu.Game/Screens/Edit/Timing/Section.cs @@ -44,9 +44,15 @@ namespace osu.Game.Screens.Edit.Timing AutoSizeAxes = Axes.Y; Masking = true; + CornerRadius = 5; InternalChildren = new Drawable[] { + new Box + { + Colour = colours.Background4, + RelativeSizeAxes = Axes.Both, + }, new Container { RelativeSizeAxes = Axes.X, @@ -69,11 +75,6 @@ namespace osu.Game.Screens.Edit.Timing AutoSizeAxes = Axes.Y, Children = new Drawable[] { - new Box - { - Colour = colours.Background3, - RelativeSizeAxes = Axes.Both, - }, Flow = new FillFlowContainer { Padding = new MarginPadding(20), diff --git a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs index 67f1dacec4..9f036f0215 100644 --- a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs @@ -11,6 +11,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays.Settings; using osu.Game.Utils; +using osuTK; namespace osu.Game.Screens.Edit.Timing { @@ -33,6 +34,7 @@ namespace osu.Game.Screens.Edit.Timing RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, + Spacing = new Vector2(20), Children = new Drawable[] { textBox = new LabelledTextBox From 648cee71061f043eb813c5eb7e01bebfd63ecb22 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 17:23:42 +0900 Subject: [PATCH 1249/1276] Remove editor screen transitions for now --- osu.Game/Screens/Edit/Editor.cs | 1 + osu.Game/Screens/Edit/EditorScreen.cs | 12 ++---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0bb3f51903..90bf9c6c3c 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -777,6 +777,7 @@ namespace osu.Game.Screens.Edit if ((currentScreen = screenContainer.SingleOrDefault(s => s.Type == e.NewValue)) != null) { screenContainer.ChangeChildDepth(currentScreen, lastScreen?.Depth + 1 ?? 0); + currentScreen.Show(); return; } diff --git a/osu.Game/Screens/Edit/EditorScreen.cs b/osu.Game/Screens/Edit/EditorScreen.cs index 2837cdcd9a..31c34edd7c 100644 --- a/osu.Game/Screens/Edit/EditorScreen.cs +++ b/osu.Game/Screens/Edit/EditorScreen.cs @@ -33,17 +33,9 @@ namespace osu.Game.Screens.Edit InternalChild = content = new PopoverContainer { RelativeSizeAxes = Axes.Both }; } - protected override void PopIn() - { - this.ScaleTo(1f, 200, Easing.OutQuint) - .FadeIn(200, Easing.OutQuint); - } + protected override void PopIn() => this.FadeIn(); - protected override void PopOut() - { - this.ScaleTo(0.98f, 200, Easing.OutQuint) - .FadeOut(200, Easing.OutQuint); - } + protected override void PopOut() => this.FadeOut(); #region Clipboard operations From 0bcf8c846c91fb5482615d39fb82b0b45498a82b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 May 2022 17:36:43 +0900 Subject: [PATCH 1250/1276] Change editor colour scheme to aquamarine --- osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs | 2 +- osu.Game/Overlays/OverlayColourProvider.cs | 4 ++++ osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Tests/Visual/EditorClockTestScene.cs | 4 ++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index cdd1175692..073a228224 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Editing private Triangle tickMarkerHead => tickSliderBar.ChildrenOfType().Single(); [Cached] - private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Green); + private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Aquamarine); [SetUp] public void SetUp() => Schedule(() => diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs index 7bddb924a0..a4f6527024 100644 --- a/osu.Game/Overlays/OverlayColourProvider.cs +++ b/osu.Game/Overlays/OverlayColourProvider.cs @@ -72,6 +72,9 @@ namespace osu.Game.Overlays case OverlayColourScheme.Green: return 125 / 360f; + case OverlayColourScheme.Aquamarine: + return 160 / 360f; + case OverlayColourScheme.Purple: return 255 / 360f; @@ -94,5 +97,6 @@ namespace osu.Game.Overlays Purple, Blue, Plum, + Aquamarine } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 90bf9c6c3c..7e7764397c 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -140,7 +140,7 @@ namespace osu.Game.Screens.Edit public readonly EditorClipboard Clipboard = new EditorClipboard(); [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); public Editor(EditorLoader loader = null) { diff --git a/osu.Game/Tests/Visual/EditorClockTestScene.cs b/osu.Game/Tests/Visual/EditorClockTestScene.cs index 66ab427565..542f06f86b 100644 --- a/osu.Game/Tests/Visual/EditorClockTestScene.cs +++ b/osu.Game/Tests/Visual/EditorClockTestScene.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Input.Events; using osu.Game.Beatmaps; +using osu.Game.Overlays; using osu.Game.Screens.Edit; namespace osu.Game.Tests.Visual @@ -15,6 +16,9 @@ namespace osu.Game.Tests.Visual /// public abstract class EditorClockTestScene : OsuManualInputManagerTestScene { + [Cached] + private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + protected readonly BindableBeatDivisor BeatDivisor = new BindableBeatDivisor(); protected new readonly EditorClock Clock; From fce527b0dfbcd056dc2b2590448ae2925c803d9e Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 24 May 2022 21:25:27 +0100 Subject: [PATCH 1251/1276] Schedule channel manager bindable events to ensure they happen after the first poll --- osu.Game/Overlays/ChatOverlayV2.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 441042432a..3aab81cb04 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -155,13 +155,15 @@ namespace osu.Game.Overlays chatHeight.BindValueChanged(height => { Height = height.NewValue; }, true); currentChannel.BindTo(channelManager.CurrentChannel); - currentChannel.BindValueChanged(currentChannelChanged, true); - joinedChannels.BindTo(channelManager.JoinedChannels); - joinedChannels.BindCollectionChanged(joinedChannelsChanged, true); - availableChannels.BindTo(channelManager.AvailableChannels); - availableChannels.BindCollectionChanged(availableChannelsChanged, true); + + Schedule(() => + { + currentChannel.BindValueChanged(currentChannelChanged, true); + joinedChannels.BindCollectionChanged(joinedChannelsChanged, true); + availableChannels.BindCollectionChanged(availableChannelsChanged, true); + }); channelList.OnRequestSelect += channel => channelManager.CurrentChannel.Value = channel; channelList.OnRequestLeave += channel => channelManager.LeaveChannel(channel); From c33aee17b002cc863ebb4826560de4257fe59ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 24 May 2022 23:14:28 +0200 Subject: [PATCH 1252/1276] Cache some more `OverlayColourProvider`s to fix tests --- .../Editor/TestSceneManiaComposeScreen.cs | 2 ++ osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs | 2 ++ osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs index 5dd7c23ab6..746bdae02e 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Database; +using osu.Game.Overlays; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Screens.Edit; @@ -45,6 +46,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor { (typeof(EditorBeatmap), editorBeatmap), (typeof(IBeatSnapProvider), editorBeatmap), + (typeof(OverlayColourProvider), new OverlayColourProvider(OverlayColourScheme.Green)), }, Child = new ComposeScreen { State = { Value = Visibility.Visible } }, }; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs index 30c8539d85..fa15c00cd4 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; +using osu.Game.Overlays; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; @@ -47,6 +48,7 @@ namespace osu.Game.Tests.Visual.Editing { (typeof(EditorBeatmap), editorBeatmap), (typeof(IBeatSnapProvider), editorBeatmap), + (typeof(OverlayColourProvider), new OverlayColourProvider(OverlayColourScheme.Green)), }, Child = new ComposeScreen { State = { Value = Visibility.Visible } }, }; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs index 20e58c3d2a..b78512e469 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineTickDisplay.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Overlays; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -14,6 +15,9 @@ namespace osu.Game.Tests.Visual.Editing { public override Drawable CreateTestComponent() => Empty(); // tick display is implicitly inside the timeline. + [Cached] + private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Green); + [BackgroundDependencyLoader] private void load() { From f6810d3f59d831e02ae1c4a42fe61001746858d8 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 24 May 2022 23:05:25 +0100 Subject: [PATCH 1253/1276] Add keyboard shortcuts to `ChatOverlayV2` with tests --- .../Visual/Online/TestSceneChatOverlayV2.cs | 72 +++++++++++++++++++ osu.Game/Overlays/ChatOverlayV2.cs | 52 +++++++++++++- 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs index 7268dd31f6..0580d20171 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlayV2.cs @@ -12,6 +12,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; using osu.Framework.Logging; using osu.Framework.Testing; using osu.Framework.Utils; @@ -417,6 +418,67 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); } + [Test] + public void TestKeyboardCloseAndRestoreChannel() + { + AddStep("Show overlay with channel 1", () => + { + channelManager.JoinChannel(testChannel1); + chatOverlay.Show(); + }); + AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + + AddStep("Press document close keys", () => InputManager.Keys(PlatformAction.DocumentClose)); + AddAssert("Listing is visible", () => listingIsVisible); + + AddStep("Press tab restore keys", () => InputManager.Keys(PlatformAction.TabRestore)); + AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + } + + [Test] + public void TestKeyboardNewChannel() + { + AddStep("Show overlay with channel 1", () => + { + channelManager.JoinChannel(testChannel1); + chatOverlay.Show(); + }); + AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + + AddStep("Press tab new keys", () => InputManager.Keys(PlatformAction.TabNew)); + AddAssert("Listing is visible", () => listingIsVisible); + } + + [Test] + public void TestKeyboardNextChannel() + { + Channel pmChannel1 = createPrivateChannel(); + Channel pmChannel2 = createPrivateChannel(); + + AddStep("Show overlay with channels", () => + { + channelManager.JoinChannel(testChannel1); + channelManager.JoinChannel(testChannel2); + channelManager.JoinChannel(pmChannel1); + channelManager.JoinChannel(pmChannel2); + chatOverlay.Show(); + }); + + AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + + AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); + AddAssert("Channel 2 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel2); + + AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); + AddAssert("PM Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == pmChannel1); + + AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); + AddAssert("PM Channel 2 displayed", () => channelIsVisible && currentDrawableChannel.Channel == pmChannel2); + + AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext)); + AddAssert("Channel 1 displayed", () => channelIsVisible && currentDrawableChannel.Channel == testChannel1); + } + private bool listingIsVisible => chatOverlay.ChildrenOfType().Single().State.Value == Visibility.Visible; @@ -467,6 +529,16 @@ namespace osu.Game.Tests.Visual.Online Type = ChannelType.Public, }; + private Channel createPrivateChannel() + { + int id = RNG.Next(0, 10000); + return new Channel(new APIUser + { + Id = id, + Username = $"test user {id}", + }); + } + private class TestChatOverlayV2 : ChatOverlayV2 { public bool SlowLoading { get; set; } diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 3aab81cb04..39717b1f31 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -13,6 +13,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Configuration; @@ -26,7 +28,7 @@ using osu.Game.Overlays.Chat.Listing; namespace osu.Game.Overlays { - public class ChatOverlayV2 : OsuFocusedOverlayContainer, INamedOverlayComponent + public class ChatOverlayV2 : OsuFocusedOverlayContainer, INamedOverlayComponent, IKeyBindingHandler { public string IconTexture => "Icons/Hexacons/messaging"; public LocalisableString Title => ChatStrings.HeaderTitle; @@ -197,6 +199,35 @@ namespace osu.Game.Overlays Show(); } + public bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case PlatformAction.TabNew: + currentChannel.Value = channelList.ChannelListingChannel; + return true; + + case PlatformAction.DocumentClose: + channelManager.LeaveChannel(currentChannel.Value); + return true; + + case PlatformAction.TabRestore: + channelManager.JoinLastClosedChannel(); + return true; + + case PlatformAction.DocumentNext: + cycleChannel(); + return true; + + default: + return false; + } + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + protected override bool OnDragStart(DragStartEvent e) { isDraggingTopBar = topBar.IsHovered; @@ -341,7 +372,7 @@ namespace osu.Game.Overlays private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) => channelListing.UpdateAvailableChannels(channelManager.AvailableChannels); - private IEnumerable filterChannels(IList channels) + private IEnumerable filterChannels(IEnumerable channels) => channels.Cast().Where(c => c.Type == ChannelType.Public || c.Type == ChannelType.PM); private void handleChatMessage(string message) @@ -354,5 +385,22 @@ namespace osu.Game.Overlays else channelManager.PostMessage(message); } + + private void cycleChannel() + { + List overlayChannels = filterChannels(channelManager.JoinedChannels).ToList(); + + if (overlayChannels.Count < 2) + return; + + int currentIdx = overlayChannels.IndexOf(currentChannel.Value); + int nextIdx = currentIdx + 1; + + // Cycle the list when reaching the end + if (nextIdx > overlayChannels.Count - 1) + nextIdx = 0; + + currentChannel.Value = overlayChannels[nextIdx]; + } } } From de5d2df100a58a5dcd407ab434f2512be215d400 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 May 2022 12:17:11 +0900 Subject: [PATCH 1254/1276] Move UI scale setup to the beginning of the first run setup --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 4 ++-- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 48b5690243..d09efdc925 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -185,7 +185,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("step to next", () => overlay.NextButton.TriggerClick()); - AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenBeatmaps); + AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenUIScale); AddStep("hide", () => overlay.Hide()); AddAssert("overlay hidden", () => overlay.State.Value == Visibility.Hidden); @@ -195,7 +195,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("run notification action", () => lastNotification.Activated()); AddAssert("overlay shown", () => overlay.State.Value == Visibility.Visible); - AddAssert("is resumed", () => overlay.CurrentScreen is ScreenBeatmaps); + AddAssert("is resumed", () => overlay.CurrentScreen is ScreenUIScale); } // interface mocks break hot reload, mocking this stub implementation instead works around it. diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 7b0de4affe..a5bece0832 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -76,10 +76,10 @@ namespace osu.Game.Overlays private void load(OsuColour colours, LegacyImportManager? legacyImportManager) { steps.Add(typeof(ScreenWelcome)); + steps.Add(typeof(ScreenUIScale)); steps.Add(typeof(ScreenBeatmaps)); if (legacyImportManager?.SupportsImportFromStable == true) steps.Add(typeof(ScreenImportFromStable)); - steps.Add(typeof(ScreenUIScale)); steps.Add(typeof(ScreenBehaviour)); Header.Title = FirstRunSetupOverlayStrings.FirstRunSetupTitle; From 87959a59d980286daf6c082ef3edda8494e94bf6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 May 2022 18:28:29 +0900 Subject: [PATCH 1255/1276] Add missing "announce" channel type Of note, this doesn't mean the channels will display, but it does fix parsing errors which cause the whole chat display to fail. --- osu.Game/Online/Chat/ChannelType.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Chat/ChannelType.cs b/osu.Game/Online/Chat/ChannelType.cs index 151efc4645..bd628e90c4 100644 --- a/osu.Game/Online/Chat/ChannelType.cs +++ b/osu.Game/Online/Chat/ChannelType.cs @@ -13,5 +13,6 @@ namespace osu.Game.Online.Chat PM, Group, System, + Announce, } } From a92089c443b9cd256f145aef6711b76bd4e18579 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 May 2022 18:42:33 +0900 Subject: [PATCH 1256/1276] Change filter method to consider all non-system channels as joinable --- osu.Game/Overlays/ChatOverlayV2.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 39717b1f31..a23595e1c0 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -342,7 +342,7 @@ namespace osu.Game.Overlays switch (args.Action) { case NotifyCollectionChangedAction.Add: - IEnumerable newChannels = filterChannels(args.NewItems); + IEnumerable newChannels = filterToChatChannels(args.NewItems); foreach (var channel in newChannels) channelList.AddChannel(channel); @@ -350,7 +350,7 @@ namespace osu.Game.Overlays break; case NotifyCollectionChangedAction.Remove: - IEnumerable leftChannels = filterChannels(args.OldItems); + IEnumerable leftChannels = filterToChatChannels(args.OldItems); foreach (var channel in leftChannels) { @@ -372,8 +372,6 @@ namespace osu.Game.Overlays private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) => channelListing.UpdateAvailableChannels(channelManager.AvailableChannels); - private IEnumerable filterChannels(IEnumerable channels) - => channels.Cast().Where(c => c.Type == ChannelType.Public || c.Type == ChannelType.PM); private void handleChatMessage(string message) { @@ -402,5 +400,7 @@ namespace osu.Game.Overlays currentChannel.Value = overlayChannels[nextIdx]; } + + private IEnumerable filterToChatChannels(IEnumerable channels) => channels.Cast().Where(c => c.Type != ChannelType.System); } } From 49ab031e75f0398ed321b0664d68acee9aba5b31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 May 2022 18:42:49 +0900 Subject: [PATCH 1257/1276] Allow traversing in both directions using ctrl-tab shortcuts --- osu.Game/Overlays/ChatOverlayV2.cs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index a23595e1c0..d0355e0165 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -215,8 +215,12 @@ namespace osu.Game.Overlays channelManager.JoinLastClosedChannel(); return true; + case PlatformAction.DocumentPrevious: + cycleChannel(-1); + return true; + case PlatformAction.DocumentNext: - cycleChannel(); + cycleChannel(1); return true; default: @@ -372,7 +376,6 @@ namespace osu.Game.Overlays private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) => channelListing.UpdateAvailableChannels(channelManager.AvailableChannels); - private void handleChatMessage(string message) { if (string.IsNullOrWhiteSpace(message)) @@ -384,21 +387,16 @@ namespace osu.Game.Overlays channelManager.PostMessage(message); } - private void cycleChannel() + private void cycleChannel(int direction) { - List overlayChannels = filterChannels(channelManager.JoinedChannels).ToList(); + List overlayChannels = filterToChatChannels(channelManager.JoinedChannels).ToList(); if (overlayChannels.Count < 2) return; - int currentIdx = overlayChannels.IndexOf(currentChannel.Value); - int nextIdx = currentIdx + 1; + int currentIndex = overlayChannels.IndexOf(currentChannel.Value); - // Cycle the list when reaching the end - if (nextIdx > overlayChannels.Count - 1) - nextIdx = 0; - - currentChannel.Value = overlayChannels[nextIdx]; + currentChannel.Value = overlayChannels[(currentIndex + direction + overlayChannels.Count) % overlayChannels.Count]; } private IEnumerable filterToChatChannels(IEnumerable channels) => channels.Cast().Where(c => c.Type != ChannelType.System); From f1d4902be524cbd65a73c8298739ba36f1990ef4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 May 2022 19:25:23 +0900 Subject: [PATCH 1258/1276] Remove unnecessary bindable cast --- osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs index 0830ba8cf4..c80d3c4261 100644 --- a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs @@ -47,7 +47,7 @@ namespace osu.Game.Screens.Edit.Timing private double selectedGroupStartTime; private double selectedGroupEndTime; - private readonly BindableList controlPointGroups = new BindableList(); + private readonly IBindableList controlPointGroups = new BindableList(); public WaveformComparisonDisplay() { @@ -83,7 +83,7 @@ namespace osu.Game.Screens.Edit.Timing selectedGroup.BindValueChanged(_ => updateTimingGroup(), true); - ((IBindableList)controlPointGroups).BindTo(editorBeatmap.ControlPointInfo.Groups); + controlPointGroups.BindTo(editorBeatmap.ControlPointInfo.Groups); controlPointGroups.BindCollectionChanged((_, __) => updateTimingGroup()); beatLength.BindValueChanged(_ => showFrom(lastDisplayedBeatIndex), true); From 795c9fcbc9cfa9345d9ed1cbde3a8e9fc4a7606d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 May 2022 19:38:50 +0900 Subject: [PATCH 1259/1276] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index aaea784852..d5a77c6349 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5bc65ca507..32a0adb859 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 3597e7e5c0..112b5b4615 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 4a36f3aa4c5f7c6af19627ed38ccc48dc353a39f Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 25 May 2022 12:29:04 +0100 Subject: [PATCH 1260/1276] Ensure channel traversal shortcut is using visual order --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 7 +++++++ osu.Game/Overlays/ChatOverlayV2.cs | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index d1ceae604c..f212f5b162 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using System.Linq; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -22,6 +23,8 @@ namespace osu.Game.Overlays.Chat.ChannelList public Action? OnRequestSelect; public Action? OnRequestLeave; + public IEnumerable Channels => publicChannelFlow.Channels.Concat(privateChannelFlow.Channels); + public readonly ChannelListing.ChannelListingChannel ChannelListingChannel = new ChannelListing.ChannelListingChannel(); private readonly Dictionary channelMap = new Dictionary(); @@ -118,6 +121,10 @@ namespace osu.Game.Overlays.Chat.ChannelList private class ChannelListItemFlow : FillFlowContainer { + public IEnumerable Channels => Children.Where(c => c is ChannelListItem) + .Cast() + .Select(c => c.Channel); + public ChannelListItemFlow(string label) { Direction = FillDirection.Vertical; diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index d0355e0165..cc780073ce 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -389,7 +389,7 @@ namespace osu.Game.Overlays private void cycleChannel(int direction) { - List overlayChannels = filterToChatChannels(channelManager.JoinedChannels).ToList(); + List overlayChannels = channelList.Channels.ToList(); if (overlayChannels.Count < 2) return; @@ -399,6 +399,7 @@ namespace osu.Game.Overlays currentChannel.Value = overlayChannels[(currentIndex + direction + overlayChannels.Count) % overlayChannels.Count]; } - private IEnumerable filterToChatChannels(IEnumerable channels) => channels.Cast().Where(c => c.Type != ChannelType.System); + private IEnumerable filterToChatChannels(IEnumerable channels) + => channels.Cast().Where(c => c.Type == ChannelType.PM || c.Type == ChannelType.Public); } } From cd90d2931534bf8210fedfc6a4c2efbb5863d05a Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 25 May 2022 12:37:28 +0100 Subject: [PATCH 1261/1276] Ensure channel traversal shortcut scrolls channel list item into view --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 5 ++++- osu.Game/Overlays/ChatOverlayV2.cs | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index f212f5b162..2798841925 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -29,6 +29,7 @@ namespace osu.Game.Overlays.Chat.ChannelList private readonly Dictionary channelMap = new Dictionary(); + private OsuScrollContainer scroll = null!; private ChannelListItemFlow publicChannelFlow = null!; private ChannelListItemFlow privateChannelFlow = null!; private ChannelListItem selector = null!; @@ -43,7 +44,7 @@ namespace osu.Game.Overlays.Chat.ChannelList RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background6, }, - new OsuScrollContainer + scroll = new OsuScrollContainer { Padding = new MarginPadding { Vertical = 7 }, RelativeSizeAxes = Axes.Both, @@ -104,6 +105,8 @@ namespace osu.Game.Overlays.Chat.ChannelList return channelMap[channel]; } + public void ScrollChannelIntoView(Channel channel) => scroll.ScrollIntoView(this.GetItem(channel)); + private ChannelListItemFlow getFlowForChannel(Channel channel) { switch (channel.Type) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index cc780073ce..ad7a856dbc 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -397,6 +397,8 @@ namespace osu.Game.Overlays int currentIndex = overlayChannels.IndexOf(currentChannel.Value); currentChannel.Value = overlayChannels[(currentIndex + direction + overlayChannels.Count) % overlayChannels.Count]; + + channelList.ScrollChannelIntoView(currentChannel.Value); } private IEnumerable filterToChatChannels(IEnumerable channels) From c4cae7f677968a72a4363ce9970f00b7891b9c2b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 May 2022 22:19:24 +0900 Subject: [PATCH 1262/1276] Update `EditorMenuBar` to match new design language --- .../Edit/Components/Menus/EditorMenuBar.cs | 107 +++++++----------- 1 file changed, 44 insertions(+), 63 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs index 2a8435ff47..440071bc4c 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs @@ -2,16 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Components.Menus { @@ -24,7 +21,12 @@ namespace osu.Game.Screens.Edit.Components.Menus MaskingContainer.CornerRadius = 0; ItemsContainer.Padding = new MarginPadding { Left = 100 }; - BackgroundColour = Color4Extensions.FromHex("111"); + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + BackgroundColour = colourProvider.Background3; } protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu(); @@ -33,29 +35,26 @@ namespace osu.Game.Screens.Edit.Components.Menus private class DrawableEditorBarMenuItem : DrawableOsuMenuItem { - private BackgroundBox background; - public DrawableEditorBarMenuItem(MenuItem item) : base(item) { - Anchor = Anchor.CentreLeft; - Origin = Anchor.CentreLeft; - - StateChanged += stateChanged; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider) { - ForegroundColour = colours.BlueLight; - BackgroundColour = Color4.Transparent; - ForegroundColourHover = Color4.White; - BackgroundColourHover = colours.Gray3; + ForegroundColour = colourProvider.Light3; + BackgroundColour = colourProvider.Background2; + ForegroundColourHover = colourProvider.Content1; + BackgroundColourHover = colourProvider.Background1; } - public override void SetFlowDirection(Direction direction) + protected override void LoadComplete() { - AutoSizeAxes = Axes.Both; + base.LoadComplete(); + + Foreground.Anchor = Anchor.CentreLeft; + Foreground.Origin = Anchor.CentreLeft; } protected override void UpdateBackgroundColour() @@ -74,54 +73,16 @@ namespace osu.Game.Screens.Edit.Components.Menus base.UpdateForegroundColour(); } - private void stateChanged(MenuItemState newState) - { - if (newState == MenuItemState.Selected) - background.Expand(); - else - background.Contract(); - } - - protected override Drawable CreateBackground() => background = new BackgroundBox(); protected override DrawableOsuMenuItem.TextContainer CreateTextContainer() => new TextContainer(); private new class TextContainer : DrawableOsuMenuItem.TextContainer { public TextContainer() { - NormalText.Font = NormalText.Font.With(size: 14); - BoldText.Font = BoldText.Font.With(size: 14); - NormalText.Margin = BoldText.Margin = new MarginPadding { Horizontal = 10, Vertical = MARGIN_VERTICAL }; + NormalText.Font = OsuFont.TorusAlternate; + BoldText.Font = OsuFont.TorusAlternate.With(weight: FontWeight.Bold); } } - - private class BackgroundBox : CompositeDrawable - { - private readonly Container innerBackground; - - public BackgroundBox() - { - RelativeSizeAxes = Axes.Both; - Masking = true; - InternalChild = innerBackground = new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 4, - Child = new Box { RelativeSizeAxes = Axes.Both } - }; - } - - /// - /// Expands the background such that it doesn't show the bottom corners. - /// - public void Expand() => innerBackground.Height = 2; - - /// - /// Contracts the background such that it shows the bottom corners. - /// - public void Contract() => innerBackground.Height = 1; - } } private class SubMenu : OsuMenu @@ -129,14 +90,15 @@ namespace osu.Game.Screens.Edit.Components.Menus public SubMenu() : base(Direction.Vertical) { - OriginPosition = new Vector2(5, 1); - ItemsContainer.Padding = new MarginPadding { Top = 5, Bottom = 5 }; + ItemsContainer.Padding = new MarginPadding(); + + MaskingContainer.CornerRadius = 0; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider) { - BackgroundColour = colours.Gray3; + BackgroundColour = colourProvider.Background2; } protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu(); @@ -147,9 +109,27 @@ namespace osu.Game.Screens.Edit.Components.Menus { case EditorMenuItemSpacer spacer: return new DrawableSpacer(spacer); + + default: + return new EditorMenuItem(item); + } + } + + private class EditorMenuItem : DrawableOsuMenuItem + { + public EditorMenuItem(MenuItem item) + : base(item) + { } - return base.CreateDrawableMenuItem(item); + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + BackgroundColour = colourProvider.Background2; + BackgroundColourHover = colourProvider.Background1; + + Foreground.Padding = new MarginPadding { Vertical = 2 }; + } } private class DrawableSpacer : DrawableOsuMenuItem @@ -157,6 +137,7 @@ namespace osu.Game.Screens.Edit.Components.Menus public DrawableSpacer(MenuItem item) : base(item) { + Scale = new Vector2(1, 0.3f); } protected override bool OnHover(HoverEvent e) => true; From 206f2ca208fafa01fbfbf616077027ba8360dc41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 May 2022 22:20:33 +0900 Subject: [PATCH 1263/1276] Rename screen switcher control to be in line with naming expectations --- ...nSelectionTabControl.cs => EditorScreenSwitcherControl.cs} | 4 ++-- osu.Game/Screens/Edit/Editor.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename osu.Game/Screens/Edit/Components/Menus/{ScreenSelectionTabControl.cs => EditorScreenSwitcherControl.cs} (94%) diff --git a/osu.Game/Screens/Edit/Components/Menus/ScreenSelectionTabControl.cs b/osu.Game/Screens/Edit/Components/Menus/EditorScreenSwitcherControl.cs similarity index 94% rename from osu.Game/Screens/Edit/Components/Menus/ScreenSelectionTabControl.cs rename to osu.Game/Screens/Edit/Components/Menus/EditorScreenSwitcherControl.cs index b8bc5cdf36..28a8efcdd2 100644 --- a/osu.Game/Screens/Edit/Components/Menus/ScreenSelectionTabControl.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorScreenSwitcherControl.cs @@ -13,9 +13,9 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Components.Menus { - public class ScreenSelectionTabControl : OsuTabControl + public class EditorScreenSwitcherControl : OsuTabControl { - public ScreenSelectionTabControl() + public EditorScreenSwitcherControl() { AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 7e7764397c..bdf204c1b6 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -278,7 +278,7 @@ namespace osu.Game.Screens.Edit } } }, - new ScreenSelectionTabControl + new EditorScreenSwitcherControl { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, From 463b8130c8351e14d7c7295e54b0ea886739beee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 May 2022 22:31:27 +0900 Subject: [PATCH 1264/1276] Update editor screen switcher to match new design language --- .../Menus/EditorScreenSwitcherControl.cs | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorScreenSwitcherControl.cs b/osu.Game/Screens/Edit/Components/Menus/EditorScreenSwitcherControl.cs index 28a8efcdd2..8b868a4649 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorScreenSwitcherControl.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorScreenSwitcherControl.cs @@ -2,14 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Components.Menus { @@ -22,22 +21,19 @@ namespace osu.Game.Screens.Edit.Components.Menus TabContainer.RelativeSizeAxes &= ~Axes.X; TabContainer.AutoSizeAxes = Axes.X; - TabContainer.Padding = new MarginPadding(); - - AddInternal(new Box - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = 1, - Colour = Color4.White.Opacity(0.2f), - }); + TabContainer.Padding = new MarginPadding(10); } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider) { - AccentColour = colours.Yellow; + AccentColour = colourProvider.Light3; + + AddInternal(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background2, + }); } protected override Dropdown CreateDropdown() => null; @@ -54,6 +50,15 @@ namespace osu.Game.Screens.Edit.Components.Menus Text.Margin = new MarginPadding(); Text.Anchor = Anchor.CentreLeft; Text.Origin = Anchor.CentreLeft; + + Text.Font = OsuFont.TorusAlternate; + + Bar.Expire(); + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { } protected override void OnActivated() From 91834f03190bfe66303975a54c3c07fbfb415b6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 May 2022 22:56:47 +0900 Subject: [PATCH 1265/1276] Fix missing colour dependency in menu bar test scene --- osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs index 3cb44d9ae8..ad6fc55a32 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osu.Game.Screens.Edit.Components.Menus; namespace osu.Game.Tests.Visual.Editing @@ -13,6 +15,9 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneEditorMenuBar : OsuTestScene { + [Cached] + private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + public TestSceneEditorMenuBar() { Add(new Container From c0da05dda0454690d11d3d5536d322c80cced4c6 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 25 May 2022 15:15:26 +0100 Subject: [PATCH 1266/1276] Use an array of channels to exclude from the overlay --- osu.Game/Overlays/ChatOverlayV2.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index ad7a856dbc..9ca4d9cb6c 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -56,6 +56,13 @@ namespace osu.Game.Overlays private const float side_bar_width = 190; private const float chat_bar_height = 60; + private readonly ChannelType[] excluded_channel_types = new[] + { + ChannelType.Multiplayer, + ChannelType.Spectator, + ChannelType.Temporary, + }; + [Resolved] private OsuConfigManager config { get; set; } = null!; @@ -402,6 +409,6 @@ namespace osu.Game.Overlays } private IEnumerable filterToChatChannels(IEnumerable channels) - => channels.Cast().Where(c => c.Type == ChannelType.PM || c.Type == ChannelType.Public); + => channels.Cast().Where(c => !excluded_channel_types.Contains(c.Type)); } } From 997fc716e8609d84e157cf602e468699dff4d04b Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 25 May 2022 15:15:46 +0100 Subject: [PATCH 1267/1276] Split out the channel list labels from the channel list item flow --- .../Overlays/Chat/ChannelList/ChannelList.cs | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 2798841925..21b2251aca 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -57,12 +57,14 @@ namespace osu.Game.Overlays.Chat.ChannelList AutoSizeAxes = Axes.Y, Children = new Drawable[] { - publicChannelFlow = new ChannelListItemFlow("CHANNELS"), + new ChannelListLabel("CHANNELS"), + publicChannelFlow = new ChannelListItemFlow(), selector = new ChannelListItem(ChannelListingChannel) { Margin = new MarginPadding { Bottom = 10 }, }, - privateChannelFlow = new ChannelListItemFlow("DIRECT MESSAGES"), + new ChannelListLabel("DIRECT MESSAGES"), + privateChannelFlow = new ChannelListItemFlow(), }, }, }, @@ -105,7 +107,7 @@ namespace osu.Game.Overlays.Chat.ChannelList return channelMap[channel]; } - public void ScrollChannelIntoView(Channel channel) => scroll.ScrollIntoView(this.GetItem(channel)); + public void ScrollChannelIntoView(Channel channel) => scroll.ScrollIntoView(GetItem(channel)); private ChannelListItemFlow getFlowForChannel(Channel channel) { @@ -118,28 +120,29 @@ namespace osu.Game.Overlays.Chat.ChannelList return privateChannelFlow; default: - throw new ArgumentOutOfRangeException(); + return publicChannelFlow; } } - private class ChannelListItemFlow : FillFlowContainer + private class ChannelListLabel : OsuSpriteText { - public IEnumerable Channels => Children.Where(c => c is ChannelListItem) - .Cast() - .Select(c => c.Channel); + public ChannelListLabel(string label) + { + Text = label; + Margin = new MarginPadding { Left = 18, Bottom = 5 }; + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold); + } + } - public ChannelListItemFlow(string label) + private class ChannelListItemFlow : FillFlowContainer + { + public IEnumerable Channels => Children.Select(c => c.Channel); + + public ChannelListItemFlow() { Direction = FillDirection.Vertical; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - - Add(new OsuSpriteText - { - Text = label, - Margin = new MarginPadding { Left = 18, Bottom = 5 }, - Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), - }); } } } From f94afa5f8cf0b384322ba12a1ca527b36bae1580 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 25 May 2022 18:28:18 +0100 Subject: [PATCH 1268/1276] Code quality --- osu.Game/Overlays/ChatOverlayV2.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 9ca4d9cb6c..68199c9d25 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays private const float side_bar_width = 190; private const float chat_bar_height = 60; - private readonly ChannelType[] excluded_channel_types = new[] + private readonly ChannelType[] excludedChannelTypes = { ChannelType.Multiplayer, ChannelType.Spectator, @@ -409,6 +409,6 @@ namespace osu.Game.Overlays } private IEnumerable filterToChatChannels(IEnumerable channels) - => channels.Cast().Where(c => !excluded_channel_types.Contains(c.Type)); + => channels.Cast().Where(c => !excludedChannelTypes.Contains(c.Type)); } } From 4f8b6b52ba1757396e1ef407d47bed974d5568f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 25 May 2022 21:49:31 +0200 Subject: [PATCH 1269/1276] Add failing test coverage for select/deselect all enable behaviour --- .../Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs | 4 ++++ .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index c37bff2066..44ae467c80 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -73,12 +73,15 @@ namespace osu.Game.Tests.Visual.Multiplayer { createFreeModSelect(); + AddAssert("select all button enabled", () => this.ChildrenOfType().ElementAt(1).Enabled.Value); + AddStep("click select all button", () => { InputManager.MoveMouseTo(this.ChildrenOfType().ElementAt(1)); InputManager.Click(MouseButton.Left); }); AddUntilStep("all mods selected", assertAllAvailableModsSelected); + AddAssert("select all button disabled", () => !this.ChildrenOfType().ElementAt(1).Enabled.Value); AddStep("click deselect all button", () => { @@ -86,6 +89,7 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); AddUntilStep("all mods deselected", () => !freeModSelectOverlay.SelectedMods.Value.Any()); + AddAssert("select all button enabled", () => this.ChildrenOfType().ElementAt(1).Enabled.Value); } private void createFreeModSelect() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 75e30f76c3..0c3acded88 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -435,8 +435,11 @@ namespace osu.Game.Tests.Visual.UserInterface createScreen(); changeRuleset(0); + AddAssert("deselect all button disabled", () => !this.ChildrenOfType().Last().Enabled.Value); + AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddAssert("deselect all button enabled", () => this.ChildrenOfType().Last().Enabled.Value); AddStep("click deselect all button", () => { @@ -444,6 +447,7 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.Click(MouseButton.Left); }); AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any()); + AddAssert("deselect all button disabled", () => !this.ChildrenOfType().Last().Enabled.Value); } [Test] From a4bd399b0cbd6a1c14a20fbb8fc2b431f2e97ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 25 May 2022 22:09:31 +0200 Subject: [PATCH 1270/1276] Split off "deselect all mods" button to separate class --- .../Overlays/Mods/DeselectAllModsButton.cs | 38 +++++++++++++++++++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 15 ++------ 2 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Overlays/Mods/DeselectAllModsButton.cs diff --git a/osu.Game/Overlays/Mods/DeselectAllModsButton.cs b/osu.Game/Overlays/Mods/DeselectAllModsButton.cs new file mode 100644 index 0000000000..39009c8656 --- /dev/null +++ b/osu.Game/Overlays/Mods/DeselectAllModsButton.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Bindables; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; +using osu.Game.Localisation; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Overlays.Mods +{ + public class DeselectAllModsButton : ShearedButton, IKeyBindingHandler + { + public DeselectAllModsButton(ModSelectOverlay modSelectOverlay) + : base(ModSelectOverlay.BUTTON_WIDTH) + { + Text = CommonStrings.DeselectAll; + Action = modSelectOverlay.DeselectAll; + } + + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Repeat || e.Action != GlobalAction.DeselectAllMods) + return false; + + TriggerClick(); + return true; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } +} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index f1a998bd3c..ea7f969450 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Mods { public abstract class ModSelectOverlay : ShearedOverlayContainer, ISamplePlaybackDisabler { - protected const int BUTTON_WIDTH = 200; + public const int BUTTON_WIDTH = 200; [Cached] public Bindable> SelectedMods { get; private set; } = new Bindable>(Array.Empty()); @@ -76,11 +76,7 @@ namespace osu.Game.Overlays.Mods }; } - yield return deselectAllButton = new ShearedButton(BUTTON_WIDTH) - { - Text = CommonStrings.DeselectAll, - Action = DeselectAll - }; + yield return new DeselectAllModsButton(this); } private readonly Bindable>> availableMods = new Bindable>>(); @@ -98,7 +94,6 @@ namespace osu.Game.Overlays.Mods private DifficultyMultiplierDisplay? multiplierDisplay; private ShearedToggleButton? customisationButton; - private ShearedButton? deselectAllButton; protected ModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green) : base(colourScheme) @@ -256,7 +251,7 @@ namespace osu.Game.Overlays.Mods /// /// Deselect all visible mods in all columns. /// - protected void DeselectAll() + public void DeselectAll() { foreach (var column in columnFlow.Columns) column.DeselectAll(); @@ -514,10 +509,6 @@ namespace osu.Game.Overlays.Mods hideOverlay(true); return true; } - - case GlobalAction.DeselectAllMods: - deselectAllButton?.TriggerClick(); - return true; } return base.OnPressed(e); From a3f29625587fd0cb52c8d4042dd1b48f82d62db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 25 May 2022 22:12:09 +0200 Subject: [PATCH 1271/1276] Disable "deselect all mods" button if none are selected --- osu.Game/Overlays/Mods/DeselectAllModsButton.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Overlays/Mods/DeselectAllModsButton.cs b/osu.Game/Overlays/Mods/DeselectAllModsButton.cs index 39009c8656..8288d34c95 100644 --- a/osu.Game/Overlays/Mods/DeselectAllModsButton.cs +++ b/osu.Game/Overlays/Mods/DeselectAllModsButton.cs @@ -15,11 +15,27 @@ namespace osu.Game.Overlays.Mods { public class DeselectAllModsButton : ShearedButton, IKeyBindingHandler { + private readonly Bindable> selectedMods = new Bindable>(); + public DeselectAllModsButton(ModSelectOverlay modSelectOverlay) : base(ModSelectOverlay.BUTTON_WIDTH) { Text = CommonStrings.DeselectAll; Action = modSelectOverlay.DeselectAll; + + selectedMods.BindTo(modSelectOverlay.SelectedMods); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedMods.BindValueChanged(_ => updateEnabledState(), true); + } + + private void updateEnabledState() + { + Enabled.Value = selectedMods.Value.Any(); } public bool OnPressed(KeyBindingPressEvent e) From f0303d76e8e8dfb4b174bec2bd15e5a9aae23f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 25 May 2022 22:18:30 +0200 Subject: [PATCH 1272/1276] Split off "select all mods" button to separate class --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- osu.Game/Overlays/Mods/SelectAllModsButton.cs | 35 +++++++++++++++++++ .../OnlinePlay/FreeModSelectOverlay.cs | 31 ++-------------- 3 files changed, 38 insertions(+), 30 deletions(-) create mode 100644 osu.Game/Overlays/Mods/SelectAllModsButton.cs diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ea7f969450..d8355a4dea 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -242,7 +242,7 @@ namespace osu.Game.Overlays.Mods /// /// Select all visible mods in all columns. /// - protected void SelectAll() + public void SelectAll() { foreach (var column in columnFlow.Columns) column.SelectAll(); diff --git a/osu.Game/Overlays/Mods/SelectAllModsButton.cs b/osu.Game/Overlays/Mods/SelectAllModsButton.cs new file mode 100644 index 0000000000..b9d6bf9633 --- /dev/null +++ b/osu.Game/Overlays/Mods/SelectAllModsButton.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; +using osu.Game.Screens.OnlinePlay; + +namespace osu.Game.Overlays.Mods +{ + public class SelectAllModsButton : ShearedButton, IKeyBindingHandler + { + public SelectAllModsButton(FreeModSelectOverlay modSelectOverlay) + : base(ModSelectOverlay.BUTTON_WIDTH) + { + Text = CommonStrings.SelectAll; + Action = modSelectOverlay.SelectAll; + } + + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Repeat || e.Action != PlatformAction.SelectAll) + return false; + + TriggerClick(); + return true; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index f494f9799a..7c9184cc0f 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -6,18 +6,14 @@ using osu.Game.Overlays; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; -using osu.Framework.Input; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; using osuTK.Input; -using osu.Game.Localisation; namespace osu.Game.Screens.OnlinePlay { - public class FreeModSelectOverlay : ModSelectOverlay, IKeyBindingHandler + public class FreeModSelectOverlay : ModSelectOverlay { protected override bool ShowTotalMultiplier => false; @@ -29,8 +25,6 @@ namespace osu.Game.Screens.OnlinePlay set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m); } - private ShearedButton selectAllButton; - public FreeModSelectOverlay() : base(OverlayColourScheme.Plum) { @@ -40,31 +34,10 @@ namespace osu.Game.Screens.OnlinePlay protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys); protected override IEnumerable CreateFooterButtons() => base.CreateFooterButtons().Prepend( - selectAllButton = new ShearedButton(BUTTON_WIDTH) + new SelectAllModsButton(this) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Text = CommonStrings.SelectAll, - Action = SelectAll }); - - public bool OnPressed(KeyBindingPressEvent e) - { - if (e.Repeat) - return false; - - switch (e.Action) - { - case PlatformAction.SelectAll: - selectAllButton.TriggerClick(); - return true; - } - - return false; - } - - public void OnReleased(KeyBindingReleaseEvent e) - { - } } } From 071e158a297139bff0812b567cf6f6064e98839e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 25 May 2022 22:20:10 +0200 Subject: [PATCH 1273/1276] Expose available mod state outwardly as a bindable --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 38 ++++++++++++++-------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d8355a4dea..4bad34d94f 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -34,6 +34,15 @@ namespace osu.Game.Overlays.Mods [Cached] public Bindable> SelectedMods { get; private set; } = new Bindable>(Array.Empty()); + /// + /// Contains a dictionary with the current of all mods applicable for the current ruleset. + /// + /// + /// Contrary to and , the instances + /// inside the objects are owned solely by this instance. + /// + public Bindable>> AvailableMods { get; } = new Bindable>>(new Dictionary>()); + private Func isValidMod = m => true; /// @@ -79,9 +88,9 @@ namespace osu.Game.Overlays.Mods yield return new DeselectAllModsButton(this); } - private readonly Bindable>> availableMods = new Bindable>>(); - private readonly Dictionary> localAvailableMods = new Dictionary>(); - private IEnumerable allLocalAvailableMods => localAvailableMods.SelectMany(pair => pair.Value); + private readonly Bindable>> globalAvailableMods = new Bindable>>(); + + private IEnumerable allAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value); private readonly BindableBool customisationVisible = new BindableBool(); @@ -204,13 +213,13 @@ namespace osu.Game.Overlays.Mods }) }; - availableMods.BindTo(game.AvailableMods); + globalAvailableMods.BindTo(game.AvailableMods); } protected override void LoadComplete() { // this is called before base call so that the mod state is populated early, and the transition in `PopIn()` can play out properly. - availableMods.BindValueChanged(_ => createLocalMods(), true); + globalAvailableMods.BindValueChanged(_ => createLocalMods(), true); base.LoadComplete(); @@ -275,9 +284,9 @@ namespace osu.Game.Overlays.Mods private void createLocalMods() { - localAvailableMods.Clear(); + var newLocalAvailableMods = new Dictionary>(); - foreach (var (modType, mods) in availableMods.Value) + foreach (var (modType, mods) in globalAvailableMods.Value) { var modStates = mods.SelectMany(ModUtils.FlattenMod) .Select(mod => new ModState(mod.DeepClone())) @@ -286,18 +295,19 @@ namespace osu.Game.Overlays.Mods foreach (var modState in modStates) modState.Active.BindValueChanged(_ => updateFromInternalSelection()); - localAvailableMods[modType] = modStates; + newLocalAvailableMods[modType] = modStates; } + AvailableMods.Value = newLocalAvailableMods; filterMods(); foreach (var column in columnFlow.Columns) - column.AvailableMods = localAvailableMods.GetValueOrDefault(column.ModType, Array.Empty()); + column.AvailableMods = AvailableMods.Value.GetValueOrDefault(column.ModType, Array.Empty()); } private void filterMods() { - foreach (var modState in allLocalAvailableMods) + foreach (var modState in allAvailableMods) modState.Filtered.Value = !modState.Mod.HasImplementation || !IsValidMod.Invoke(modState.Mod); } @@ -378,7 +388,7 @@ namespace osu.Game.Overlays.Mods var newSelection = new List(); - foreach (var modState in allLocalAvailableMods) + foreach (var modState in allAvailableMods) { var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == modState.Mod.GetType()); @@ -405,9 +415,9 @@ namespace osu.Game.Overlays.Mods if (externalSelectionUpdateInProgress) return; - var candidateSelection = allLocalAvailableMods.Where(modState => modState.Active.Value) - .Select(modState => modState.Mod) - .ToArray(); + var candidateSelection = allAvailableMods.Where(modState => modState.Active.Value) + .Select(modState => modState.Mod) + .ToArray(); SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection); } From a1b8fa09921666ee65531cea264aeade2aa5718b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 25 May 2022 22:24:26 +0200 Subject: [PATCH 1274/1276] Disable "select all mods" button if all are selected --- osu.Game/Overlays/Mods/SelectAllModsButton.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/osu.Game/Overlays/Mods/SelectAllModsButton.cs b/osu.Game/Overlays/Mods/SelectAllModsButton.cs index b9d6bf9633..f7078b2fa5 100644 --- a/osu.Game/Overlays/Mods/SelectAllModsButton.cs +++ b/osu.Game/Overlays/Mods/SelectAllModsButton.cs @@ -1,22 +1,48 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay; namespace osu.Game.Overlays.Mods { public class SelectAllModsButton : ShearedButton, IKeyBindingHandler { + private readonly Bindable> selectedMods = new Bindable>(); + private readonly Bindable>> availableMods = new Bindable>>(); + public SelectAllModsButton(FreeModSelectOverlay modSelectOverlay) : base(ModSelectOverlay.BUTTON_WIDTH) { Text = CommonStrings.SelectAll; Action = modSelectOverlay.SelectAll; + + selectedMods.BindTo(modSelectOverlay.SelectedMods); + availableMods.BindTo(modSelectOverlay.AvailableMods); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedMods.BindValueChanged(_ => Scheduler.AddOnce(updateEnabledState)); + availableMods.BindValueChanged(_ => Scheduler.AddOnce(updateEnabledState)); + updateEnabledState(); + } + + private void updateEnabledState() + { + Enabled.Value = availableMods.Value + .SelectMany(pair => pair.Value) + .Any(modState => !modState.Active.Value && !modState.Filtered.Value); } public bool OnPressed(KeyBindingPressEvent e) From af1d4d3aee582950473057e8733855c71ddbff62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 25 May 2022 22:27:54 +0200 Subject: [PATCH 1275/1276] Use more proper button subtypes in tests --- .../Multiplayer/TestSceneFreeModSelectOverlay.cs | 11 +++++------ .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 8 ++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index 44ae467c80..a8471edbf8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -9,7 +9,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Testing; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Mods; @@ -73,23 +72,23 @@ namespace osu.Game.Tests.Visual.Multiplayer { createFreeModSelect(); - AddAssert("select all button enabled", () => this.ChildrenOfType().ElementAt(1).Enabled.Value); + AddAssert("select all button enabled", () => this.ChildrenOfType().Single().Enabled.Value); AddStep("click select all button", () => { - InputManager.MoveMouseTo(this.ChildrenOfType().ElementAt(1)); + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); AddUntilStep("all mods selected", assertAllAvailableModsSelected); - AddAssert("select all button disabled", () => !this.ChildrenOfType().ElementAt(1).Enabled.Value); + AddAssert("select all button disabled", () => !this.ChildrenOfType().Single().Enabled.Value); AddStep("click deselect all button", () => { - InputManager.MoveMouseTo(this.ChildrenOfType().Last()); + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); AddUntilStep("all mods deselected", () => !freeModSelectOverlay.SelectedMods.Value.Any()); - AddAssert("select all button enabled", () => this.ChildrenOfType().ElementAt(1).Enabled.Value); + AddAssert("select all button enabled", () => this.ChildrenOfType().Single().Enabled.Value); } private void createFreeModSelect() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 0c3acded88..9bb02c3e75 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -435,19 +435,19 @@ namespace osu.Game.Tests.Visual.UserInterface createScreen(); changeRuleset(0); - AddAssert("deselect all button disabled", () => !this.ChildrenOfType().Last().Enabled.Value); + AddAssert("deselect all button disabled", () => !this.ChildrenOfType().Single().Enabled.Value); AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); - AddAssert("deselect all button enabled", () => this.ChildrenOfType().Last().Enabled.Value); + AddAssert("deselect all button enabled", () => this.ChildrenOfType().Single().Enabled.Value); AddStep("click deselect all button", () => { - InputManager.MoveMouseTo(this.ChildrenOfType().Last()); + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any()); - AddAssert("deselect all button disabled", () => !this.ChildrenOfType().Last().Enabled.Value); + AddAssert("deselect all button disabled", () => !this.ChildrenOfType().Single().Enabled.Value); } [Test] From a98d0cf0d8b6d8c91cdaff9626cc76859ca71537 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 May 2022 18:47:50 +0900 Subject: [PATCH 1276/1276] Simplify channel filter method --- osu.Game/Overlays/ChatOverlayV2.cs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlayV2.cs b/osu.Game/Overlays/ChatOverlayV2.cs index 68199c9d25..f2ec007b19 100644 --- a/osu.Game/Overlays/ChatOverlayV2.cs +++ b/osu.Game/Overlays/ChatOverlayV2.cs @@ -3,7 +3,6 @@ #nullable enable -using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; @@ -56,13 +55,6 @@ namespace osu.Game.Overlays private const float side_bar_width = 190; private const float chat_bar_height = 60; - private readonly ChannelType[] excludedChannelTypes = - { - ChannelType.Multiplayer, - ChannelType.Spectator, - ChannelType.Temporary, - }; - [Resolved] private OsuConfigManager config { get; set; } = null!; @@ -353,7 +345,7 @@ namespace osu.Game.Overlays switch (args.Action) { case NotifyCollectionChangedAction.Add: - IEnumerable newChannels = filterToChatChannels(args.NewItems); + IEnumerable newChannels = args.NewItems.OfType().Where(isChatChannel); foreach (var channel in newChannels) channelList.AddChannel(channel); @@ -361,7 +353,7 @@ namespace osu.Game.Overlays break; case NotifyCollectionChangedAction.Remove: - IEnumerable leftChannels = filterToChatChannels(args.OldItems); + IEnumerable leftChannels = args.OldItems.OfType().Where(isChatChannel); foreach (var channel in leftChannels) { @@ -408,7 +400,21 @@ namespace osu.Game.Overlays channelList.ScrollChannelIntoView(currentChannel.Value); } - private IEnumerable filterToChatChannels(IEnumerable channels) - => channels.Cast().Where(c => !excludedChannelTypes.Contains(c.Type)); + /// + /// Whether a channel should be displayed in this overlay, based on its type. + /// + private static bool isChatChannel(Channel channel) + { + switch (channel.Type) + { + case ChannelType.Multiplayer: + case ChannelType.Spectator: + case ChannelType.Temporary: + return false; + + default: + return true; + } + } } }