Merge remote-tracking branch 'origin/master' into optimize-keycombination

This commit is contained in:
smoogipoo
2019-11-12 17:27:01 +09:00
72 changed files with 688 additions and 249 deletions

View File

@@ -111,9 +111,15 @@ csharp_preserve_single_line_statements = true
#Roslyn language styles
#Style - this. qualification
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning
#Style - type names
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
dotnet_style_predefined_type_for_member_access = true:warning
csharp_style_var_when_type_is_apparent = true:none
csharp_style_var_for_built_in_types = true:none
csharp_style_var_elsewhere = true:silent
@@ -126,12 +132,13 @@ csharp_preferred_modifier_order = public,private,protected,internal,new,abstract
# Skipped because roslyn cannot separate +-*/ with << >>
#Style - expression bodies
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_accessors = true:warning
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_indexers = true:warning
csharp_style_expression_bodied_methods = true:silent
csharp_style_expression_bodied_operators = true:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_operators = true:warning
csharp_style_expression_bodied_properties = true:warning
csharp_style_expression_bodied_local_functions = true:silent
#Style - expression preferences
dotnet_style_object_initializer = true:warning
@@ -145,32 +152,37 @@ dotnet_style_prefer_compound_assignment = true:warning
#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_pattern_matching_over_is_with_cast_check = true:warning
csharp_style_pattern_matching_over_as_with_null_check = true:warning
csharp_style_throw_expression = true:silent
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_conditional_delegate_call = true:warning
#Style - unused
dotnet_style_readonly_field = true:silent
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:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:warning
#Style - variable declaration
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_deconstructed_variable_declaration = true:silent
csharp_style_inlined_variable_declaration = true:warning
csharp_style_deconstructed_variable_declaration = true:warning
#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:warning
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
#Style - C# 8 features
csharp_prefer_static_local_function = true:warning
csharp_prefer_simple_using_statement = true:silent
csharp_style_prefer_index_operator = false:none
csharp_style_prefer_range_operator = false:none
csharp_style_prefer_switch_expression = false:none
#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

76
.gitignore vendored
View File

