diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 1b70c949eb..6ba6ae82c8 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -7,6 +7,12 @@ "commands": [ "dotnet-cake" ] + }, + "dotnet-format": { + "version": "3.1.37601", + "commands": [ + "dotnet-format" + ] } } } \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index 0dd7ef8ed1..2c000d3881 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,16 +12,171 @@ trim_trailing_whitespace = true #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 -dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event,delegate -dotnet_naming_rule.public_members_pascalcase.severity = suggestion +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 + dotnet_naming_symbols.private_members.applicable_accessibilities = private -dotnet_naming_symbols.private_members.applicable_kinds = property,method,field,event,delegate -dotnet_naming_rule.private_members_camelcase.severity = suggestion +dotnet_naming_symbols.private_members.applicable_kinds = property,method,field,event +dotnet_naming_rule.private_members_camelcase.severity = warning dotnet_naming_rule.private_members_camelcase.symbols = private_members -dotnet_naming_rule.private_members_camelcase.style = camelcase \ No newline at end of file +dotnet_naming_rule.private_members_camelcase.style = camelcase + +dotnet_naming_symbols.local_function.applicable_kinds = local_function +dotnet_naming_rule.local_function_camelcase.severity = warning +dotnet_naming_rule.local_function_camelcase.symbols = local_function +dotnet_naming_rule.local_function_camelcase.style = camelcase + +#all_lower for private and local constants/static readonlys +dotnet_naming_style.all_lower.capitalization = all_lower +dotnet_naming_style.all_lower.word_separator = _ + +dotnet_naming_symbols.private_constants.applicable_accessibilities = private +dotnet_naming_symbols.private_constants.required_modifiers = const +dotnet_naming_symbols.private_constants.applicable_kinds = field +dotnet_naming_rule.private_const_all_lower.severity = warning +dotnet_naming_rule.private_const_all_lower.symbols = private_constants +dotnet_naming_rule.private_const_all_lower.style = all_lower + +dotnet_naming_symbols.private_static_readonly.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly.required_modifiers = static,readonly +dotnet_naming_symbols.private_static_readonly.applicable_kinds = field +dotnet_naming_rule.private_static_readonly_all_lower.severity = warning +dotnet_naming_rule.private_static_readonly_all_lower.symbols = private_static_readonly +dotnet_naming_rule.private_static_readonly_all_lower.style = all_lower + +dotnet_naming_symbols.local_constants.applicable_kinds = local +dotnet_naming_symbols.local_constants.required_modifiers = const +dotnet_naming_rule.local_const_all_lower.severity = warning +dotnet_naming_rule.local_const_all_lower.symbols = local_constants +dotnet_naming_rule.local_const_all_lower.style = all_lower + +#ALL_UPPER for non private constants/static readonlys +dotnet_naming_style.all_upper.capitalization = all_upper +dotnet_naming_style.all_upper.word_separator = _ + +dotnet_naming_symbols.public_constants.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.public_constants.required_modifiers = const +dotnet_naming_symbols.public_constants.applicable_kinds = field +dotnet_naming_rule.public_const_all_upper.severity = warning +dotnet_naming_rule.public_const_all_upper.symbols = public_constants +dotnet_naming_rule.public_const_all_upper.style = all_upper + +dotnet_naming_symbols.public_static_readonly.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.public_static_readonly.required_modifiers = static,readonly +dotnet_naming_symbols.public_static_readonly.applicable_kinds = field +dotnet_naming_rule.public_static_readonly_all_upper.severity = warning +dotnet_naming_rule.public_static_readonly_all_upper.symbols = public_static_readonly +dotnet_naming_rule.public_static_readonly_all_upper.style = all_upper + +#Roslyn formating options + +#Formatting - indentation options +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +#Formatting - new line options +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_open_brace = all +#csharp_new_line_before_members_in_anonymous_types = true +#csharp_new_line_before_members_in_object_initializers = true # Currently no effect in VS/dotnet format (16.4), and makes Rider confusing +csharp_new_line_between_query_expression_clauses = true + +#Formatting - organize using options +dotnet_sort_system_directives_first = true + +#Formatting - spacing options +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false + +#Formatting - wrapping options +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#Roslyn language styles + +#Style - type names +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +csharp_style_var_when_type_is_apparent = true:none +csharp_style_var_for_built_in_types = true:none +csharp_style_var_elsewhere = true:silent + +#Style - modifiers +dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning +csharp_preferred_modifier_order = public,private,protected,internal,new,abstract,virtual,sealed,override,static,readonly,extern,unsafe,volatile,async:warning + +#Style - parentheses +# Skipped because roslyn cannot separate +-*/ with << >> + +#Style - expression bodies +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_operators = true:silent +csharp_style_expression_bodied_properties = true:silent + +#Style - expression preferences +dotnet_style_object_initializer = true:warning +dotnet_style_collection_initializer = true:warning +dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_compound_assignment = true:silent + +#Style - null/type checks +dotnet_style_coalesce_expression = true:warning +dotnet_style_null_propagation = true:warning +csharp_style_pattern_matching_over_is_with_cast_check = true:silent +csharp_style_pattern_matching_over_as_with_null_check = true:silent +csharp_style_throw_expression = true:silent +csharp_style_conditional_delegate_call = true:suggestion + +#Style - unused +dotnet_code_quality_unused_parameters = non_public:silent +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_unused_value_assignment_preference = discard_variable:silent + +#Style - variable declaration +csharp_style_inlined_variable_declaration = true:silent +csharp_style_deconstructed_variable_declaration = true:silent + +#Style - other C# 7.x features +csharp_style_expression_bodied_local_functions = true:silent +dotnet_style_prefer_inferred_tuple_names = true:warning +csharp_prefer_simple_default_expression = true:warning +csharp_style_pattern_local_over_anonymous_function = true:silent +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent + +#Supressing roslyn built-in analyzers +# Suppress: EC112 + +#Field can be readonly +dotnet_diagnostic.IDE0044.severity = silent +#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 \ No newline at end of file diff --git a/README.md b/README.md index 67eb3254e1..a078265d6c 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh ## Requirements - A desktop platform with the [.NET Core SDK 3.0](https://www.microsoft.com/net/learn/get-started) or higher installed. -- When running on linux, please have a system-wide ffmpeg installation available to support video decoding. +- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding. - When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs. -- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/). +- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/). ## Running osu! @@ -68,7 +68,7 @@ dotnet run --project osu.Desktop If you are not interested in debugging osu!, you can add `-c Release` to gain performance. In this case, you must replace `Debug` with `Release` in any commands mentioned in this document. -If the build fails, try to restore nuget packages with `dotnet restore`. +If the build fails, try to restore NuGet packages with `dotnet restore`. ### Testing with resource/framework modifications @@ -76,11 +76,11 @@ Sometimes it may be necessary to cross-test changes in [osu-resources](https://g ### Code analysis -Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install resharper or use rider to get inline support in your IDE of choice. +Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under Windows due to [ReSharper CLI shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice. ## Contributing -We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted. +We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention of having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted. If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) label). @@ -88,7 +88,7 @@ Before starting, please make sure you are familiar with the [development and tes Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as pain-free as possible. -For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via paypal or osu! supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. +For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via PayPal or osu!supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. ## Licence diff --git a/build/build.cake b/build/build.cake index 389ff4829d..274e57ef4e 100644 --- a/build/build.cake +++ b/build/build.cake @@ -11,6 +11,7 @@ var target = Argument("target", "Build"); var configuration = Argument("configuration", "Release"); var rootDirectory = new DirectoryPath(".."); +var sln = rootDirectory.CombineWithFilePath("osu.sln"); var desktopBuilds = rootDirectory.CombineWithFilePath("build/Desktop.proj"); var desktopSlnf = rootDirectory.CombineWithFilePath("osu.Desktop.slnf"); @@ -60,8 +61,12 @@ Task("CodeFileSanity") }); }); +Task("DotnetFormat") + .Does(() => DotNetCoreTool(sln.FullPath, "format", "--dry-run --check")); + Task("Build") .IsDependentOn("CodeFileSanity") + .IsDependentOn("DotnetFormat") .IsDependentOn("InspectCode") .IsDependentOn("Test"); diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj index 708779986c..be6044bbd0 100644 --- a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj @@ -1,4 +1,4 @@ - + Debug diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index 7b8c699f2c..da36673930 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -40,8 +40,10 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.HitObjects.Add(new Fruit { StartTime = 1008, X = 56 / 512f, }); for (int i = 0; i < 512; i++) + { if (i % 5 < 3) beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = 2000 + i * 100, NewCombo = i % 8 == 0 }); + } return beatmap; } diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 5ab47c1611..58bf811fac 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -195,10 +195,15 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { if (currentObject is Fruit) objectWithDroplets.Add(currentObject); + if (currentObject is JuiceStream) + { foreach (var currentJuiceElement in currentObject.NestedHitObjects) + { if (!(currentJuiceElement is TinyDroplet)) objectWithDroplets.Add((CatchHitObject)currentJuiceElement); + } + } } objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index 6d44e4660e..267e6d12c7 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -27,11 +27,13 @@ namespace osu.Game.Rulesets.Catch.Objects return; for (double i = StartTime; i <= EndTime; i += spacing) + { AddNested(new Banana { Samples = Samples, StartTime = i }); + } } public double EndTime => StartTime + Duration; diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj index 52e558931b..88ad484bc1 100644 --- a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj @@ -1,4 +1,4 @@ - + Debug diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index decd159ee9..ada960a78d 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -109,8 +109,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { // Generate a new pattern by copying the last hit objects in reverse-column order for (int i = RandomStart; i < TotalColumns; i++) + { if (PreviousPattern.ColumnHasObject(i)) addToPattern(pattern, RandomStart + TotalColumns - i - 1); + } return pattern; } @@ -132,8 +134,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { // Generate a new pattern by placing on the already filled columns for (int i = RandomStart; i < TotalColumns; i++) + { if (PreviousPattern.ColumnHasObject(i)) addToPattern(pattern, i); + } return pattern; } diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj index cacd035078..545abcec6c 100644 --- a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj @@ -1,4 +1,4 @@ - + Debug diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index 433ec6bd25..ac627aa23e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs @@ -24,12 +24,14 @@ namespace osu.Game.Rulesets.Osu.Tests public TestSceneDrawableJudgement() { foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType().Skip(1)) + { AddStep("Show " + result.GetDescription(), () => SetContents(() => new DrawableOsuJudgement(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); + } } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs index 95c2810e94..b99cd523ff 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLongCombo.cs @@ -29,8 +29,10 @@ namespace osu.Game.Rulesets.Osu.Tests }; for (int i = 0; i < 512; i++) + { if (i % 32 < 20) beatmap.HitObjects.Add(new HitCircle { Position = new Vector2(256, 192), StartTime = i * 100 }); + } return beatmap; } diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index cc08d356f9..3437af8c1e 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -2,8 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using osuTK; @@ -12,11 +16,36 @@ namespace osu.Game.Rulesets.Osu.Edit { public class DrawableOsuEditRuleset : DrawableOsuRuleset { + /// + /// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay. + /// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points. + /// + private const double editor_hit_object_fade_out_extension = 500; + public DrawableOsuEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { } + public override DrawableHitObject CreateDrawableRepresentation(OsuHitObject h) + => base.CreateDrawableRepresentation(h)?.With(d => d.ApplyCustomUpdateState += updateState); + + private void updateState(DrawableHitObject hitObject, ArmedState state) + { + switch (state) + { + case ArmedState.Miss: + // Get the existing fade out transform + var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha)); + if (existing == null) + return; + + using (hitObject.BeginAbsoluteSequence(existing.StartTime)) + hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire(); + break; + } + } + protected override Playfield CreatePlayfield() => new OsuPlayfieldNoCursor(); public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { Size = Vector2.One }; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs index 17fcd03dd5..1664a37a66 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -55,8 +55,10 @@ namespace osu.Game.Rulesets.Osu.Mods } for (int i = 0; i < amountWiggles; i++) + { using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration, true)) wiggle(); + } // Keep wiggling sliders and spinners for their duration if (!(osuObject is IHasEndTime endTime)) @@ -65,8 +67,10 @@ namespace osu.Game.Rulesets.Osu.Mods amountWiggles = (int)(endTime.Duration / wiggle_duration); for (int i = 0; i < amountWiggles; i++) + { using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration, true)) wiggle(); + } } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index f60b7e67b2..3e23d09741 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -133,12 +133,14 @@ namespace osu.Game.Rulesets.Osu.Objects var sampleList = new List(); if (firstSample != null) + { sampleList.Add(new HitSampleInfo { Bank = firstSample.Bank, Volume = firstSample.Volume, Name = @"slidertick", }); + } switch (e.Type) { diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj index c20bd4fe02..8ee640cd99 100644 --- a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj @@ -1,4 +1,4 @@ - + Debug diff --git a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj index 583d188295..ca68369ebb 100644 --- a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj +++ b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj @@ -1,4 +1,4 @@ - + Debug diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 18cbd4e7c5..7df7df22ea 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -225,8 +225,10 @@ namespace osu.Game.Tests.NonVisual private void fastForwardToPoint(double destination) { for (int i = 0; i < 1000; i++) + { if (handler.SetFrameFromTime(destination) == null) return; + } throw new TimeoutException("Seek was never fulfilled"); } diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs index 0d96dd08da..085f502517 100644 --- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs @@ -25,7 +25,9 @@ namespace osu.Game.Tests.Skins var comboColors = decoder.Decode(stream).ComboColours; List expectedColors; + if (hasColours) + { expectedColors = new List { new Color4(142, 199, 255, 255), @@ -33,6 +35,7 @@ namespace osu.Game.Tests.Skins new Color4(128, 255, 255, 255), new Color4(100, 100, 100, 100), }; + } else expectedColors = new DefaultSkin().Configuration.ComboColours; diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index 01400bf1d9..28b5693ef4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -130,12 +130,14 @@ namespace osu.Game.Tests.Visual.Online AddRepeatStep("add many messages", () => { for (int i = 0; i < messages_per_call; i++) + { testChannel.AddNewMessages(new Message(sequence++) { Sender = longUsernameUser, Content = "Many messages! " + Guid.NewGuid(), Timestamp = DateTimeOffset.Now }); + } }, Channel.MAX_HISTORY / messages_per_call + 5); AddAssert("Ensure no adjacent day separators", () => @@ -143,8 +145,10 @@ namespace osu.Game.Tests.Visual.Online var indices = chatDisplay.FillFlow.OfType().Select(ds => chatDisplay.FillFlow.IndexOf(ds)); foreach (var i in indices) + { if (i < chatDisplay.FillFlow.Count && chatDisplay.FillFlow[i + 1] is DrawableChannel.DaySeparator) return false; + } return true; }); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 8b82567a8d..aa63bc1cf6 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -467,8 +467,10 @@ namespace osu.Game.Tests.Visual.SongSelect private void advanceSelection(bool diff, int direction = 1, int count = 1) { if (count == 1) + { AddStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () => carousel.SelectNext(direction, !diff)); + } else { AddRepeatStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () => diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index efe7fee5e4..794d135b06 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -132,11 +132,13 @@ namespace osu.Game.Tests.Visual.SongSelect changeRuleset(1); if (rulesetsInSameBeatmap) + { AddStep("import multi-ruleset map", () => { var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray(); manager.Import(createTestBeatmapSet(0, usableRulesets)).Wait(); }); + } else { addRulesetImportStep(1); diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/TestSceneOsuGame.cs index fcc3a3596f..36cd49d839 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuGame.cs @@ -109,16 +109,20 @@ namespace osu.Game.Tests.Visual AddAssert("check OsuGame DI members", () => { foreach (var type in requiredGameDependencies) + { if (game.Dependencies.Get(type) == null) throw new Exception($"{type} has not been cached"); + } return true; }); AddAssert("check OsuGameBase DI members", () => { foreach (var type in requiredGameBaseDependencies) + { if (gameBase.Dependencies.Get(type) == null) throw new Exception($"{type} has not been cached"); + } return true; }); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs new file mode 100644 index 0000000000..2ada5b927b --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs @@ -0,0 +1,145 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneStatefulMenuItem : ManualInputManagerTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(OsuMenu), + typeof(StatefulMenuItem), + typeof(TernaryStateMenuItem), + typeof(DrawableStatefulMenuItem), + }; + + [Test] + public void TestTernaryMenuItem() + { + OsuMenu menu = null; + + Bindable state = new Bindable(TernaryState.Indeterminate); + + AddStep("create menu", () => + { + state.Value = TernaryState.Indeterminate; + + Child = menu = new OsuMenu(Direction.Vertical, true) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Items = new[] + { + new TernaryStateMenuItem("First"), + new TernaryStateMenuItem("Second") { State = { BindTarget = state } }, + new TernaryStateMenuItem("Third") { State = { Value = TernaryState.True } }, + } + }; + }); + + checkState(TernaryState.Indeterminate); + + click(); + checkState(TernaryState.True); + + click(); + checkState(TernaryState.False); + + click(); + checkState(TernaryState.True); + + click(); + checkState(TernaryState.False); + + AddStep("change state via bindable", () => state.Value = TernaryState.True); + + void click() => + AddStep("click", () => + { + InputManager.MoveMouseTo(menu.ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + }); + + void checkState(TernaryState expected) + => AddAssert($"state is {expected}", () => state.Value == expected); + } + + [Test] + public void TestCustomState() + { + AddStep("create menu", () => + { + Child = new OsuMenu(Direction.Vertical, true) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Items = new[] + { + new TestMenuItem("First", MenuItemType.Standard, getNextState), + new TestMenuItem("Second", MenuItemType.Standard, getNextState) { State = { Value = TestStates.State2 } }, + new TestMenuItem("Third", MenuItemType.Standard, getNextState) { State = { Value = TestStates.State3 } }, + } + }; + }); + } + + private TestStates getNextState(TestStates state) + { + switch (state) + { + case TestStates.State1: + return TestStates.State2; + + case TestStates.State2: + return TestStates.State3; + + case TestStates.State3: + return TestStates.State1; + } + + return TestStates.State1; + } + + private class TestMenuItem : StatefulMenuItem + { + public TestMenuItem(string text, MenuItemType type, Func changeStateFunc) + : base(text, changeStateFunc, type) + { + } + + public override IconUsage? GetIconForState(TestStates state) + { + switch (state) + { + case TestStates.State1: + return FontAwesome.Solid.DiceOne; + + case TestStates.State2: + return FontAwesome.Solid.DiceTwo; + + case TestStates.State3: + return FontAwesome.Solid.DiceThree; + + default: + throw new ArgumentOutOfRangeException(nameof(state), state, null); + } + } + } + + private enum TestStates + { + State1, + State2, + State3 + } + } +} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToggleMenuItem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToggleMenuItem.cs new file mode 100644 index 0000000000..2abda56a28 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToggleMenuItem.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneToggleMenuItem : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(OsuMenu), + typeof(ToggleMenuItem), + typeof(DrawableStatefulMenuItem) + }; + + public TestSceneToggleMenuItem() + { + Add(new OsuMenu(Direction.Vertical, true) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Items = new[] + { + new ToggleMenuItem("First"), + new ToggleMenuItem("Second") { State = { Value = true } } + } + }); + } + } +} diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentMatchChatDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentMatchChatDisplay.cs index 41d32d9448..9905e17824 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentMatchChatDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentMatchChatDisplay.cs @@ -102,7 +102,8 @@ namespace osu.Game.Tournament.Tests.Components Content = "Okay okay, calm down guys. Let's do this!" })); - AddStep("multiple messages", () => testChannel.AddNewMessages(new Message(nextMessageId()) + AddStep("multiple messages", () => testChannel.AddNewMessages( + new Message(nextMessageId()) { Sender = admin, Content = "I spam you!" diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index f6c1be0e36..0908814537 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -131,6 +131,7 @@ namespace osu.Game.Tournament.Components }); if (!string.IsNullOrEmpty(mods)) + { AddInternal(new Sprite { Texture = textures.Get($"mods/{mods}"), @@ -139,6 +140,7 @@ namespace osu.Game.Tournament.Components Margin = new MarginPadding(20), Scale = new Vector2(0.5f) }); + } } private void matchChanged(ValueChangedEvent match) diff --git a/osu.Game.Tournament/Components/TourneyVideo.cs b/osu.Game.Tournament/Components/TourneyVideo.cs index 8582b1634c..206689ca1a 100644 --- a/osu.Game.Tournament/Components/TourneyVideo.cs +++ b/osu.Game.Tournament/Components/TourneyVideo.cs @@ -29,12 +29,14 @@ namespace osu.Game.Tournament.Components }; } else + { InternalChild = video = new VideoSprite(stream) { RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, Clock = new FramedClock(manualClock = new ManualClock()) }; + } } public bool Loop diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index e05d96e098..47f2bed77a 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -60,6 +60,7 @@ namespace osu.Game.Tournament.IPC const string file_ipc_channel_filename = "ipc-channel.txt"; if (Storage.Exists(file_ipc_filename)) + { scheduled = Scheduler.AddDelayed(delegate { try @@ -134,6 +135,7 @@ namespace osu.Game.Tournament.IPC // file might be in use. } }, 250, true); + } } catch (Exception e) { diff --git a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs index b036350879..2c515edda7 100644 --- a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs @@ -266,12 +266,14 @@ namespace osu.Game.Tournament.Screens.Editors drawableContainer.Clear(); if (Model.BeatmapInfo != null) + { drawableContainer.Child = new TournamentBeatmapPanel(Model.BeatmapInfo, Model.Mods) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Width = 300 }; + } } } } diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs index 83a41a662f..66e68a0f37 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs @@ -81,8 +81,10 @@ namespace osu.Game.Tournament.Screens.Ladder LadderInfo.Matches.ItemsRemoved += matches => { foreach (var p in matches) - foreach (var d in MatchesContainer.Where(d => d.Match == p)) - d.Expire(); + { + foreach (var d in MatchesContainer.Where(d => d.Match == p)) + d.Expire(); + } layout.Invalidate(); }; diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index c901a5c7ef..47c923ff30 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -164,6 +164,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro if (team != null) { foreach (var p in team.Players) + { players.Add(new OsuSpriteText { Text = p.Username, @@ -172,6 +173,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro Anchor = left ? Anchor.CentreRight : Anchor.CentreLeft, Origin = left ? Anchor.CentreRight : Anchor.CentreLeft, }); + } } } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 3b7718c61d..d34ea9648d 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -164,15 +164,17 @@ namespace osu.Game.Tournament // link matches to rounds foreach (var round in ladder.Rounds) - foreach (var id in round.Matches) { - var found = ladder.Matches.FirstOrDefault(p => p.ID == id); - - if (found != null) + foreach (var id in round.Matches) { - found.Round.Value = round; - if (round.StartDate.Value > found.Date.Value) - found.Date.Value = round.StartDate.Value; + var found = ladder.Matches.FirstOrDefault(p => p.ID == id); + + if (found != null) + { + found.Round.Value = round; + if (round.StartDate.Value > found.Date.Value) + found.Date.Value = round.StartDate.Value; + } } } @@ -192,15 +194,19 @@ namespace osu.Game.Tournament bool addedInfo = false; foreach (var t in ladder.Teams) - foreach (var p in t.Players) - if (string.IsNullOrEmpty(p.Username)) + { + foreach (var p in t.Players) { - var req = new GetUserRequest(p.Id); - req.Perform(API); - p.Username = req.Result.Username; + if (string.IsNullOrEmpty(p.Username)) + { + var req = new GetUserRequest(p.Id); + req.Perform(API); + p.Username = req.Result.Username; - addedInfo = true; + addedInfo = true; + } } + } return addedInfo; } @@ -213,15 +219,19 @@ namespace osu.Game.Tournament bool addedInfo = false; foreach (var r in ladder.Rounds) - foreach (var b in r.Beatmaps) - if (b.BeatmapInfo == null && b.ID > 0) + { + foreach (var b in r.Beatmaps) { - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); - req.Perform(API); - b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore); + if (b.BeatmapInfo == null && b.ID > 0) + { + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); + req.Perform(API); + b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore); - addedInfo = true; + addedInfo = true; + } } + } return addedInfo; } diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 5dbd67d304..e3320f62ac 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -144,16 +144,16 @@ namespace osu.Game.Beatmaps.Formats var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue; var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0; timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); - } break; + } case "L": { var startTime = double.Parse(split[1], CultureInfo.InvariantCulture); var loopCount = int.Parse(split[2]); timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); - } break; + } default: { @@ -171,16 +171,16 @@ namespace osu.Game.Beatmaps.Formats var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); - } break; + } case "S": { var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); - } break; + } case "V": { @@ -189,16 +189,16 @@ namespace osu.Game.Beatmaps.Formats var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); - } break; + } case "R": { var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue)); - } break; + } case "M": { @@ -208,24 +208,24 @@ namespace osu.Game.Beatmaps.Formats var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); - } break; + } case "MX": { var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); - } break; + } case "MY": { var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); - } break; + } case "C": { @@ -238,8 +238,8 @@ namespace osu.Game.Beatmaps.Formats timelineGroup?.Colour.Add(easing, startTime, endTime, new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); - } break; + } case "P": { @@ -259,14 +259,16 @@ namespace osu.Game.Beatmaps.Formats timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); break; } - } + break; + } default: throw new InvalidDataException($@"Unknown command type: {commandType}"); } - } + break; + } } } } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 3fc33e9f52..7c69a992dd 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -133,8 +133,10 @@ namespace osu.Game.Beatmaps obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty); foreach (var mod in mods.OfType()) - foreach (var obj in converted.HitObjects) - mod.ApplyToHitObject(obj); + { + foreach (var obj in converted.HitObjects) + mod.ApplyToHitObject(obj); + } processor?.PostProcess(); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 9fed8e03ac..8fa4eaf267 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -264,9 +264,12 @@ namespace osu.Game.Database { // for now, concatenate all .osu files in the set to create a unique hash. MemoryStream hashable = new MemoryStream(); + foreach (string file in reader.Filenames.Where(f => HashableFileTypes.Any(f.EndsWith))) + { using (Stream s = reader.GetStream(file)) s.CopyTo(hashable); + } return hashable.Length > 0 ? hashable.ComputeSHA2Hash() : null; } @@ -485,12 +488,16 @@ namespace osu.Game.Database // import files to manager foreach (string file in reader.Filenames) + { using (Stream s = reader.GetStream(file)) + { fileInfos.Add(new TFileModel { Filename = FileSafety.PathStandardise(file.Substring(prefix.Length)), FileInfo = files.Add(s) }); + } + } return fileInfos; } @@ -651,8 +658,10 @@ namespace osu.Game.Database private void handleEvent(Action a) { if (delayingEvents) + { lock (queuedEvents) queuedEvents.Add(a); + } else a.Invoke(); } diff --git a/osu.Game/Graphics/Containers/ShakeContainer.cs b/osu.Game/Graphics/Containers/ShakeContainer.cs index e5a6bcc28e..dca9df1e98 100644 --- a/osu.Game/Graphics/Containers/ShakeContainer.cs +++ b/osu.Game/Graphics/Containers/ShakeContainer.cs @@ -43,9 +43,11 @@ namespace osu.Game.Graphics.Containers // if we don't have enough time for the second shake, skip it. if (!maximumLength.HasValue || maximumLength >= ShakeDuration * 4) + { sequence = sequence .MoveToX(shake_amount, ShakeDuration, Easing.InOutSine).Then() .MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then(); + } sequence.MoveToX(0, ShakeDuration / 2, Easing.InSine); } diff --git a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs new file mode 100644 index 0000000000..591ed3df83 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs @@ -0,0 +1,133 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Sprites; +using osuTK.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + public class DrawableOsuMenuItem : Menu.DrawableMenuItem + { + public const int MARGIN_HORIZONTAL = 17; + public const int MARGIN_VERTICAL = 4; + private const int text_size = 17; + private const int transition_length = 80; + + private SampleChannel sampleClick; + private SampleChannel sampleHover; + + private TextContainer text; + + public DrawableOsuMenuItem(MenuItem item) + : base(item) + { + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleHover = audio.Samples.Get(@"UI/generic-hover"); + sampleClick = audio.Samples.Get(@"UI/generic-select"); + + BackgroundColour = Color4.Transparent; + BackgroundColourHover = OsuColour.FromHex(@"172023"); + + updateTextColour(); + } + + private void updateTextColour() + { + switch ((Item as OsuMenuItem)?.Type) + { + default: + case MenuItemType.Standard: + text.Colour = Color4.White; + break; + + case MenuItemType.Destructive: + text.Colour = Color4.Red; + break; + + case MenuItemType.Highlighted: + text.Colour = OsuColour.FromHex(@"ffcc22"); + break; + } + } + + protected override bool OnHover(HoverEvent e) + { + sampleHover.Play(); + text.BoldText.FadeIn(transition_length, Easing.OutQuint); + text.NormalText.FadeOut(transition_length, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + text.BoldText.FadeOut(transition_length, Easing.OutQuint); + text.NormalText.FadeIn(transition_length, Easing.OutQuint); + base.OnHoverLost(e); + } + + protected override bool OnClick(ClickEvent e) + { + sampleClick.Play(); + return base.OnClick(e); + } + + protected sealed override Drawable CreateContent() => text = CreateTextContainer(); + protected virtual TextContainer CreateTextContainer() => new TextContainer(); + + protected class TextContainer : Container, IHasText + { + public string Text + { + get => NormalText.Text; + set + { + NormalText.Text = value; + BoldText.Text = value; + } + } + + public readonly SpriteText NormalText; + public readonly SpriteText BoldText; + + public TextContainer() + { + Anchor = Anchor.CentreLeft; + Origin = Anchor.CentreLeft; + + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + NormalText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(size: text_size), + Margin = new MarginPadding { Horizontal = MARGIN_HORIZONTAL, Vertical = MARGIN_VERTICAL }, + }, + BoldText = new OsuSpriteText + { + AlwaysPresent = true, + Alpha = 0, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Margin = new MarginPadding { Horizontal = MARGIN_HORIZONTAL, Vertical = MARGIN_VERTICAL }, + } + }; + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs new file mode 100644 index 0000000000..3dc99f2dbe --- /dev/null +++ b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Graphics.UserInterface +{ + public class DrawableStatefulMenuItem : DrawableOsuMenuItem + { + protected new StatefulMenuItem Item => (StatefulMenuItem)base.Item; + + public DrawableStatefulMenuItem(StatefulMenuItem item) + : base(item) + { + } + + protected override TextContainer CreateTextContainer() => new ToggleTextContainer(Item); + + private class ToggleTextContainer : TextContainer + { + private readonly StatefulMenuItem menuItem; + private readonly Bindable state; + private readonly SpriteIcon stateIcon; + + public ToggleTextContainer(StatefulMenuItem menuItem) + { + this.menuItem = menuItem; + + state = menuItem.State.GetBoundCopy(); + + Add(stateIcon = new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(10), + Margin = new MarginPadding { Horizontal = MARGIN_HORIZONTAL }, + AlwaysPresent = true, + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + state.BindValueChanged(updateState, true); + } + + protected override void Update() + { + base.Update(); + + // Todo: This is bad. This can maybe be done better with a refactor of DrawableOsuMenuItem. + stateIcon.X = BoldText.DrawWidth + 10; + } + + private void updateState(ValueChangedEvent state) + { + var icon = menuItem.GetIconForState(state.NewValue); + + if (icon == null) + stateIcon.Alpha = 0; + else + { + stateIcon.Alpha = 1; + stateIcon.Icon = icon.Value; + } + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs index cea8427296..4b629080e1 100644 --- a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterface { @@ -35,5 +36,7 @@ namespace osu.Game.Graphics.UserInterface protected override void AnimateOpen() => this.FadeIn(fade_duration, Easing.OutQuint); protected override void AnimateClose() => this.FadeOut(fade_duration, Easing.OutQuint); + + protected override Menu CreateSubMenu() => new OsuContextMenu(); } } diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index c4c6950eb1..e7bf4f66ee 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -1,18 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osuTK; namespace osu.Game.Graphics.UserInterface @@ -45,7 +39,16 @@ namespace osu.Game.Graphics.UserInterface } } - protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuMenuItem(item); + protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) + { + switch (item) + { + case StatefulMenuItem stateful: + return new DrawableStatefulMenuItem(stateful); + } + + return new DrawableOsuMenuItem(item); + } protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); @@ -53,122 +56,5 @@ namespace osu.Game.Graphics.UserInterface { Anchor = Direction == Direction.Horizontal ? Anchor.BottomLeft : Anchor.TopRight }; - - protected class DrawableOsuMenuItem : DrawableMenuItem - { - private const int margin_horizontal = 17; - private const int text_size = 17; - private const int transition_length = 80; - public const int MARGIN_VERTICAL = 4; - - private SampleChannel sampleClick; - private SampleChannel sampleHover; - - private TextContainer text; - - public DrawableOsuMenuItem(MenuItem item) - : base(item) - { - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleHover = audio.Samples.Get(@"UI/generic-hover"); - sampleClick = audio.Samples.Get(@"UI/generic-select"); - - BackgroundColour = Color4.Transparent; - BackgroundColourHover = OsuColour.FromHex(@"172023"); - - updateTextColour(); - } - - private void updateTextColour() - { - switch ((Item as OsuMenuItem)?.Type) - { - default: - case MenuItemType.Standard: - text.Colour = Color4.White; - break; - - case MenuItemType.Destructive: - text.Colour = Color4.Red; - break; - - case MenuItemType.Highlighted: - text.Colour = OsuColour.FromHex(@"ffcc22"); - break; - } - } - - protected override bool OnHover(HoverEvent e) - { - sampleHover.Play(); - text.BoldText.FadeIn(transition_length, Easing.OutQuint); - text.NormalText.FadeOut(transition_length, Easing.OutQuint); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - text.BoldText.FadeOut(transition_length, Easing.OutQuint); - text.NormalText.FadeIn(transition_length, Easing.OutQuint); - base.OnHoverLost(e); - } - - protected override bool OnClick(ClickEvent e) - { - sampleClick.Play(); - return base.OnClick(e); - } - - protected sealed override Drawable CreateContent() => text = CreateTextContainer(); - protected virtual TextContainer CreateTextContainer() => new TextContainer(); - - protected class TextContainer : Container, IHasText - { - public string Text - { - get => NormalText.Text; - set - { - NormalText.Text = value; - BoldText.Text = value; - } - } - - public readonly SpriteText NormalText; - public readonly SpriteText BoldText; - - public TextContainer() - { - Anchor = Anchor.CentreLeft; - Origin = Anchor.CentreLeft; - - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - NormalText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(size: text_size), - Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL }, - }, - BoldText = new OsuSpriteText - { - AlwaysPresent = true, - Alpha = 0, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), - Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL }, - } - }; - } - } - } } } diff --git a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs index b7aa666302..0fe41937ce 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs @@ -11,9 +11,8 @@ namespace osu.Game.Graphics.UserInterface public readonly MenuItemType Type; public OsuMenuItem(string text, MenuItemType type = MenuItemType.Standard) - : base(text) + : this(text, type, null) { - Type = type; } public OsuMenuItem(string text, MenuItemType type, Action action) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index c55d14456b..5d1bdc62e9 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -51,8 +51,10 @@ namespace osu.Game.Graphics.UserInterface }); if (isEnumType && AddEnumEntriesAutomatically) + { foreach (var val in (T[])Enum.GetValues(typeof(T))) AddItem(val); + } } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index 63062cdc9d..e291401670 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -43,9 +43,12 @@ namespace osu.Game.Graphics.UserInterface protected override string FormatCount(double count) { string format = new string('0', (int)LeadingZeroes); + if (UseCommaSeparator) + { for (int i = format.Length - 3; i > 0; i -= 3) format = format.Insert(i, @","); + } return ((long)count).ToString(format); } diff --git a/osu.Game/Graphics/UserInterface/StatefulMenuItem.cs b/osu.Game/Graphics/UserInterface/StatefulMenuItem.cs new file mode 100644 index 0000000000..0d7b36e51b --- /dev/null +++ b/osu.Game/Graphics/UserInterface/StatefulMenuItem.cs @@ -0,0 +1,105 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// An which contains and displays a state. + /// + public abstract class StatefulMenuItem : OsuMenuItem + { + /// + /// The current state that should be displayed. + /// + public readonly Bindable State = new Bindable(); + + /// + /// Creates a new . + /// + /// The text to display. + /// A function that mutates a state to another state after this is pressed. + /// The type of action which this performs. + protected StatefulMenuItem(string text, Func changeStateFunc, MenuItemType type = MenuItemType.Standard) + : this(text, changeStateFunc, type, null) + { + } + + /// + /// Creates a new . + /// + /// The text to display. + /// A function that mutates a state to another state after this is pressed. + /// The type of action which this performs. + /// A delegate to be invoked when this is pressed. + protected StatefulMenuItem(string text, Func changeStateFunc, MenuItemType type, Action action) + : base(text, type) + { + Action.Value = () => + { + State.Value = changeStateFunc?.Invoke(State.Value) ?? State.Value; + action?.Invoke(State.Value); + }; + } + + /// + /// Retrieves the icon to be displayed for a state. + /// + /// The state to retrieve the relevant icon for. + /// The icon to be displayed for . + public abstract IconUsage? GetIconForState(object state); + } + + public abstract class StatefulMenuItem : StatefulMenuItem + where T : struct + { + /// + /// The current state that should be displayed. + /// + public new readonly Bindable State = new Bindable(); + + /// + /// Creates a new . + /// + /// The text to display. + /// A function that mutates a state to another state after this is pressed. + /// The type of action which this performs. + protected StatefulMenuItem(string text, Func changeStateFunc, MenuItemType type = MenuItemType.Standard) + : this(text, changeStateFunc, type, null) + { + } + + /// + /// Creates a new . + /// + /// The text to display. + /// A function that mutates a state to another state after this is pressed. + /// The type of action which this performs. + /// A delegate to be invoked when this is pressed. + protected StatefulMenuItem(string text, Func changeStateFunc, MenuItemType type, Action action) + : base(text, o => changeStateFunc?.Invoke((T)o) ?? o, type, o => action?.Invoke((T)o)) + { + base.State.BindValueChanged(state => + { + if (state.NewValue == null) + base.State.Value = default(T); + + State.Value = (T)base.State.Value; + }, true); + + State.BindValueChanged(state => base.State.Value = state.NewValue); + } + + public sealed override IconUsage? GetIconForState(object state) => GetIconForState((T)state); + + /// + /// Retrieves the icon to be displayed for a state. + /// + /// The state to retrieve the relevant icon for. + /// The icon to be displayed for . + public abstract IconUsage? GetIconForState(T state); + } +} diff --git a/osu.Game/Graphics/UserInterface/TernaryState.cs b/osu.Game/Graphics/UserInterface/TernaryState.cs new file mode 100644 index 0000000000..d4de28044f --- /dev/null +++ b/osu.Game/Graphics/UserInterface/TernaryState.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// An on/off state with an extra indeterminate state. + /// + public enum TernaryState + { + /// + /// The current state is false. + /// + False, + + /// + /// The current state is a combination of and . + /// The state becomes if the is pressed. + /// + Indeterminate, + + /// + /// The current state is true. + /// + True + } +} diff --git a/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs b/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs new file mode 100644 index 0000000000..2d9e2106d4 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/TernaryStateMenuItem.cs @@ -0,0 +1,79 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// An with three possible states. + /// + public class TernaryStateMenuItem : StatefulMenuItem + { + /// + /// Creates a new . + /// + /// The text to display. + /// The type of action which this performs. + public TernaryStateMenuItem(string text, MenuItemType type = MenuItemType.Standard) + : this(text, type, null) + { + } + + /// + /// Creates a new . + /// + /// The text to display. + /// The type of action which this performs. + /// A delegate to be invoked when this is pressed. + public TernaryStateMenuItem(string text, MenuItemType type, Action action) + : this(text, getNextState, type, action) + { + } + + /// + /// Creates a new . + /// + /// The text to display. + /// A function that mutates a state to another state after this is pressed. + /// The type of action which this performs. + /// A delegate to be invoked when this is pressed. + protected TernaryStateMenuItem(string text, Func changeStateFunc, MenuItemType type, Action action) + : base(text, changeStateFunc, type, action) + { + } + + public override IconUsage? GetIconForState(TernaryState state) + { + switch (state) + { + case TernaryState.Indeterminate: + return FontAwesome.Solid.DotCircle; + + case TernaryState.True: + return FontAwesome.Solid.Check; + } + + return null; + } + + private static TernaryState getNextState(TernaryState state) + { + switch (state) + { + case TernaryState.False: + return TernaryState.True; + + case TernaryState.Indeterminate: + return TernaryState.True; + + case TernaryState.True: + return TernaryState.False; + + default: + throw new ArgumentOutOfRangeException(nameof(state), state, null); + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ToggleMenuItem.cs b/osu.Game/Graphics/UserInterface/ToggleMenuItem.cs new file mode 100644 index 0000000000..f9ff9859dd --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ToggleMenuItem.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// An which displays an enabled or disabled state. + /// + public class ToggleMenuItem : StatefulMenuItem + { + /// + /// Creates a new . + /// + /// The text to display. + /// The type of action which this performs. + public ToggleMenuItem(string text, MenuItemType type = MenuItemType.Standard) + : this(text, type, null) + { + } + + /// + /// Creates a new . + /// + /// The text to display. + /// The type of action which this performs. + /// A delegate to be invoked when this is pressed. + public ToggleMenuItem(string text, MenuItemType type, Action action) + : base(text, value => !value, type, action) + { + } + + public override IconUsage? GetIconForState(bool state) => state ? (IconUsage?)FontAwesome.Solid.Check : null; + } +} diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index caddb1ae0d..74b3134964 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -46,6 +46,7 @@ namespace osu.Game.Input continue; foreach (var insertable in group.Skip(count).Take(aimCount - count)) + { // insert any defaults which are missing. usage.Context.DatabasedKeyBinding.Add(new DatabasedKeyBinding { @@ -54,6 +55,7 @@ namespace osu.Game.Input RulesetID = rulesetId, Variant = variant }); + } } } } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 4f6066cab1..1d8c5609d9 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -220,7 +220,7 @@ namespace osu.Game.Online.Chat break; } - var channel = availableChannels.Where(c => c.Name == content || c.Name == $"#{content}").FirstOrDefault(); + var channel = availableChannels.FirstOrDefault(c => c.Name == content || c.Name == $"#{content}"); if (channel == null) { diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 83de0635fb..d214743b30 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -75,8 +75,10 @@ namespace osu.Game.Online.Leaderboards int i = 0; foreach (var s in scrollFlow.Children) + { using (s.BeginDelayedSequence(i++ * 50, true)) s.Show(); + } scrollContainer.ScrollTo(0f, false); }, (showScoresCancellationSource = new CancellationTokenSource()).Token)); @@ -342,13 +344,17 @@ namespace osu.Game.Online.Leaderboards else { if (bottomY - fadeBottom > 0 && FadeBottom) + { c.Colour = ColourInfo.GradientVertical( Color4.White.Opacity(Math.Min(1 - (topY - fadeBottom) / LeaderboardScore.HEIGHT, 1)), Color4.White.Opacity(Math.Min(1 - (bottomY - fadeBottom) / LeaderboardScore.HEIGHT, 1))); + } else if (FadeTop) + { c.Colour = ColourInfo.GradientVertical( Color4.White.Opacity(Math.Min(1 - (fadeTop - topY) / LeaderboardScore.HEIGHT, 1)), Color4.White.Opacity(Math.Min(1 - (fadeTop - bottomY) / LeaderboardScore.HEIGHT, 1))); + } } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1f823e6eba..7735030bbb 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -402,8 +402,10 @@ namespace osu.Game nextBeatmap.Track.Completed += currentTrackCompleted; using (var oldBeatmap = beatmap.OldValue) + { if (oldBeatmap?.Track != null) oldBeatmap.Track.Completed -= currentTrackCompleted; + } nextBeatmap?.LoadBeatmapAsync(); } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 194a439b06..87321030a9 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -292,8 +292,10 @@ namespace osu.Game var extension = Path.GetExtension(paths.First())?.ToLowerInvariant(); foreach (var importer in fileImporters) + { if (importer.HandledExtensions.Contains(extension)) await importer.Import(paths); + } } public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray(); diff --git a/osu.Game/Overlays/Changelog/ChangelogBuild.cs b/osu.Game/Overlays/Changelog/ChangelogBuild.cs index d8488b21ab..5a3ce6291e 100644 --- a/osu.Game/Overlays/Changelog/ChangelogBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogBuild.cs @@ -130,6 +130,7 @@ namespace osu.Game.Overlays.Changelog }); if (entry.GithubUser.UserId != null) + { title.AddUserLink(new User { Username = entry.GithubUser.OsuUsername, @@ -139,18 +140,23 @@ namespace osu.Game.Overlays.Changelog t.Font = fontMedium; t.Colour = entryColour; }); + } else if (entry.GithubUser.GithubUrl != null) + { title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, t => { t.Font = fontMedium; t.Colour = entryColour; }); + } else + { title.AddText(entry.GithubUser.DisplayName, t => { t.Font = fontSmall; t.Colour = entryColour; }); + } ChangelogEntries.Add(titleContainer); diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index adcd33fb48..3297b00322 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -68,11 +68,13 @@ namespace osu.Game.Overlays.Changelog } if (build != null) + { Children = new Drawable[] { new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild }, new Comments(build) }; + } } public class ChangelogBuildWithNavigation : ChangelogBuild diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index abc1b7233d..560123eb55 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -162,10 +162,12 @@ namespace osu.Game.Overlays.Comments foreach (var c in response.Comments) { if (c.IsTopLevel) + { page.Add(new DrawableComment(c) { ShowDeleted = { BindTarget = ShowDeleted } }); + } } LoadComponentAsync(page, loaded => diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 3ffc3f332b..c1c5113c5e 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -149,8 +149,10 @@ namespace osu.Game.Overlays.Direct icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is DirectListPanel ? Color4.White : colours.Gray5)); } else + { foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty)) icons.Add(new DifficultyIcon(b)); + } return icons; } diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 58892cd0dd..8252020e9b 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -194,8 +194,10 @@ namespace osu.Game.Overlays.Mods start = Mods.Length - 1; for (int i = start; i < Mods.Length && i >= 0; i += direction) + { if (SelectAt(i)) return; + } Deselect(); } diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index dedd397fa5..3b16189e73 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -112,6 +112,7 @@ namespace osu.Game.Overlays.Mods if (selected == null) continue; foreach (var type in modTypes) + { if (type.IsInstanceOfType(selected)) { if (immediate) @@ -119,6 +120,7 @@ namespace osu.Game.Overlays.Mods else Scheduler.AddDelayed(button.Deselect, delay += 50); } + } } } @@ -158,7 +160,8 @@ namespace osu.Game.Overlays.Mods }, ButtonsContainer = new FillFlowContainer { - AutoSizeAxes = Axes.Both, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, Spacing = new Vector2(50f, 0f), diff --git a/osu.Game/Rulesets/Difficulty/Utils/LimitedCapacityStack.cs b/osu.Game/Rulesets/Difficulty/Utils/LimitedCapacityStack.cs index 95b7d9b19d..d47caf409b 100644 --- a/osu.Game/Rulesets/Difficulty/Utils/LimitedCapacityStack.cs +++ b/osu.Game/Rulesets/Difficulty/Utils/LimitedCapacityStack.cs @@ -81,8 +81,10 @@ namespace osu.Game.Rulesets.Difficulty.Utils yield return array[i]; if (Count == capacity) + { for (int i = 0; i < marker; ++i) yield return array[i]; + } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1d9b66f88d..af1a2fb63a 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -113,8 +113,10 @@ namespace osu.Game.Rulesets.Objects.Drawables if (samples.Length > 0) { if (HitObject.SampleControlPoint == null) + { throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}." + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); + } samples = samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).ToArray(); foreach (var s in samples) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index e990938291..4049e40013 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -74,9 +74,12 @@ namespace osu.Game.Rulesets.Objects.Legacy string[] pointSplit = split[5].Split('|'); int pointCount = 1; + foreach (var t in pointSplit) + { if (t.Length > 1) pointCount++; + } var points = new Vector2[pointCount]; diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index bc9571c85d..7763b0eaaf 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -185,8 +185,10 @@ namespace osu.Game.Rulesets.Objects ReadOnlySpan cpSpan = ControlPoints.Slice(start, end - start); foreach (Vector2 t in calculateSubpath(cpSpan)) + { if (calculatedPath.Count == 0 || calculatedPath.Last() != t) calculatedPath.Add(t); + } start = end; } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 23988ff0ff..7d13afe9e5 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -69,8 +69,10 @@ namespace osu.Game.Rulesets //add any other modes foreach (var r in instances.Where(r => r.LegacyID == null)) + { if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null) context.RulesetInfo.Add(r.RulesetInfo); + } context.SaveChanges(); diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index e005eea831..d1749d33c0 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -436,8 +436,10 @@ namespace osu.Game.Rulesets.UI return h.HitWindows; foreach (var n in h.NestedHitObjects) + { if (h.HitWindows.WindowFor(HitResult.Miss) > 0) return n.HitWindows; + } } return null; diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index f2e7f51b52..ca4983e3d7 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -130,9 +130,13 @@ namespace osu.Game.Rulesets.UI base.Update(); if (beatmap != null) + { foreach (var mod in mods) + { if (mod is IUpdatableByPlayfield updatable) updatable.Update(this); + } + } } /// diff --git a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs index f79cac7649..b41b2d073e 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs @@ -57,11 +57,13 @@ namespace osu.Game.Screens.Multi.Components var beatmap = CurrentItem.Value?.Beatmap; if (beatmap == null) + { textFlow.AddText("No beatmap selected", s => { s.Font = s.Font.With(size: TextSize); s.Colour = colours.PinkLight; }); + } else { textFlow.AddLink(new[] diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index a05937801c..968b83e68c 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -100,9 +100,11 @@ namespace osu.Game.Screens.Play.HUD if (text.Alpha > 0 || button.Progress.Value > 0 || button.IsHovered) Alpha = 1; else + { Alpha = Interpolation.ValueAt( MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 200), Alpha, MathHelper.Clamp(1 - positionalAdjust, 0.04f, 1), 0, 200, Easing.OutQuint); + } } private class Button : HoldToConfirmContainer, IKeyBindingHandler diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 31cdff5fb9..835867fe62 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -181,8 +181,11 @@ namespace osu.Game.Screens.Play this.FadeIn(500, Easing.OutExpo); if (!IsHovered && !IsDragged) + { using (BeginDelayedSequence(1000)) scheduledHide = Schedule(Hide); + } + break; case Visibility.Hidden: diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 6c3c9d20f3..afd6211dec 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -44,10 +44,14 @@ namespace osu.Game.Screens.Select.Carousel criteria.Artist.Matches(Beatmap.Metadata.ArtistUnicode); if (match) + { foreach (var criteriaTerm in criteria.SearchTerms) + { match &= Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0) || Beatmap.Version.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0; + } + } Filtered.Value = !match; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 409ea4bbbe..375b994169 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -251,11 +251,13 @@ namespace osu.Game.Screens.Select { // if we have no beatmaps but osu-stable is found, let's prompt the user to import. if (!beatmaps.GetAllUsableBeatmapSetsEnumerable().Any() && beatmaps.StableInstallationAvailable) + { dialogOverlay.Push(new ImportFromStablePopup(() => { Task.Run(beatmaps.ImportFromStableAsync).ContinueWith(_ => scores.ImportFromStableAsync(), TaskContinuationOptions.OnlyOnRanToCompletion); Task.Run(skins.ImportFromStableAsync); })); + } }); } } @@ -332,12 +334,14 @@ namespace osu.Game.Screens.Select if (e.NewValue is DummyWorkingBeatmap) return; if (this.IsCurrentScreen() && !Carousel.SelectBeatmap(e.NewValue?.BeatmapInfo, false)) + { // If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch if (e.NewValue?.BeatmapInfo?.Ruleset != null && !e.NewValue.BeatmapInfo.Ruleset.Equals(decoupledRuleset.Value)) { Ruleset.Value = e.NewValue.BeatmapInfo.Ruleset; Carousel.SelectBeatmap(e.NewValue.BeatmapInfo); } + } } // We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds. diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index fea15458e4..67a83f19e2 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -35,9 +35,12 @@ namespace osu.Game.Skinning : base(skin) { Stream stream = storage?.GetStream(filename); + if (stream != null) + { using (LineBufferedReader reader = new LineBufferedReader(stream)) Configuration = new LegacySkinDecoder().Decode(reader); + } else Configuration = new DefaultSkinConfiguration(); diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index c5582af836..c758b699ed 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -22,17 +22,19 @@ namespace osu.Game.Skinning if (animatable) { - for (int i = 0;; i++) + for (int i = 0; true; i++) { if ((texture = getFrameTexture(i)) == null) break; if (animation == null) + { animation = new TextureAnimation { DefaultFrameLength = default_frame_time, Repeat = looping }; + } animation.AddFrame(texture); } @@ -42,10 +44,12 @@ namespace osu.Game.Skinning return animation; if ((texture = source.GetTexture(componentName)) != null) + { return new Sprite { Texture = texture }; + } return null; } diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index bdf8be773b..6d23f22515 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -81,9 +81,13 @@ namespace osu.Game.Skinning var ch = skin.GetSample(s); if (ch == null && allowFallback) + { foreach (var lookup in s.LookupNames) + { if ((ch = samples.Get($"Gameplay/{lookup}")) != null) break; + } + } if (ch != null) { @@ -91,8 +95,10 @@ namespace osu.Game.Skinning ch.Volume.Value = s.Volume / 100.0; if (adjustments != null) + { foreach (var adjustment in adjustments) ch.AddAdjustment(adjustment.property, adjustment.bindable); + } } return ch; @@ -104,8 +110,10 @@ namespace osu.Game.Skinning base.Dispose(isDisposing); if (channels != null) + { foreach (var c in channels) c.Dispose(); + } } } } diff --git a/osu.Game/Storyboards/CommandTimelineGroup.cs b/osu.Game/Storyboards/CommandTimelineGroup.cs index 461ee762e9..364c971874 100644 --- a/osu.Game/Storyboards/CommandTimelineGroup.cs +++ b/osu.Game/Storyboards/CommandTimelineGroup.cs @@ -65,6 +65,7 @@ namespace osu.Game.Storyboards public virtual IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0) { if (offset != 0) + { return timelineSelector(this).Commands.Select(command => new CommandTimeline.TypedCommand { @@ -74,6 +75,7 @@ namespace osu.Game.Storyboards StartValue = command.StartValue, EndValue = command.EndValue, }); + } return timelineSelector(this).Commands; } diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 37c3ff495f..d5e69fd103 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -105,9 +105,13 @@ namespace osu.Game.Storyboards var commands = TimelineGroup.GetCommands(timelineSelector); foreach (var loop in loops) commands = commands.Concat(loop.GetCommands(timelineSelector)); + if (triggeredGroups != null) + { foreach (var pair in triggeredGroups) commands = commands.Concat(pair.Item1.GetCommands(timelineSelector, pair.Item2)); + } + return commands; } diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index e99b5fc5fb..b144de35c5 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -72,11 +72,15 @@ namespace osu.Game.Tests.Beatmaps break; if (objectCounter >= ourMapping.Objects.Count) + { Assert.Fail($"The conversion did not generate a hitobject, but should have, for hitobject at time: {expectedMapping.StartTime}:\n" + $"Expected: {JsonConvert.SerializeObject(expectedMapping.Objects[objectCounter])}\n"); + } else if (objectCounter >= expectedMapping.Objects.Count) + { Assert.Fail($"The conversion generated a hitobject, but should not have, for hitobject at time: {ourMapping.StartTime}:\n" + $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n"); + } else if (!expectedMapping.Objects[objectCounter].Equals(ourMapping.Objects[objectCounter])) { Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}:\n" diff --git a/osu.Game/Tests/Visual/OsuGridTestScene.cs b/osu.Game/Tests/Visual/OsuGridTestScene.cs index c09f4d6218..48f85be6ba 100644 --- a/osu.Game/Tests/Visual/OsuGridTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGridTestScene.cs @@ -37,9 +37,12 @@ namespace osu.Game.Tests.Visual Add(testContainer = new GridContainer { RelativeSizeAxes = Axes.Both }); cells = new Drawable[rows, cols]; + for (int r = 0; r < rows; r++) - for (int c = 0; c < cols; c++) - cells[r, c] = new Container { RelativeSizeAxes = Axes.Both }; + { + for (int c = 0; c < cols; c++) + cells[r, c] = new Container { RelativeSizeAxes = Axes.Both }; + } testContainer.Content = cells.ToJagged(); } diff --git a/osu.Game/Users/UserCoverBackground.cs b/osu.Game/Users/UserCoverBackground.cs index e583acac9f..a45fd85901 100644 --- a/osu.Game/Users/UserCoverBackground.cs +++ b/osu.Game/Users/UserCoverBackground.cs @@ -46,6 +46,7 @@ namespace osu.Game.Users }; } else + { InternalChild = new Sprite { RelativeSizeAxes = Axes.Both, @@ -54,6 +55,7 @@ namespace osu.Game.Users Anchor = Anchor.Centre, Origin = Anchor.Centre }; + } } protected override void LoadComplete() diff --git a/osu.iOS/AppDelegate.cs b/osu.iOS/AppDelegate.cs index 164a182ebe..14e3627752 100644 --- a/osu.iOS/AppDelegate.cs +++ b/osu.iOS/AppDelegate.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Foundation; using osu.Framework.iOS; -using osu.Framework.Threading; using UIKit; namespace osu.iOS diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index 378ac231c2..d60a3475e7 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -1,4 +1,4 @@ - + Debug diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index eb9e3ffafb..44c5c05bc0 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -12,7 +12,7 @@ HINT HINT WARNING - + WARNING True WARNING WARNING @@ -70,11 +70,20 @@ WARNING WARNING DO_NOT_SHOW + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING HINT WARNING DO_NOT_SHOW WARNING HINT + DO_NOT_SHOW HINT HINT ERROR @@ -125,6 +134,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING ERROR @@ -171,7 +181,7 @@ WARNING WARNING WARNING - HINT + WARNING WARNING WARNING WARNING @@ -201,6 +211,7 @@ HINT HINT + HINT WARNING WARNING WARNING @@ -218,9 +229,14 @@ WARNING <?xml version="1.0" encoding="utf-16"?><Profile name="Code Cleanup (peppy)"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSUseVar><BehavourStyle>CAN_CHANGE_TO_EXPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_EXPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSUpdateFileHeader>True</CSUpdateFileHeader><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSArrangeQualifiers>True</CSArrangeQualifiers></Profile> Code Cleanup (peppy) - Required - Required - Required + RequiredForMultiline + RequiredForMultiline + RequiredForMultiline + RequiredForMultiline + RequiredForMultiline + RequiredForMultiline + RequiredForMultiline + RequiredForMultiline Explicit ExpressionBody ExpressionBody @@ -239,6 +255,10 @@ 1 NEXT_LINE MULTILINE + True + True + True + True NEXT_LINE 1 1 @@ -289,397 +309,397 @@ SHA SRGB TK - SS - PP - GMT - QAT - BNG + SS + PP + GMT + QAT + BNG UI False HINT <?xml version="1.0" encoding="utf-16"?> <Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> - <TypePattern DisplayName="COM interfaces or structs"> - <TypePattern.Match> - <Or> - <And> - <Kind Is="Interface" /> - <Or> - <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /> - <HasAttribute Name="System.Runtime.InteropServices.ComImport" /> - </Or> - </And> - <Kind Is="Struct" /> - </Or> - </TypePattern.Match> - </TypePattern> - <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"> - <TypePattern.Match> - <And> - <Kind Is="Class" /> - <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /> - </And> - </TypePattern.Match> - <Entry DisplayName="Setup/Teardown Methods"> - <Entry.Match> - <And> - <Kind Is="Method" /> - <Or> - <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /> - <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /> - <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" /> - <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" /> - </Or> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="All other members" /> - <Entry Priority="100" DisplayName="Test Methods"> - <Entry.Match> - <And> - <Kind Is="Method" /> - <HasAttribute Name="NUnit.Framework.TestAttribute" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Name /> - </Entry.SortBy> - </Entry> - </TypePattern> - <TypePattern DisplayName="Default Pattern"> - <Group DisplayName="Fields/Properties"> - <Group DisplayName="Public Fields"> - <Entry DisplayName="Constant Fields"> - <Entry.Match> - <And> - <Access Is="Public" /> - <Or> - <Kind Is="Constant" /> - <Readonly /> - <And> - <Static /> - <Readonly /> - </And> - </Or> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Static Fields"> - <Entry.Match> - <And> - <Access Is="Public" /> - <Static /> - <Not> - <Readonly /> - </Not> - <Kind Is="Field" /> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Normal Fields"> - <Entry.Match> - <And> - <Access Is="Public" /> - <Not> - <Or> - <Static /> - <Readonly /> - </Or> - </Not> - <Kind Is="Field" /> - </And> - </Entry.Match> - </Entry> - </Group> - <Entry DisplayName="Public Properties"> - <Entry.Match> - <And> - <Access Is="Public" /> - <Kind Is="Property" /> - </And> - </Entry.Match> - </Entry> - <Group DisplayName="Internal Fields"> - <Entry DisplayName="Constant Fields"> - <Entry.Match> - <And> - <Access Is="Internal" /> - <Or> - <Kind Is="Constant" /> - <Readonly /> - <And> - <Static /> - <Readonly /> - </And> - </Or> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Static Fields"> - <Entry.Match> - <And> - <Access Is="Internal" /> - <Static /> - <Not> - <Readonly /> - </Not> - <Kind Is="Field" /> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Normal Fields"> - <Entry.Match> - <And> - <Access Is="Internal" /> - <Not> - <Or> - <Static /> - <Readonly /> - </Or> - </Not> - <Kind Is="Field" /> - </And> - </Entry.Match> - </Entry> - </Group> - <Entry DisplayName="Internal Properties"> - <Entry.Match> - <And> - <Access Is="Internal" /> - <Kind Is="Property" /> - </And> - </Entry.Match> - </Entry> - <Group DisplayName="Protected Fields"> - <Entry DisplayName="Constant Fields"> - <Entry.Match> - <And> - <Access Is="Protected" /> - <Or> - <Kind Is="Constant" /> - <Readonly /> - <And> - <Static /> - <Readonly /> - </And> - </Or> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Static Fields"> - <Entry.Match> - <And> - <Access Is="Protected" /> - <Static /> - <Not> - <Readonly /> - </Not> - <Kind Is="Field" /> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Normal Fields"> - <Entry.Match> - <And> - <Access Is="Protected" /> - <Not> - <Or> - <Static /> - <Readonly /> - </Or> - </Not> - <Kind Is="Field" /> - </And> - </Entry.Match> - </Entry> - </Group> - <Entry DisplayName="Protected Properties"> - <Entry.Match> - <And> - <Access Is="Protected" /> - <Kind Is="Property" /> - </And> - </Entry.Match> - </Entry> - <Group DisplayName="Private Fields"> - <Entry DisplayName="Constant Fields"> - <Entry.Match> - <And> - <Access Is="Private" /> - <Or> - <Kind Is="Constant" /> - <Readonly /> - <And> - <Static /> - <Readonly /> - </And> - </Or> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Static Fields"> - <Entry.Match> - <And> - <Access Is="Private" /> - <Static /> - <Not> - <Readonly /> - </Not> - <Kind Is="Field" /> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Normal Fields"> - <Entry.Match> - <And> - <Access Is="Private" /> - <Not> - <Or> - <Static /> - <Readonly /> - </Or> - </Not> - <Kind Is="Field" /> - </And> - </Entry.Match> - </Entry> - </Group> - <Entry DisplayName="Private Properties"> - <Entry.Match> - <And> - <Access Is="Private" /> - <Kind Is="Property" /> - </And> - </Entry.Match> - </Entry> - </Group> - <Group DisplayName="Constructor/Destructor"> - <Entry DisplayName="Ctor"> - <Entry.Match> - <Kind Is="Constructor" /> - </Entry.Match> - </Entry> - <Region Name="Disposal"> - <Entry DisplayName="Dtor"> - <Entry.Match> - <Kind Is="Destructor" /> - </Entry.Match> - </Entry> - <Entry DisplayName="Dispose()"> - <Entry.Match> - <And> - <Access Is="Public" /> - <Kind Is="Method" /> - <Name Is="Dispose" /> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Dispose(true)"> - <Entry.Match> - <And> - <Access Is="Protected" /> - <Or> - <Virtual /> - <Override /> - </Or> - <Kind Is="Method" /> - <Name Is="Dispose" /> - </And> - </Entry.Match> - </Entry> - </Region> - </Group> - <Group DisplayName="Methods"> - <Group DisplayName="Public"> - <Entry DisplayName="Static Methods"> - <Entry.Match> - <And> - <Access Is="Public" /> - <Static /> - <Kind Is="Method" /> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Methods"> - <Entry.Match> - <And> - <Access Is="Public" /> - <Not> - <Static /> - </Not> - <Kind Is="Method" /> - </And> - </Entry.Match> - </Entry> - </Group> - <Group DisplayName="Internal"> - <Entry DisplayName="Static Methods"> - <Entry.Match> - <And> - <Access Is="Internal" /> - <Static /> - <Kind Is="Method" /> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Methods"> - <Entry.Match> - <And> - <Access Is="Internal" /> - <Not> - <Static /> - </Not> - <Kind Is="Method" /> - </And> - </Entry.Match> - </Entry> - </Group> - <Group DisplayName="Protected"> - <Entry DisplayName="Static Methods"> - <Entry.Match> - <And> - <Access Is="Protected" /> - <Static /> - <Kind Is="Method" /> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Methods"> - <Entry.Match> - <And> - <Access Is="Protected" /> - <Not> - <Static /> - </Not> - <Kind Is="Method" /> - </And> - </Entry.Match> - </Entry> - </Group> - <Group DisplayName="Private"> - <Entry DisplayName="Static Methods"> - <Entry.Match> - <And> - <Access Is="Private" /> - <Static /> - <Kind Is="Method" /> - </And> - </Entry.Match> - </Entry> - <Entry DisplayName="Methods"> - <Entry.Match> - <And> - <Access Is="Private" /> - <Not> - <Static /> - </Not> - <Kind Is="Method" /> - </And> - </Entry.Match> - </Entry> - </Group> - </Group> - </TypePattern> + <TypePattern DisplayName="COM interfaces or structs"> + <TypePattern.Match> + <Or> + <And> + <Kind Is="Interface" /> + <Or> + <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /> + <HasAttribute Name="System.Runtime.InteropServices.ComImport" /> + </Or> + </And> + <Kind Is="Struct" /> + </Or> + </TypePattern.Match> + </TypePattern> + <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"> + <TypePattern.Match> + <And> + <Kind Is="Class" /> + <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /> + </And> + </TypePattern.Match> + <Entry DisplayName="Setup/Teardown Methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <Or> + <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" /> + <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" /> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="All other members" /> + <Entry Priority="100" DisplayName="Test Methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <HasAttribute Name="NUnit.Framework.TestAttribute" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Name /> + </Entry.SortBy> + </Entry> + </TypePattern> + <TypePattern DisplayName="Default Pattern"> + <Group DisplayName="Fields/Properties"> + <Group DisplayName="Public Fields"> + <Entry DisplayName="Constant Fields"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Or> + <Kind Is="Constant" /> + <Readonly /> + <And> + <Static /> + <Readonly /> + </And> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Static Fields"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Static /> + <Not> + <Readonly /> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Normal Fields"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Not> + <Or> + <Static /> + <Readonly /> + </Or> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Entry DisplayName="Public Properties"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Kind Is="Property" /> + </And> + </Entry.Match> + </Entry> + <Group DisplayName="Internal Fields"> + <Entry DisplayName="Constant Fields"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Or> + <Kind Is="Constant" /> + <Readonly /> + <And> + <Static /> + <Readonly /> + </And> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Static Fields"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Static /> + <Not> + <Readonly /> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Normal Fields"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Not> + <Or> + <Static /> + <Readonly /> + </Or> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Entry DisplayName="Internal Properties"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Kind Is="Property" /> + </And> + </Entry.Match> + </Entry> + <Group DisplayName="Protected Fields"> + <Entry DisplayName="Constant Fields"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Or> + <Kind Is="Constant" /> + <Readonly /> + <And> + <Static /> + <Readonly /> + </And> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Static Fields"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Static /> + <Not> + <Readonly /> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Normal Fields"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Not> + <Or> + <Static /> + <Readonly /> + </Or> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Entry DisplayName="Protected Properties"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Kind Is="Property" /> + </And> + </Entry.Match> + </Entry> + <Group DisplayName="Private Fields"> + <Entry DisplayName="Constant Fields"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Or> + <Kind Is="Constant" /> + <Readonly /> + <And> + <Static /> + <Readonly /> + </And> + </Or> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Static Fields"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Static /> + <Not> + <Readonly /> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Normal Fields"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Not> + <Or> + <Static /> + <Readonly /> + </Or> + </Not> + <Kind Is="Field" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Entry DisplayName="Private Properties"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Kind Is="Property" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Group DisplayName="Constructor/Destructor"> + <Entry DisplayName="Ctor"> + <Entry.Match> + <Kind Is="Constructor" /> + </Entry.Match> + </Entry> + <Region Name="Disposal"> + <Entry DisplayName="Dtor"> + <Entry.Match> + <Kind Is="Destructor" /> + </Entry.Match> + </Entry> + <Entry DisplayName="Dispose()"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Kind Is="Method" /> + <Name Is="Dispose" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Dispose(true)"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Or> + <Virtual /> + <Override /> + </Or> + <Kind Is="Method" /> + <Name Is="Dispose" /> + </And> + </Entry.Match> + </Entry> + </Region> + </Group> + <Group DisplayName="Methods"> + <Group DisplayName="Public"> + <Entry DisplayName="Static Methods"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Static /> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Methods"> + <Entry.Match> + <And> + <Access Is="Public" /> + <Not> + <Static /> + </Not> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Group DisplayName="Internal"> + <Entry DisplayName="Static Methods"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Static /> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Methods"> + <Entry.Match> + <And> + <Access Is="Internal" /> + <Not> + <Static /> + </Not> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Group DisplayName="Protected"> + <Entry DisplayName="Static Methods"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Static /> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Methods"> + <Entry.Match> + <And> + <Access Is="Protected" /> + <Not> + <Static /> + </Not> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + </Group> + <Group DisplayName="Private"> + <Entry DisplayName="Static Methods"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Static /> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + <Entry DisplayName="Methods"> + <Entry.Match> + <And> + <Access Is="Private" /> + <Not> + <Static /> + </Not> + <Kind Is="Method" /> + </And> + </Entry.Match> + </Entry> + </Group> + </Group> + </TypePattern> </Patterns> Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. See the LICENCE file in the repository root for full licence text. @@ -774,7 +794,7 @@ Origin = Anchor.$anchor$, True InternalChildren = new Drawable[] { - $END$ + $END$ }; True True @@ -787,12 +807,12 @@ Origin = Anchor.$anchor$, True new GridContainer { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] { $END$ }, - new Drawable[] { } - } + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] { $END$ }, + new Drawable[] { } + } }; True True @@ -805,12 +825,12 @@ Origin = Anchor.$anchor$, True new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - $END$ - } + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + $END$ + } }, True True @@ -823,11 +843,11 @@ Origin = Anchor.$anchor$, True new Container { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - $END$ - } + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + $END$ + } }, True True @@ -841,7 +861,7 @@ Origin = Anchor.$anchor$, [BackgroundDependencyLoader] private void load() { - $END$ + $END$ } True True @@ -854,8 +874,8 @@ private void load() True new Box { - Colour = Color4.Black, - RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, }, True True @@ -868,7 +888,7 @@ private void load() True Children = new Drawable[] { - $END$ + $END$ }; True True