From 4f16da893286b09da833f3b3aa24037019fdb266 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 2 May 2022 20:04:34 +0800 Subject: [PATCH 01/78] 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 02/78] 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 03/78] 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 547038f13b9840e263ce1bc6c0d57841acf8aeb6 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 3 May 2022 14:28:53 +0800 Subject: [PATCH 04/78] 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 05/78] 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 06/78] 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 07/78] 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 8e96af8ff0b7caaaf7e2542856daa6a7ecd001af Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 4 May 2022 13:43:59 +0800 Subject: [PATCH 08/78] 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 09/78] 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 a17bbd62b7d05491a1ed2d5575819feb0c7fcdc5 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 4 May 2022 14:06:01 +0800 Subject: [PATCH 10/78] 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 de9b3d33ebfba7beefe36f659e3a8aa77ab6a7eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 May 2022 17:00:36 +0900 Subject: [PATCH 11/78] 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 12/78] 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 13/78] 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 14/78] 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 15/78] 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 269e15c167dc53a20afd3162e936b578d8fbae4a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 5 May 2022 11:42:50 +0300 Subject: [PATCH 16/78] 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 17/78] 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 18/78] 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 19/78] 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 20/78] 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 aafb363a3487e547ea349d0717f489557eb82252 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 6 May 2022 01:24:19 +0800 Subject: [PATCH 21/78] 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 22/78] 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 23/78] 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 16a6c11bc87a906b36e699fcd2101eac371dcb25 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Thu, 5 May 2022 21:39:31 +0100 Subject: [PATCH 24/78] 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 25/78] 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 26/78] 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 27/78] 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 28/78] 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 29/78] 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 30/78] 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 31/78] 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 32/78] 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 33/78] 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 34/78] 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 35/78] 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 36/78] 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 37/78] 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 38/78] 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 39/78] 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 40/78] 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 41/78] 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 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 42/78] 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 43/78] 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 44/78] 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 45/78] 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 46/78] 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 47/78] 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 48/78] 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 49/78] 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 50/78] 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 51/78] 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 52/78] 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 53/78] 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 54/78] 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 55/78] 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 56/78] 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 049fed42e2084497b6766d7af736026fe77b96b4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 May 2022 21:42:20 +0300 Subject: [PATCH 57/78] 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 58/78] 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 59/78] 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 60/78] 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 61/78] 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 62/78] 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 ce14fddcb2c2950285dd89d96b8587cd9c3fbf6f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 7 May 2022 14:02:07 +0900 Subject: [PATCH 63/78] 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 64/78] 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 65/78] 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 66/78] 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 67/78] 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 68/78] 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 69/78] 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 70/78] 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 71/78] 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 72/78] 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 73/78] 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 74/78] 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 75/78] 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 76/78] 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 77/78] 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 78/78] 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())