@@ -247,13 +247,6 @@ paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
@@ -262,3 +255,72 @@ __pycache__/
/tools/**
/build/tools/**
/build/temp/**
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
.idea/modules.xml
.idea/*.iml
.idea/modules
*.iml
*.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

View File

@@ -0,0 +1 @@
osu-framework.Android

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ContentModelUserStore">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.jetbrains.rider.android.RiderAndroidMiscFileCreationComponent">
<option name="ENSURE_MISC_FILE_EXISTS" value="true" />
</component>
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.osu-framework.Android/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.osu-framework.Android/riderModule.iml" />
</modules>
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1 @@
osu-framework.Desktop

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ContentModelUserStore">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.jetbrains.rider.android.RiderAndroidMiscFileCreationComponent">
<option name="ENSURE_MISC_FILE_EXISTS" value="true" />
</component>
</project>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.osu-framework/SampleGame.Android.iml" filepath="$PROJECT_DIR$/.idea/.idea.osu-framework/SampleGame.Android.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.osu-framework/osu.Framework.Tests.Android.iml" filepath="$PROJECT_DIR$/.idea/.idea.osu-framework/osu.Framework.Tests.Android.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.osu-framework.Desktop/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.osu-framework.Desktop/riderModule.iml" />
</modules>
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RiderProjectSettingsUpdater">
<option name="vcsConfiguration" value="1" />
</component>
</project>

View File

@@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="SampleGame" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/SampleGame.Desktop/bin/Debug/netcoreapp3.0/SampleGame.Desktop.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/SampleGame.Desktop/bin/Debug/netcoreapp3.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/SampleGame.Desktop/SampleGame.Desktop.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1 @@
osu-framework.iOS

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ContentModelUserStore">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.jetbrains.rider.android.RiderAndroidMiscFileCreationComponent">
<option name="ENSURE_MISC_FILE_EXISTS" value="true" />
</component>
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.osu-framework.iOS/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.osu-framework.iOS/riderModule.iml" />
</modules>
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -1,7 +1,7 @@
<!-- Contains required properties for osu!framework projects. -->
<Project>
<PropertyGroup Label="C#">
<LangVersion>7.3</LangVersion>
<LangVersion>8.0</LangVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup Label="License">

View File

@@ -13,7 +13,7 @@ A game framework written with [osu!](https://github.com/ppy/osu) in mind.
- 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 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 Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio Code](https://code.visualstudio.com/) (with the C# plugin installed) or [Jetbrains Rider](https://www.jetbrains.com/rider/) (commercial).
- 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/).
## Objectives
@@ -23,6 +23,17 @@ This framework is intended to take steps beyond what you would normally expect f
- Common elements used by games (texture caching, font loading) will be automatically initialised at runtime.
- Allow for isolated development of components via a solid testing environment (`VisualTests` and `TestCases`). Check the [wiki](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) for more information on how these can be used to streamline development.
### Building
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided [below](#contributing).
- Visual Studio / Rider users should load the project via one of the platform-specific .slnf files, rather than the main .sln. This will allow access to template run configurations.
- Visual Studio Code users must run the `Restore` task before any build attempt.
### 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.
## Contributing
Contributions can be made via pull requests to this repository.

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

View File

@@ -70,6 +70,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToLambdaExpression/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToLocalFunction/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToStaticClass/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToUsingDeclaration/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=DoubleNegationOperator/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EmptyGeneralCatchClause/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceDoWhileStatementBraces/@EntryIndexedValue">WARNING</s:String>
@@ -118,6 +119,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ParameterHidesMember/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ParameterOnlyUsedForPreconditionCheck_002EGlobal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ParameterOnlyUsedForPreconditionCheck_002ELocal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PatternAlwaysOfType/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PossibleInterfaceMemberAmbiguity/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PossibleMultipleEnumeration/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PrivateVariableCanBeMadeReadonly/@EntryIndexedValue">WARNING</s:String>
@@ -196,6 +198,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralTypo/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FBuiltInTypes/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuspiciousTypeConversion_002EGlobal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SwitchStatementMissingSomeCases/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=TabsAndSpacesMismatch/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=TabsAreDisallowed/@EntryIndexedValue">WARNING</s:String>
@@ -214,9 +217,11 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedParameter_002EGlobal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedParameter_002ELocal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseAwaitUsing/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseCollectionCountProperty/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseFormatSpecifierInFormatString/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseFormatSpecifierInInterpolation/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseIndexFromEndExpression/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseNameofExpression/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseNegatedPatternMatching/@EntryIndexedValue"></s:String>
<s:Boolean x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseNegatedPatternMatching/@EntryIndexRemoved">True</s:Boolean>

View File

@@ -1,5 +1,6 @@
<Project>
<PropertyGroup>
<LangVersion>8.0</LangVersion>
<OutputPath>bin\$(Configuration)</OutputPath>
<WarningLevel>4</WarningLevel>
<SchemaVersion>2.0</SchemaVersion>

View File

@@ -1,6 +1,7 @@
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Framework.Android.props" />
<PropertyGroup>
<LangVersion>8.0</LangVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{320089C6-A141-4D3E-BD5F-C4A6CE9E567B}</ProjectGuid>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

View File

@@ -0,0 +1,69 @@
// 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.
using NUnit.Framework;
using osu.Framework.Bindables;
namespace osu.Framework.Tests.Bindables
{
[TestFixture]
public class BindableWithCurrentTest
{
[Test]
public void TestBindableWithCurrentReceivesBoundValue()
{
const string expected_value = "test";
var bindable = new Bindable<string>(expected_value);
var bindableWithCurrent = new BindableWithCurrent<string> { Current = bindable };
Assert.That(bindable.Value, Is.EqualTo(expected_value));
Assert.That(bindableWithCurrent.Value, Is.EqualTo(expected_value));
}
[Test]
public void TestBindableWithCurrentReceivesValueChanges()
{
const string expected_value = "test2";
var bindable = new Bindable<string>();
var bindableWithCurrent = new BindableWithCurrent<string> { Current = bindable };
bindable.Value = expected_value;
Assert.That(bindableWithCurrent.Value, Is.EqualTo(expected_value));
}
[Test]
public void TestChangeCurrentDoesNotUnbindOthers()
{
const string expected_value = "test2";
var bindable1 = new Bindable<string>();
var bindable2 = bindable1.GetBoundCopy();
var bindableWithCurrent = new BindableWithCurrent<string> { Current = bindable1 };
bindableWithCurrent.Current = new Bindable<string>();
bindable1.Value = expected_value;
Assert.That(bindable2.Value, Is.EqualTo(expected_value));
Assert.That(bindableWithCurrent.Value, Is.Not.EqualTo(expected_value));
}
[Test]
public void TestChangeCurrentBindsToNewBindable()
{
const string expected_value = "test3";
var bindable1 = new Bindable<string>();
var bindable2 = new Bindable<string>();
var bindableWithCurrent = new BindableWithCurrent<string> { Current = bindable1 };
bindableWithCurrent.Current = bindable2;
bindableWithCurrent.Value = "test3";
Assert.That(bindable1.Value, Is.Not.EqualTo(expected_value));
Assert.That(bindable2.Value, Is.EqualTo(expected_value));
}
}
}

View File

@@ -48,7 +48,7 @@ namespace osu.Framework.Tests.IO
[Test]
public void TestCustomComparer()
{
int compare(int i1, int i2) => i2.CompareTo(i1);
static int compare(int i1, int i2) => i2.CompareTo(i1);
var original = new SortedList<int>(compare);
original.AddRange(new[] { 1, 2, 3, 4, 5, 6 });

View File

@@ -20,7 +20,7 @@ namespace osu.Framework.Tests.Polygons
private static readonly Vector2 right_1 = new Vector2(1, 0);
private static readonly Vector2 right_2 = new Vector2(2, 0);
private static object[] testCases =
private static readonly object[] test_cases =
{
// Parallel
new object[] { new Line(origin, up_1), new Line(right_1, right_1 + up_1), false, 0f },
@@ -62,7 +62,7 @@ namespace osu.Framework.Tests.Polygons
new object[] { new Line(origin, up_1), new Line(down_1, origin), false, 0f },
};
[TestCaseSource(nameof(testCases))]
[TestCaseSource(nameof(test_cases))]
public void TestIntersections(Line l1, Line l2, bool expectedResult, float expectedT)
{
(bool success, float t) = l1.IntersectWith(l2);

View File

@@ -242,6 +242,21 @@ namespace osu.Framework.Tests.Threading
Assert.AreEqual(1, invocations);
}
[Test]
public void TestPerUpdateTask()
{
int invocations = 0;
scheduler.AddDelayed(() => invocations++, 0, true);
Assert.AreEqual(0, invocations);
scheduler.Update();
Assert.AreEqual(1, invocations);
scheduler.Update();
Assert.AreEqual(2, invocations);
}
[Test]
public void TestScheduleFromInsideDelegate([Values(false, true)] bool forceScheduled)
{
@@ -274,5 +289,64 @@ namespace osu.Framework.Tests.Threading
scheduleTask();
}, forceScheduled);
}
[Test]
public void TestInvokeBeforeSchedulerRun()
{
int invocations = 0;
ScheduledDelegate del = new ScheduledDelegate(() => invocations++);
scheduler.Add(del);
Assert.AreEqual(0, invocations);
del.RunTask();
Assert.AreEqual(1, invocations);
scheduler.Update();
Assert.AreEqual(1, invocations);
}
[Test]
public void TestInvokeAfterSchedulerRun()
{
int invocations = 0;
ScheduledDelegate del = new ScheduledDelegate(() => invocations++);
scheduler.Add(del);
Assert.AreEqual(0, invocations);
scheduler.Update();
Assert.AreEqual(1, invocations);
Assert.Throws<InvalidOperationException>(del.RunTask);
Assert.AreEqual(1, invocations);
}
[Test]
public void TestInvokeBeforeScheduleUpdate()
{
int invocations = 0;
ScheduledDelegate del;
scheduler.Add(del = new ScheduledDelegate(() => invocations++));
Assert.AreEqual(0, invocations);
del.RunTask();
Assert.AreEqual(1, invocations);
scheduler.Update();
Assert.AreEqual(1, invocations);
}
[Test]
public void TestRepeatAlreadyCompletedSchedule()
{
int invocations = 0;
var del = new ScheduledDelegate(() => invocations++);
del.RunTask();
Assert.AreEqual(1, invocations);
Assert.Throws<InvalidOperationException>(() => scheduler.Add(del));
scheduler.Update();
Assert.AreEqual(1, invocations);
}
}
}

View File

@@ -37,7 +37,7 @@ namespace osu.Framework.Tests.Visual.Containers
AddAssert("has correct corner radius", () => hasCorrectCornerRadius);
void onUpdate(Container parent)
static void onUpdate(Container parent)
{
// Suppose the parent has some arbitrary size prior to the child being updated...
parent.Size = Vector2.One;

View File

@@ -119,14 +119,14 @@ namespace osu.Framework.Tests.Visual.Containers
{
public new Vector2 RelativeChildSize
{
protected get { return innerContainer.RelativeChildSize; }
set { innerContainer.RelativeChildSize = value; }
protected get => innerContainer.RelativeChildSize;
set => innerContainer.RelativeChildSize = value;
}
public new Vector2 RelativeChildOffset
{
protected get { return innerContainer.RelativeChildOffset; }
set { innerContainer.RelativeChildOffset = value; }
protected get => innerContainer.RelativeChildOffset;
set => innerContainer.RelativeChildOffset = value;
}
private readonly Container innerContainer;

View File

@@ -234,7 +234,7 @@ namespace osu.Framework.Tests.Visual.Containers
case 4:
{
Drawable createMaskingBox(float scale)
static Drawable createMaskingBox(float scale)
{
float size = 200 / scale;
return new Container

View File

@@ -1,7 +1,6 @@
// 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.
using System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
@@ -112,9 +111,9 @@ namespace osu.Framework.Tests.Visual.Input
var currentState = inputManager.CurrentState;
var mouse = currentState.Mouse;
inputManagerStatus.Text = $"{inputManager}";
mouseStatus.Text = $"Mouse: {mouse.Position} {mouse.Scroll} " + String.Join(" ", mouse.Buttons);
keyboardStatus.Text = "Keyboard: " + String.Join(" ", currentState.Keyboard.Keys);
joystickStatus.Text = "Joystick: " + String.Join(" ", currentState.Joystick.Buttons);
mouseStatus.Text = $"Mouse: {mouse.Position} {mouse.Scroll} " + string.Join(" ", mouse.Buttons);
keyboardStatus.Text = "Keyboard: " + string.Join(" ", currentState.Keyboard.Keys);
joystickStatus.Text = "Joystick: " + string.Join(" ", currentState.Joystick.Buttons);
base.Update();
}

View File

@@ -70,7 +70,7 @@ namespace osu.Framework.Tests.Visual.UserInterface
[Test]
public void TestDirectToggle()
{
var testBindable = basic.Current.GetBoundCopy();
var testBindable = new Bindable<bool> { BindTarget = basic.Current };
AddAssert("is unchecked", () => !basic.Current.Value);
AddAssert("bindable unchecked", () => !testBindable.Value);

View File

@@ -96,7 +96,7 @@ namespace osu.Framework.Tests.Visual.UserInterface
public TestFocusedOverlayContainer(bool startHidden = true)
{
this.StartHidden = startHidden;
StartHidden = startHidden;
Size = new Vector2(0.5f);
RelativeSizeAxes = Axes.Both;

View File

@@ -54,7 +54,7 @@ namespace osu.Framework.Tests.Visual.UserInterface
},
sliderBar = new BasicSliderBar<double>
{
Size = new Vector2(200, 10),
Size = new Vector2(200, 50),
BackgroundColour = Color4.White,
SelectionColour = Color4.Pink,
KeyboardStep = 1,
@@ -97,9 +97,36 @@ namespace osu.Framework.Tests.Visual.UserInterface
sliderBar.Current.Value = 0;
}
[TestCase(true)]
[Test]
public void TestVerticalDragHasNoEffect()
{
checkValue(0, false);
AddStep("Move Cursor",
() => { InputManager.MoveMouseTo(sliderBar.ToScreenSpace(sliderBar.DrawSize * new Vector2(0.75f, 0.0f))); });
AddStep("Click", () => { InputManager.PressButton(MouseButton.Left); });
AddStep("Drag",
() => { InputManager.MoveMouseTo(sliderBar.ToScreenSpace(sliderBar.DrawSize * new Vector2(0.75f, 1f))); });
AddStep("Release Click", () => { InputManager.ReleaseButton(MouseButton.Left); });
checkValue(0, false);
}
[Test]
public void TestDragOutReleaseInHasNoEffect()
{
checkValue(0, false);
AddStep("Move Cursor",
() => { InputManager.MoveMouseTo(sliderBar.ToScreenSpace(sliderBar.DrawSize * new Vector2(0.75f, 0.0f))); });
AddStep("Click", () => { InputManager.PressButton(MouseButton.Left); });
AddStep("Drag", () => { InputManager.MoveMouseTo(sliderBar.ToScreenSpace(sliderBar.DrawSize * new Vector2(0.75f, 1.5f))); });
AddStep("Drag Left", () => { InputManager.MoveMouseTo(sliderBar.ToScreenSpace(sliderBar.DrawSize * new Vector2(0.25f, 1.5f))); });
AddStep("Drag Up", () => { InputManager.MoveMouseTo(sliderBar.ToScreenSpace(sliderBar.DrawSize * new Vector2(0.25f, 0.5f))); });
AddStep("Release Click", () => { InputManager.ReleaseButton(MouseButton.Left); });
checkValue(0, false);
}
[TestCase(false)]
public void SliderBar(bool disabled)
[TestCase(true)]
public void TestAdjustmentPrecision(bool disabled)
{
AddStep($"set disabled to {disabled}", () => sliderBar.Current.Disabled = disabled);
@@ -129,17 +156,9 @@ namespace osu.Framework.Tests.Visual.UserInterface
checkValue(5, disabled);
}
private void checkValue(int expected, bool disabled)
{
if (disabled)
AddAssert("value unchanged (disabled)", () => Precision.AlmostEquals(sliderBarValue.Value, 0, Precision.FLOAT_EPSILON));
else
AddAssert($"Value == {expected}", () => Precision.AlmostEquals(sliderBarValue.Value, expected, Precision.FLOAT_EPSILON));
}
[TestCase(true)]
[TestCase(false)]
public void TransferValueOnCommit(bool disabled)
[TestCase(true)]
public void TestTransferValueOnCommit(bool disabled)
{
AddStep($"set disabled to {disabled}", () => sliderBar.Current.Disabled = disabled);
@@ -161,6 +180,14 @@ namespace osu.Framework.Tests.Visual.UserInterface
checkValue(-5, disabled);
}
private void checkValue(int expected, bool disabled)
{
if (disabled)
AddAssert("value unchanged (disabled)", () => Precision.AlmostEquals(sliderBarValue.Value, 0, Precision.FLOAT_EPSILON));
else
AddAssert($"Value == {expected}", () => Precision.AlmostEquals(sliderBarValue.Value, expected, Precision.FLOAT_EPSILON));
}
private void sliderBarValueChanged(ValueChangedEvent<double> args)
{
sliderBarText.Text = $"Value of Bindable: {args.NewValue:N}";

View File

@@ -221,7 +221,7 @@ namespace osu.Framework.Tests.Visual.UserInterface
public TooltipSpriteText(string displayedContent, string tooltipContent)
{
this.TooltipText = tooltipContent;
TooltipText = tooltipContent;
AutoSizeAxes = Axes.Both;
Children = new[]

View File

@@ -0,0 +1,56 @@
// 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.
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
namespace osu.Framework.Tests.Visual.UserInterface
{
[HeadlessTest]
public class TestUIComponentsWithCurrent : FrameworkTestScene
{
[Test]
public void TestUnbindDoesntUnbindBound()
{
Bindable<string> bindable = new Bindable<string>("test");
Bindable<string> boundBindable = bindable.GetBoundCopy();
Assert.That(boundBindable.Value, Is.EqualTo(bindable.Value));
var dropdown = new BasicDropdown<string> { Current = bindable };
AddStep("add dropdown", () => Add(dropdown));
AddStep("expire", () => dropdown.Expire());
AddUntilStep("wait for dispose", () => dropdown.IsDisposed);
AddStep("update unrelated bindable", () => bindable.Value = "test2");
AddAssert("ensure current unbound", () => dropdown.Current.Value != bindable.Value);
AddAssert("ensure externals still bound", () => boundBindable.Value == bindable.Value);
}
[Test]
public void TestChangeCurrent()
{
Bindable<string> bindable = new Bindable<string>("test");
Bindable<string> bindable2 = new Bindable<string>("test2");
var dropdown = new BasicDropdown<string> { Current = bindable };
AddStep("add dropdown", () => Add(dropdown));
AddAssert("ensure current bound", () => dropdown.Current.Value == bindable.Value);
AddStep("change target", () => dropdown.Current = bindable2);
AddAssert("ensure current switched", () => dropdown.Current.Value == bindable2.Value);
AddAssert("ensure original intact", () => dropdown.Current.Value != bindable.Value);
AddStep("change value", () => bindable2.Value = "test3");
AddAssert("ensure current bound", () => dropdown.Current.Value == bindable2.Value);
AddAssert("ensure original intact", () => dropdown.Current.Value != bindable.Value);
}
// TODO: add tests for other components
}
}

View File

@@ -1,5 +1,6 @@
<Project>
<PropertyGroup>
<LangVersion>8.0</LangVersion>
<DefaultMtouchExtraArgs>--nolinkaway</DefaultMtouchExtraArgs>
<DefaultMtouchGccFlags>-lstdc++ -lbz2 -framework AudioToolbox -framework AVFoundation -framework CoreMedia -framework VideoToolbox -framework SystemConfiguration -framework CFNetwork -framework Accelerate</DefaultMtouchGccFlags>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
@@ -76,7 +77,7 @@
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="ManagedBass" Version="2.0.4" />
<PackageReference Include="ManagedBass.Fx" Version="2.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.6.0" />
<PackageReference Include="System.Reflection.Emit.ILGeneration" Version="4.6.0" />
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />

View File

@@ -45,7 +45,7 @@ namespace osu.Framework.iOS
int eventScanCode = (int)eventPtr[gsevent_keycode];
int eventLastModifier = lastEventFlags;
bool isBlockKey(int keyCode)
static bool isBlockKey(int keyCode)
=> keyCode == 79 || // Right
keyCode == 80 || // Left
keyCode == 81 || // Down

View File

@@ -75,11 +75,10 @@ namespace osu.Framework.Allocation
/// <returns>The requested dependency, or default(<typeparamref name="T"/>) if not found.</returns>
internal static T GetValue<T>(this IReadOnlyDependencyContainer container, CacheInfo info)
{
var result = container.Get(typeof(T), info);
if (result == null)
return default;
if (container.Get(typeof(T), info) is T value)
return value;
return (T)container.Get(typeof(T), info);
return default;
}
/// <summary>

View File

@@ -66,11 +66,9 @@ namespace osu.Framework.Allocation
try
{
var value = handle.Target;
if (value is T)
if (handle.Target is T value)
{
target = (T)value;
target = value;
return true;
}
}

View File

@@ -1,8 +1,6 @@
// 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.
using System;
namespace osu.Framework.Audio.Track
{
public interface ITrackStore : IAdjustableResourceStore<Track>
@@ -12,6 +10,6 @@ namespace osu.Framework.Audio.Track
/// </summary>
/// <param name="length">The length of the virtual track.</param>
/// <returns>A new virtual track.</returns>
Track GetVirtual(double length = Double.PositiveInfinity);
Track GetVirtual(double length = double.PositiveInfinity);
}
}

View File

@@ -0,0 +1,30 @@
// 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.
using System;
using osu.Framework.Graphics.UserInterface;
namespace osu.Framework.Bindables
{
/// <summary>
/// A bindable which holds a reference to a bound target, allowing switching between targets and handling unbind/rebind.
/// </summary>
/// <typeparam name="T">The type of our stored <see cref="Bindable{T}.Value"/>.</typeparam>
public class BindableWithCurrent<T> : Bindable<T>, IHasCurrentValue<T>
{
private Bindable<T> currentBound;
public Bindable<T> Current
{
get => this;
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
if (currentBound != null) UnbindFrom(currentBound);
BindTo(currentBound = value);
}
}
}
}

View File

@@ -134,7 +134,7 @@ namespace osu.Framework.Graphics.Containers
{
Drawable c = children[i];
Axes toAxes(FillDirection direction)
static Axes toAxes(FillDirection direction)
{
switch (direction)
{

View File

@@ -302,15 +302,15 @@ namespace osu.Framework.Graphics.Containers
float distributionSize = Math.Max(0, spanLength - requiredSize) / distributionCount;
// Write the sizes of distributed cells. Ordering is important to maximize excess at every step
foreach (var value in distributedDimensions.OrderBy(d => d.dim.Range))
foreach (var (i, dim) in distributedDimensions.OrderBy(d => d.dim.Range))
{
// Cells start off at their minimum size, and the total size should not exceed their maximum size
cellSizes[value.i] = Math.Min(value.dim.MaxSize, value.dim.MinSize + distributionSize);
cellSizes[i] = Math.Min(dim.MaxSize, dim.MinSize + distributionSize);
// If there's no excess, any further distributions are guaranteed to also have no excess, so this becomes a null-op
// If there is an excess, the excess should be re-distributed among all other n-1 distributed cells
if (--distributionCount > 0)
distributionSize += Math.Max(0, distributionSize - value.dim.Range) / distributionCount;
distributionSize += Math.Max(0, distributionSize - dim.Range) / distributionCount;
}
return cellSizes;

View File

@@ -71,12 +71,10 @@ namespace osu.Framework.Graphics.Containers
!filterable.FilterTerms.Any(filterTerm =>
filterTerm.IndexOf(term, StringComparison.InvariantCultureIgnoreCase) >= 0)).ToArray();
var hasFilterableChildren = filterable as IHasFilterableChildren;
bool matching = childTerms.Length == 0;
//We need to check the children and should any child match this matches as well
if (hasFilterableChildren != null)
if (filterable is IHasFilterableChildren hasFilterableChildren)
{
foreach (IFilterable child in hasFilterableChildren.FilterableChildren)
matching |= match(child, childTerms, searchActive);

View File

@@ -57,7 +57,7 @@ namespace osu.Framework.Graphics.Containers
[CanBeNull]
set
{
value = value ?? Array.Empty<TableColumn>();
value ??= Array.Empty<TableColumn>();
if (columns == value)
return;
@@ -80,7 +80,7 @@ namespace osu.Framework.Graphics.Containers
[CanBeNull]
set
{
value = value ?? new Dimension();
value ??= new Dimension();
if (rowSize == value)
return;

View File

@@ -131,14 +131,7 @@ namespace osu.Framework.Graphics.Primitives
/// <returns>This method returns true if obj is a <see cref="RectangleF"/> and its X, Y, Width, and Height properties are equal to the corresponding properties of this <see cref="RectangleF"/>; otherwise, false.</returns>
/// <param name="obj">The <see cref="System.Object"/> to test.</param>
/// <filterpriority>1</filterpriority>
public override bool Equals(object obj)
{
if (!(obj is RectangleF))
return false;
RectangleF ef = (RectangleF)obj;
return ef.X == X && ef.Y == Y && ef.Width == Width && ef.Height == Height;
}
public override bool Equals(object obj) => obj is RectangleF rec && Equals(rec);
/// <summary>Tests whether two <see cref="RectangleF"/> structures have equal location and size.</summary>
/// <returns>This operator returns true if the two specified <see cref="RectangleF"/> structures have equal <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> properties.</returns>

View File

@@ -96,14 +96,7 @@ namespace osu.Framework.Graphics.Primitives
/// <returns>This method returns true if obj is a <see cref="RectangleI"/> and its X, Y, Width, and Height properties are equal to the corresponding properties of this <see cref="RectangleI"/>; otherwise, false.</returns>
/// <param name="obj">The <see cref="T:System.Object"/> to test. </param>
/// <filterpriority>1</filterpriority>
public override bool Equals(object obj)
{
if (!(obj is RectangleI))
return false;
RectangleI ef = (RectangleI)obj;
return ef.X == X && ef.Y == Y && ef.Width == Width && ef.Height == Height;
}
public override bool Equals(object obj) => obj is RectangleI rec && Equals(rec);
/// <summary>Tests whether two <see cref="RectangleI"/> structures have equal location and size.</summary>
/// <returns>This operator returns true if the two specified <see cref="RectangleI"/> structures have equal <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> properties.</returns>

View File

@@ -100,12 +100,7 @@ namespace osu.Framework.Graphics.Sprites
public bool Equals(FontUsage other) => string.Equals(Family, other.Family) && string.Equals(Weight, other.Weight) && Italics == other.Italics && Size.Equals(other.Size) && FixedWidth == other.FixedWidth;
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is FontUsage other && Equals(other);
}
public override bool Equals(object obj) => obj is FontUsage other && Equals(other);
public override int GetHashCode()
{

View File

@@ -72,12 +72,7 @@ namespace osu.Framework.Graphics.Sprites
public bool Equals(IconUsage other) => Icon == other.Icon && string.Equals(Family, other.Family) && string.Equals(Weight, other.Weight);
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is IconUsage other && Equals(other);
}
public override bool Equals(object obj) => obj is IconUsage other && Equals(other);
public override int GetHashCode()
{

View File

@@ -95,21 +95,12 @@ namespace osu.Framework.Graphics.Sprites
}
}
private readonly Bindable<string> current = new Bindable<string>(string.Empty);
private Bindable<string> currentBound;
private readonly BindableWithCurrent<string> current = new BindableWithCurrent<string>();
public Bindable<string> Current
{
get => current;
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
if (currentBound != null) current.UnbindFrom(currentBound);
current.BindTo(currentBound = value);
}
get => current.Current;
set => current.Current = value;
}
private string displayedText => localisedText?.Value ?? text.Text.Original;

View File

@@ -1,7 +1,6 @@
// 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.
using System;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
@@ -13,24 +12,12 @@ namespace osu.Framework.Graphics.UserInterface
/// </summary>
public abstract class Checkbox : Container, IHasCurrentValue<bool>
{
private readonly Bindable<bool> current = new Bindable<bool>();
private readonly BindableWithCurrent<bool> current = new BindableWithCurrent<bool>();
private Bindable<bool> currentBound;
/// <summary>
/// A bindable that holds the value if the checkbox is checked or not.
/// </summary>
public Bindable<bool> Current
{
get => current;
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
if (currentBound != null) current.UnbindFrom(currentBound);
current.BindTo(currentBound = value);
}
get => current.Current;
set => current.Current = value;
}
protected override bool OnClick(ClickEvent e)

View File

@@ -1,7 +1,6 @@
// 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.
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Shaders;
@@ -13,21 +12,12 @@ namespace osu.Framework.Graphics.UserInterface
{
public class CircularProgress : Drawable, ITexturedShaderDrawable, IHasCurrentValue<double>
{
private readonly Bindable<double> current = new Bindable<double>();
private Bindable<double> currentBound;
private readonly BindableWithCurrent<double> current = new BindableWithCurrent<double>();
public Bindable<double> Current
{
get => current;
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
if (currentBound != null) current.UnbindFrom(currentBound);
current.BindTo(currentBound = value);
}
get => current.Current;
set => current.Current = value;
}
public CircularProgress()

View File

@@ -164,21 +164,12 @@ namespace osu.Framework.Graphics.UserInterface
}
}
private readonly Bindable<T> current = new Bindable<T>();
private Bindable<T> currentBound;
private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>();
public Bindable<T> Current
{
get => current;
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
if (currentBound != null) current.UnbindFrom(currentBound);
current.BindTo(currentBound = value);
}
get => current.Current;
set => current.Current = value;
}
private DropdownMenuItem<T> selectedItem;

View File

@@ -120,10 +120,22 @@ namespace osu.Framework.Graphics.UserInterface
UpdateValue(NormalizedValue);
}
private bool handleClick;
protected override bool OnMouseDown(MouseDownEvent e)
{
handleClick = true;
return base.OnMouseDown(e);
}
protected override bool OnClick(ClickEvent e)
{
handleMouseInput(e);
commit();
if (handleClick)
{
handleMouseInput(e);
commit();
}
return true;
}
@@ -135,9 +147,16 @@ namespace osu.Framework.Graphics.UserInterface
protected override bool OnDragStart(DragStartEvent e)
{
handleMouseInput(e);
Vector2 posDiff = e.MouseDownPosition - e.MousePosition;
return Math.Abs(posDiff.X) > Math.Abs(posDiff.Y);
if (Math.Abs(posDiff.X) < Math.Abs(posDiff.Y))
{
handleClick = false;
return false;
}
handleMouseInput(e);
return true;
}
protected override bool OnDragEnd(DragEndEvent e)

View File

@@ -27,21 +27,12 @@ namespace osu.Framework.Graphics.UserInterface
/// <typeparam name="T">The type of item to be represented by tabs.</typeparam>
public abstract class TabControl<T> : CompositeDrawable, IHasCurrentValue<T>, IKeyBindingHandler<PlatformAction>
{
private readonly Bindable<T> current = new Bindable<T>();
private Bindable<T> currentBound;
private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>();
public Bindable<T> Current
{
get => current;
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
if (currentBound != null) current.UnbindFrom(currentBound);
current.BindTo(currentBound = value);
}
get => current.Current;
set => current.Current = value;
}
/// <summary>

View File

@@ -611,21 +611,12 @@ namespace osu.Framework.Graphics.UserInterface
set => Placeholder.Text = value;
}
private readonly Bindable<string> current = new Bindable<string>(string.Empty);
private Bindable<string> currentBound;
private readonly BindableWithCurrent<string> current = new BindableWithCurrent<string>();
public Bindable<string> Current
{
get => current;
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
if (currentBound != null) current.UnbindFrom(currentBound);
current.BindTo(currentBound = value);
}
get => current.Current;
set => current.Current = value;
}
private string text = string.Empty;
@@ -641,7 +632,7 @@ namespace osu.Framework.Graphics.UserInterface
if (value == text)
return;
lastCommitText = value = value ?? string.Empty;
lastCommitText = value ??= string.Empty;
Placeholder.FadeTo(value.Length == 0 ? 1 : 0);

View File

@@ -237,7 +237,7 @@ namespace osu.Framework.IO.Network
url = @"https://" + url.Replace(@"http://", @"");
}
using (abortToken = abortToken ?? new CancellationTokenSource()) // don't recreate if already non-null. is used during retry logic.
using (abortToken ??= new CancellationTokenSource()) // don't recreate if already non-null. is used during retry logic.
using (timeoutToken = new CancellationTokenSource())
using (var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(abortToken.Token, timeoutToken.Token))
{

View File

@@ -49,7 +49,7 @@ namespace osu.Framework.IO.Stores
FontName = assetName?.Split('/').Last();
}
public Task LoadFontAsync() => fontLoadTask ?? (fontLoadTask = Task.Factory.StartNew(() =>
public Task LoadFontAsync() => fontLoadTask ??= Task.Factory.StartNew(() =>
{
try
{
@@ -65,7 +65,7 @@ namespace osu.Framework.IO.Stores
completionSource.SetResult(null);
throw;
}
}, TaskCreationOptions.PreferFairness));
}, TaskCreationOptions.PreferFairness);
public bool HasGlyph(char c) => Font.Characters.ContainsKey(c);

View File

@@ -1,7 +1,6 @@
// 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.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -67,7 +66,7 @@ namespace osu.Framework.Input.States
pressedButtons.AddRange(other.pressedButtons);
}
public override string ToString() => $@"{GetType().ReadableName()}({String.Join(" ", pressedButtons)})";
public override string ToString() => $@"{GetType().ReadableName()}({string.Join(" ", pressedButtons)})";
public IEnumerator<TButton> GetEnumerator() => ((IEnumerable<TButton>)pressedButtons).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

View File

@@ -21,7 +21,7 @@ namespace osu.Framework.Input
protected internal UserInputManager()
{
// UserInputManager is at the very top of the draw hierarchy, so it has no parnt updating its IsAlive state
// UserInputManager is at the very top of the draw hierarchy, so it has no parent updating its IsAlive state
IsAlive = true;
UseParentInput = false;
}

View File

@@ -314,7 +314,7 @@ namespace osu.Framework.Logging
if (DebugUtils.IsDebugBuild)
{
void consoleLog(string msg)
static void consoleLog(string msg)
{
// fire to all debug listeners (like visual studio's output window)
System.Diagnostics.Debug.Print(msg);

View File

@@ -90,7 +90,19 @@ namespace osu.Framework.Statistics
while (!cancellation.IsCancellationRequested)
{
if (targetThread.IsAlive && clock.ElapsedMilliseconds - LastConsumptionTime > spikeRecordThreshold / 2 && backgroundMonitorStackTrace == null)
backgroundMonitorStackTrace = getStackTrace(targetThread);
{
try
{
Logger.Log("Retrieving background stack trace...");
backgroundMonitorStackTrace = getStackTrace(targetThread);
}
catch (Exception e)
{
Enabled = false;
Logger.Log($"Failed to retrieve background stack trace: {e}");
}
}
Thread.Sleep(5);
}
}

View File

@@ -76,7 +76,7 @@ namespace osu.Framework.Testing
}
});
string getSolutionPath(DirectoryInfo d)
static string getSolutionPath(DirectoryInfo d)
{
if (d == null)
return null;

View File

@@ -95,55 +95,8 @@ namespace osu.Framework.Threading
{
lock (queueLock)
{
double currentTimeLocal = currentTime;
if (timedTasks.Count > 0)
{
foreach (var sd in timedTasks)
{
if (sd.ExecutionTime <= currentTimeLocal)
{
tasksToRemove.Add(sd);
if (sd.Cancelled) continue;
runQueue.Enqueue(sd);
if (sd.RepeatInterval >= 0)
{
if (timedTasks.Count > 1000)
throw new ArgumentException("Too many timed tasks are in the queue!");
sd.SetNextExecution(currentTimeLocal);
tasksToSchedule.Add(sd);
}
}
}
foreach (var t in tasksToRemove)
timedTasks.Remove(t);
tasksToRemove.Clear();
foreach (var t in tasksToSchedule)
timedTasks.AddInPlace(t);
tasksToSchedule.Clear();
}
for (int i = 0; i < perUpdateTasks.Count; i++)
{
ScheduledDelegate task = perUpdateTasks[i];
if (task.Cancelled)
{
perUpdateTasks.RemoveAt(i--);
continue;
}
runQueue.Enqueue(task);
}
queueTimedTasks();
queuePerUpdateTasks();
}
int countToRun = runQueue.Count;
@@ -151,7 +104,7 @@ namespace osu.Framework.Threading
while (getNextTask(out ScheduledDelegate sd))
{
if (sd.Cancelled)
if (sd.Cancelled || sd.Completed)
continue;
//todo: error handling
@@ -164,6 +117,64 @@ namespace osu.Framework.Threading
return countRun;
}
private void queueTimedTasks()
{
double currentTimeLocal = currentTime;
if (timedTasks.Count > 0)
{
foreach (var sd in timedTasks)
{
if (sd.ExecutionTime <= currentTimeLocal)
{
tasksToRemove.Add(sd);
if (sd.Cancelled) continue;
if (sd.RepeatInterval >= 0)
{
if (timedTasks.Count > 1000)
throw new ArgumentException("Too many timed tasks are in the queue!");
sd.SetNextExecution(currentTimeLocal);
tasksToSchedule.Add(sd);
}
if (!sd.Completed)
runQueue.Enqueue(sd);
}
}
foreach (var t in tasksToRemove)
timedTasks.Remove(t);
tasksToRemove.Clear();
foreach (var t in tasksToSchedule)
timedTasks.AddInPlace(t);
tasksToSchedule.Clear();
}
}
private void queuePerUpdateTasks()
{
for (int i = 0; i < perUpdateTasks.Count; i++)
{
ScheduledDelegate task = perUpdateTasks[i];
task.Completed = false;
if (task.Cancelled)
{
perUpdateTasks.RemoveAt(i--);
continue;
}
runQueue.Enqueue(task);
}
}
private bool getNextTask(out ScheduledDelegate task)
{
lock (queueLock)
@@ -223,8 +234,16 @@ namespace osu.Framework.Threading
return false;
}
/// <summary>
/// Add a task to be scheduled.
/// </summary>
/// <param name="task">The scheduled delegate to add.</param>
/// <exception cref="InvalidOperationException">Thrown when attempting to add a scheduled delegate that has been already completed.</exception>
public void Add(ScheduledDelegate task)
{
if (task.Completed)
throw new InvalidOperationException($"Can not add a {nameof(ScheduledDelegate)} that has been already {nameof(ScheduledDelegate.Completed)}");
lock (queueLock)
{
if (task.RepeatInterval == 0)
@@ -292,7 +311,7 @@ namespace osu.Framework.Threading
/// <summary>
/// Whether this task has finished running.
/// </summary>
public bool Completed { get; private set; }
public bool Completed { get; internal set; }
/// <summary>
/// Whether this task has been cancelled.
@@ -312,10 +331,17 @@ namespace osu.Framework.Threading
RepeatInterval = repeatInterval;
}
/// <summary>
/// Invokes the scheduled task.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when attempting to run a task that has been cancelled or already completed.</exception>
public void RunTask()
{
if (Cancelled)
throw new InvalidOperationException($"Can not run a {nameof(ScheduledDelegate)} that has been {nameof(Cancelled)}");
throw new InvalidOperationException($"Can not run a {nameof(ScheduledDelegate)} that has been {nameof(Cancelled)}.");
if (Completed)
throw new InvalidOperationException($"Can not run a {nameof(ScheduledDelegate)} that has been already {nameof(Completed)}.");
Task();
Completed = true;
@@ -327,6 +353,8 @@ namespace osu.Framework.Threading
internal void SetNextExecution(double currentTime)
{
Completed = false;
ExecutionTime += RepeatInterval;
if (ExecutionTime < currentTime && !PerformRepeatCatchUpExecutions)

View File

@@ -36,7 +36,7 @@
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="ManagedBass" Version="2.0.4" />
<PackageReference Include="ManagedBass.Fx" Version="2.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.6.0" />
<PackageReference Include="System.Reflection.Emit.ILGeneration" Version="4.6.0" />
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />