mirror of
https://github.com/osukey/osukey.git
synced 2025-05-26 07:57:32 +09:00
Merge branch 'mod-overlay/back-button' into mod-overlay/integration
This commit is contained in:
commit
e7e7486a8e
@ -2,12 +2,6 @@
|
|||||||
"version": 1,
|
"version": 1,
|
||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"dotnet-format": {
|
|
||||||
"version": "3.1.37601",
|
|
||||||
"commands": [
|
|
||||||
"dotnet-format"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"jetbrains.resharper.globaltools": {
|
"jetbrains.resharper.globaltools": {
|
||||||
"version": "2022.1.0-eap10",
|
"version": "2022.1.0-eap10",
|
||||||
"commands": [
|
"commands": [
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
# EditorConfig is awesome: http://editorconfig.org
|
# EditorConfig is awesome: http://editorconfig.org
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
|
[*.{csproj,props,targets}]
|
||||||
|
charset = utf-8-bom
|
||||||
|
end_of_line = crlf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.cs]
|
[*.cs]
|
||||||
end_of_line = crlf
|
end_of_line = crlf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
@ -8,8 +16,19 @@ indent_style = space
|
|||||||
indent_size = 4
|
indent_size = 4
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
#license header
|
||||||
|
file_header_template = Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.\nSee the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#Roslyn naming styles
|
#Roslyn naming styles
|
||||||
|
|
||||||
|
#PascalCase for public and protected members
|
||||||
|
dotnet_naming_style.pascalcase.capitalization = pascal_case
|
||||||
|
dotnet_naming_symbols.public_members.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||||
|
dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event
|
||||||
|
dotnet_naming_rule.public_members_pascalcase.severity = error
|
||||||
|
dotnet_naming_rule.public_members_pascalcase.symbols = public_members
|
||||||
|
dotnet_naming_rule.public_members_pascalcase.style = pascalcase
|
||||||
|
|
||||||
#camelCase for private members
|
#camelCase for private members
|
||||||
dotnet_naming_style.camelcase.capitalization = camel_case
|
dotnet_naming_style.camelcase.capitalization = camel_case
|
||||||
|
|
||||||
@ -172,24 +191,11 @@ csharp_style_prefer_index_operator = false:silent
|
|||||||
csharp_style_prefer_range_operator = false:silent
|
csharp_style_prefer_range_operator = false:silent
|
||||||
csharp_style_prefer_switch_expression = false:none
|
csharp_style_prefer_switch_expression = false:none
|
||||||
|
|
||||||
#Supressing roslyn built-in analyzers
|
[*.{yaml,yml}]
|
||||||
# Suppress: EC112
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
#Private method is unused
|
indent_size = 2
|
||||||
dotnet_diagnostic.IDE0051.severity = silent
|
trim_trailing_whitespace = true
|
||||||
#Private member is unused
|
|
||||||
dotnet_diagnostic.IDE0052.severity = silent
|
|
||||||
|
|
||||||
#Rules for disposable
|
|
||||||
dotnet_diagnostic.IDE0067.severity = none
|
|
||||||
dotnet_diagnostic.IDE0068.severity = none
|
|
||||||
dotnet_diagnostic.IDE0069.severity = none
|
|
||||||
|
|
||||||
#Disable operator overloads requiring alternate named methods
|
|
||||||
dotnet_diagnostic.CA2225.severity = none
|
|
||||||
|
|
||||||
# Banned APIs
|
|
||||||
dotnet_diagnostic.RS0030.severity = error
|
|
||||||
|
|
||||||
dotnet_diagnostic.OLOC001.words_in_name = 5
|
dotnet_diagnostic.OLOC001.words_in_name = 5
|
||||||
dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text.
|
dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text.
|
||||||
|
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@ -33,6 +33,9 @@ jobs:
|
|||||||
path: ${{ github.workspace }}/inspectcode
|
path: ${{ github.workspace }}/inspectcode
|
||||||
key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }}
|
key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }}
|
||||||
|
|
||||||
|
- name: Dotnet code style
|
||||||
|
run: dotnet build -c Debug -warnaserror osu.Desktop.slnf -p:EnforceCodeStyleInBuild=true
|
||||||
|
|
||||||
- name: CodeFileSanity
|
- name: CodeFileSanity
|
||||||
run: |
|
run: |
|
||||||
# TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround.
|
# TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround.
|
||||||
@ -46,10 +49,6 @@ jobs:
|
|||||||
done <<< $(dotnet codefilesanity)
|
done <<< $(dotnet codefilesanity)
|
||||||
exit $exit_code
|
exit $exit_code
|
||||||
|
|
||||||
# Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded.
|
|
||||||
# - name: .NET Format (Dry Run)
|
|
||||||
# run: dotnet format --dry-run --check
|
|
||||||
|
|
||||||
- name: InspectCode
|
- name: InspectCode
|
||||||
run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN
|
run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN
|
||||||
|
|
||||||
@ -147,4 +146,4 @@ jobs:
|
|||||||
# cannot accept .sln(f) files as arguments.
|
# cannot accept .sln(f) files as arguments.
|
||||||
# Build just the main game for now.
|
# Build just the main game for now.
|
||||||
- name: Build
|
- name: Build
|
||||||
run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug
|
run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug
|
||||||
|
55
.globalconfig
Normal file
55
.globalconfig
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
is_global = true
|
||||||
|
|
||||||
|
# .NET Code Style
|
||||||
|
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
|
||||||
|
|
||||||
|
# IDE0001: Simplify names
|
||||||
|
dotnet_diagnostic.IDE0001.severity = warning
|
||||||
|
|
||||||
|
# IDE0002: Simplify member access
|
||||||
|
dotnet_diagnostic.IDE0002.severity = warning
|
||||||
|
|
||||||
|
# IDE0003: Remove qualification
|
||||||
|
dotnet_diagnostic.IDE0003.severity = warning
|
||||||
|
|
||||||
|
# IDE0004: Remove unnecessary cast
|
||||||
|
dotnet_diagnostic.IDE0004.severity = warning
|
||||||
|
|
||||||
|
# IDE0005: Remove unnecessary imports
|
||||||
|
dotnet_diagnostic.IDE0005.severity = warning
|
||||||
|
|
||||||
|
# IDE0034: Simplify default literal
|
||||||
|
dotnet_diagnostic.IDE0034.severity = warning
|
||||||
|
|
||||||
|
# IDE0036: Sort modifiers
|
||||||
|
dotnet_diagnostic.IDE0036.severity = warning
|
||||||
|
|
||||||
|
# IDE0040: Add accessibility modifier
|
||||||
|
dotnet_diagnostic.IDE0040.severity = warning
|
||||||
|
|
||||||
|
# IDE0049: Use keyword for type name
|
||||||
|
dotnet_diagnostic.IDE0040.severity = warning
|
||||||
|
|
||||||
|
# IDE0055: Fix formatting
|
||||||
|
dotnet_diagnostic.IDE0055.severity = warning
|
||||||
|
|
||||||
|
# IDE0051: Private method is unused
|
||||||
|
dotnet_diagnostic.IDE0051.severity = silent
|
||||||
|
|
||||||
|
# IDE0052: Private member is unused
|
||||||
|
dotnet_diagnostic.IDE0052.severity = silent
|
||||||
|
|
||||||
|
# IDE0073: File header
|
||||||
|
dotnet_diagnostic.IDE0073.severity = warning
|
||||||
|
|
||||||
|
# IDE0130: Namespace mismatch with folder
|
||||||
|
dotnet_diagnostic.IDE0130.severity = warning
|
||||||
|
|
||||||
|
# IDE1006: Naming style
|
||||||
|
dotnet_diagnostic.IDE1006.severity = warning
|
||||||
|
|
||||||
|
#Disable operator overloads requiring alternate named methods
|
||||||
|
dotnet_diagnostic.CA2225.severity = none
|
||||||
|
|
||||||
|
# Banned APIs
|
||||||
|
dotnet_diagnostic.RS0030.severity = error
|
@ -1,4 +1,4 @@
|
|||||||
<!-- Contains required properties for osu!framework projects. -->
|
<!-- Contains required properties for osu!framework projects. -->
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup Label="C#">
|
<PropertyGroup Label="C#">
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>8.0</LangVersion>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>8.0</LangVersion>
|
||||||
<OutputPath>bin\$(Configuration)</OutputPath>
|
<OutputPath>bin\$(Configuration)</OutputPath>
|
||||||
|
@ -29,13 +29,14 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
|
|
||||||
protected CatchSelectionBlueprintTestScene()
|
protected CatchSelectionBlueprintTestScene()
|
||||||
{
|
{
|
||||||
EditorBeatmap = new EditorBeatmap(new CatchBeatmap
|
var catchBeatmap = new CatchBeatmap
|
||||||
{
|
{
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
Ruleset = new CatchRuleset().RulesetInfo,
|
Ruleset = new CatchRuleset().RulesetInfo,
|
||||||
}
|
}
|
||||||
}) { Difficulty = { CircleSize = 0 } };
|
};
|
||||||
|
EditorBeatmap = new EditorBeatmap(catchBeatmap) { Difficulty = { CircleSize = 0 } };
|
||||||
EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint
|
EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint
|
||||||
{
|
{
|
||||||
BeatLength = 100
|
BeatLength = 100
|
||||||
|
@ -4,15 +4,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
using osu.Game.Rulesets.Osu.Edit;
|
using osu.Game.Rulesets.Osu.Edit;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -25,19 +23,29 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene
|
public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
private const double beat_length = 100;
|
private const float beat_length = 100;
|
||||||
|
|
||||||
private static readonly Vector2 grid_position = new Vector2(512, 384);
|
private static readonly Vector2 grid_position = new Vector2(512, 384);
|
||||||
|
|
||||||
[Cached(typeof(EditorBeatmap))]
|
[Cached(typeof(EditorBeatmap))]
|
||||||
|
[Cached(typeof(IBeatSnapProvider))]
|
||||||
private readonly EditorBeatmap editorBeatmap;
|
private readonly EditorBeatmap editorBeatmap;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly EditorClock editorClock;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||||
|
|
||||||
[Cached(typeof(IDistanceSnapProvider))]
|
[Cached(typeof(IDistanceSnapProvider))]
|
||||||
private readonly SnapProvider snapProvider = new SnapProvider();
|
private readonly OsuHitObjectComposer snapProvider = new OsuHitObjectComposer(new OsuRuleset())
|
||||||
|
{
|
||||||
|
// Just used for the snap implementation, so let's hide from vision.
|
||||||
|
AlwaysPresent = true,
|
||||||
|
Alpha = 0,
|
||||||
|
};
|
||||||
|
|
||||||
private TestOsuDistanceSnapGrid grid;
|
private OsuDistanceSnapGrid grid;
|
||||||
|
|
||||||
public TestSceneOsuDistanceSnapGrid()
|
public TestSceneOsuDistanceSnapGrid()
|
||||||
{
|
{
|
||||||
@ -48,14 +56,25 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
Ruleset = new OsuRuleset().RulesetInfo
|
Ruleset = new OsuRuleset().RulesetInfo
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
editorClock = new EditorClock(editorBeatmap);
|
||||||
|
|
||||||
|
base.Content.Children = new Drawable[]
|
||||||
|
{
|
||||||
|
snapProvider,
|
||||||
|
Content
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup() => Schedule(() =>
|
public void Setup() => Schedule(() =>
|
||||||
{
|
{
|
||||||
editorBeatmap.Difficulty.SliderMultiplier = 1;
|
editorBeatmap.Difficulty.SliderMultiplier = 1;
|
||||||
editorBeatmap.ControlPointInfo.Clear();
|
editorBeatmap.ControlPointInfo.Clear();
|
||||||
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length });
|
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length });
|
||||||
|
snapProvider.DistanceSpacingMultiplier.Value = 1;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -64,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.SlateGray
|
Colour = Color4.SlateGray
|
||||||
},
|
},
|
||||||
grid = new TestOsuDistanceSnapGrid(new HitCircle { Position = grid_position }),
|
grid = new OsuDistanceSnapGrid(new HitCircle { Position = grid_position }),
|
||||||
new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }
|
new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -82,25 +101,45 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddStep($"set beat divisor = {divisor}", () => beatDivisor.Value = divisor);
|
AddStep($"set beat divisor = {divisor}", () => beatDivisor.Value = divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase(1.0f)]
|
||||||
|
[TestCase(2.0f)]
|
||||||
|
[TestCase(0.5f)]
|
||||||
|
public void TestDistanceSpacing(float multiplier)
|
||||||
|
{
|
||||||
|
AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCursorInCentre()
|
public void TestCursorInCentre()
|
||||||
{
|
{
|
||||||
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position)));
|
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position)));
|
||||||
assertSnappedDistance((float)beat_length);
|
assertSnappedDistance(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCursorBeforeMovementPoint()
|
public void TestCursorBeforeMovementPoint()
|
||||||
{
|
{
|
||||||
AddStep("move mouse to just before movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 1.49f)));
|
AddStep("move mouse to just before movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 1.45f)));
|
||||||
assertSnappedDistance((float)beat_length);
|
assertSnappedDistance(beat_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCursorAfterMovementPoint()
|
public void TestCursorAfterMovementPoint()
|
||||||
{
|
{
|
||||||
AddStep("move mouse to just after movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 1.51f)));
|
AddStep("move mouse to just after movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 1.55f)));
|
||||||
assertSnappedDistance((float)beat_length * 2);
|
assertSnappedDistance(beat_length * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(0.5f, beat_length * 2)]
|
||||||
|
[TestCase(1, beat_length * 2)]
|
||||||
|
[TestCase(1.5f, beat_length * 1.5f)]
|
||||||
|
[TestCase(2f, beat_length * 2)]
|
||||||
|
public void TestDistanceSpacingAdjust(float multiplier, float expectedDistance)
|
||||||
|
{
|
||||||
|
AddStep($"Set distance spacing to {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier);
|
||||||
|
AddStep("move mouse to point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 2)));
|
||||||
|
|
||||||
|
assertSnappedDistance(expectedDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -115,13 +154,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.SlateGray
|
Colour = Color4.SlateGray
|
||||||
},
|
},
|
||||||
grid = new TestOsuDistanceSnapGrid(new HitCircle { Position = grid_position }, new HitCircle { StartTime = 200 }),
|
grid = new OsuDistanceSnapGrid(new HitCircle { Position = grid_position }, new HitCircle { StartTime = 200 }),
|
||||||
new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }
|
new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("move mouse outside grid", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 3f)));
|
AddStep("move mouse outside grid", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 3f)));
|
||||||
assertSnappedDistance((float)beat_length * 2);
|
assertSnappedDistance(beat_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertSnappedDistance(float expectedDistance) => AddAssert($"snap distance = {expectedDistance}", () =>
|
private void assertSnappedDistance(float expectedDistance) => AddAssert($"snap distance = {expectedDistance}", () =>
|
||||||
@ -137,6 +176,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
private readonly Drawable cursor;
|
private readonly Drawable cursor;
|
||||||
|
|
||||||
|
private InputManager inputManager;
|
||||||
|
|
||||||
|
public override bool HandlePositionalInput => true;
|
||||||
|
|
||||||
public SnappingCursorContainer()
|
public SnappingCursorContainer()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
@ -153,51 +196,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
updatePosition(GetContainingInputManager().CurrentState.Mouse.Position);
|
inputManager = GetContainingInputManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.OnMouseMove(e);
|
base.Update();
|
||||||
|
cursor.Position = GetSnapPosition.Invoke(inputManager.CurrentState.Mouse.Position);
|
||||||
updatePosition(e.ScreenSpaceMousePosition);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePosition(Vector2 screenSpacePosition)
|
|
||||||
{
|
|
||||||
cursor.Position = GetSnapPosition.Invoke(screenSpacePosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestOsuDistanceSnapGrid : OsuDistanceSnapGrid
|
|
||||||
{
|
|
||||||
public new float DistanceSpacing => base.DistanceSpacing;
|
|
||||||
|
|
||||||
public TestOsuDistanceSnapGrid(OsuHitObject hitObject, OsuHitObject nextHitObject = null)
|
|
||||||
: base(hitObject, nextHitObject)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SnapProvider : IDistanceSnapProvider
|
|
||||||
{
|
|
||||||
public SnapResult FindSnappedPosition(Vector2 screenSpacePosition) =>
|
|
||||||
new SnapResult(screenSpacePosition, null);
|
|
||||||
|
|
||||||
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
|
||||||
|
|
||||||
public IBindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1);
|
|
||||||
|
|
||||||
public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length;
|
|
||||||
|
|
||||||
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;
|
|
||||||
|
|
||||||
public double DistanceToDuration(HitObject referenceObject, float distance) => distance;
|
|
||||||
|
|
||||||
public double FindSnappedDuration(HitObject referenceObject, float distance) => 0;
|
|
||||||
|
|
||||||
public float FindSnappedDistance(HitObject referenceObject, float distance) => 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
public class OsuDistanceSnapGrid : CircularDistanceSnapGrid
|
public class OsuDistanceSnapGrid : CircularDistanceSnapGrid
|
||||||
{
|
{
|
||||||
public OsuDistanceSnapGrid(OsuHitObject hitObject, [CanBeNull] OsuHitObject nextHitObject = null)
|
public OsuDistanceSnapGrid(OsuHitObject hitObject, [CanBeNull] OsuHitObject nextHitObject = null)
|
||||||
: base(hitObject, hitObject.StackedEndPosition, hitObject.GetEndTime(), nextHitObject?.StartTime)
|
: base(hitObject, hitObject.StackedEndPosition, hitObject.GetEndTime(), nextHitObject?.StartTime - 1)
|
||||||
{
|
{
|
||||||
Masking = true;
|
Masking = true;
|
||||||
}
|
}
|
||||||
|
@ -142,13 +142,12 @@ namespace osu.Game.Tests.Visual.Collections
|
|||||||
AddStep("add dropdown", () =>
|
AddStep("add dropdown", () =>
|
||||||
{
|
{
|
||||||
Add(new CollectionFilterDropdown
|
Add(new CollectionFilterDropdown
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Width = 0.4f,
|
Width = 0.4f,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
AddStep("add two collections with same name", () => manager.Collections.AddRange(new[]
|
AddStep("add two collections with same name", () => manager.Collections.AddRange(new[]
|
||||||
{
|
{
|
||||||
|
@ -21,8 +21,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
public class TestSceneDistanceSnapGrid : EditorClockTestScene
|
public class TestSceneDistanceSnapGrid : EditorClockTestScene
|
||||||
{
|
{
|
||||||
private const double beat_length = 100;
|
private const double beat_length = 100;
|
||||||
|
private const int beat_snap_distance = 10;
|
||||||
|
|
||||||
private static readonly Vector2 grid_position = new Vector2(512, 384);
|
private static readonly Vector2 grid_position = new Vector2(512, 384);
|
||||||
|
|
||||||
|
private TestDistanceSnapGrid grid;
|
||||||
|
|
||||||
[Cached(typeof(EditorBeatmap))]
|
[Cached(typeof(EditorBeatmap))]
|
||||||
private readonly EditorBeatmap editorBeatmap;
|
private readonly EditorBeatmap editorBeatmap;
|
||||||
|
|
||||||
@ -39,6 +43,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length });
|
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length });
|
||||||
|
editorBeatmap.Difficulty.SliderMultiplier = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
@ -51,7 +56,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.SlateGray
|
Colour = Color4.SlateGray
|
||||||
},
|
},
|
||||||
new TestDistanceSnapGrid()
|
grid = new TestDistanceSnapGrid()
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -68,9 +73,22 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep($"set beat divisor = {divisor}", () => BeatDivisor.Value = divisor);
|
AddStep($"set beat divisor = {divisor}", () => BeatDivisor.Value = divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[TestCase(1.0)]
|
||||||
public void TestLimitedDistance()
|
[TestCase(2.0)]
|
||||||
|
[TestCase(0.5)]
|
||||||
|
public void TestDistanceSpacing(double multiplier)
|
||||||
{
|
{
|
||||||
|
AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier);
|
||||||
|
AddAssert("distance spacing matches multiplier", () => grid.DistanceBetweenTicks == beat_snap_distance * multiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(1.0)]
|
||||||
|
[TestCase(2.0)]
|
||||||
|
[TestCase(0.5)]
|
||||||
|
public void TestLimitedDistance(double multiplier)
|
||||||
|
{
|
||||||
|
const int end_time = 100;
|
||||||
|
|
||||||
AddStep("create limited grid", () =>
|
AddStep("create limited grid", () =>
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -80,14 +98,19 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.SlateGray
|
Colour = Color4.SlateGray
|
||||||
},
|
},
|
||||||
new TestDistanceSnapGrid(100)
|
grid = new TestDistanceSnapGrid(end_time)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier);
|
||||||
|
AddStep("check correct interval count", () => Assert.That((end_time / grid.DistanceBetweenTicks) * multiplier, Is.EqualTo(grid.MaxIntervals)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestDistanceSnapGrid : DistanceSnapGrid
|
private class TestDistanceSnapGrid : DistanceSnapGrid
|
||||||
{
|
{
|
||||||
public new float DistanceSpacing => base.DistanceSpacing;
|
public new float DistanceBetweenTicks => base.DistanceBetweenTicks;
|
||||||
|
|
||||||
|
public new int MaxIntervals => base.MaxIntervals;
|
||||||
|
|
||||||
public TestDistanceSnapGrid(double? endTime = null)
|
public TestDistanceSnapGrid(double? endTime = null)
|
||||||
: base(new HitObject(), grid_position, 0, endTime)
|
: base(new HitObject(), grid_position, 0, endTime)
|
||||||
@ -105,7 +128,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
int indexFromPlacement = 0;
|
int indexFromPlacement = 0;
|
||||||
|
|
||||||
for (float s = StartPosition.X + DistanceSpacing; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
|
for (float s = StartPosition.X + DistanceBetweenTicks; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceBetweenTicks, indexFromPlacement++)
|
||||||
{
|
{
|
||||||
AddInternal(new Circle
|
AddInternal(new Circle
|
||||||
{
|
{
|
||||||
@ -118,7 +141,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
indexFromPlacement = 0;
|
indexFromPlacement = 0;
|
||||||
|
|
||||||
for (float s = StartPosition.X - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
|
for (float s = StartPosition.X - DistanceBetweenTicks; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceBetweenTicks, indexFromPlacement++)
|
||||||
{
|
{
|
||||||
AddInternal(new Circle
|
AddInternal(new Circle
|
||||||
{
|
{
|
||||||
@ -131,7 +154,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
indexFromPlacement = 0;
|
indexFromPlacement = 0;
|
||||||
|
|
||||||
for (float s = StartPosition.Y + DistanceSpacing; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
|
for (float s = StartPosition.Y + DistanceBetweenTicks; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceBetweenTicks, indexFromPlacement++)
|
||||||
{
|
{
|
||||||
AddInternal(new Circle
|
AddInternal(new Circle
|
||||||
{
|
{
|
||||||
@ -144,7 +167,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
indexFromPlacement = 0;
|
indexFromPlacement = 0;
|
||||||
|
|
||||||
for (float s = StartPosition.Y - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
|
for (float s = StartPosition.Y - DistanceBetweenTicks; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceBetweenTicks, indexFromPlacement++)
|
||||||
{
|
{
|
||||||
AddInternal(new Circle
|
AddInternal(new Circle
|
||||||
{
|
{
|
||||||
@ -167,9 +190,11 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
||||||
|
|
||||||
public IBindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1);
|
public Bindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1);
|
||||||
|
|
||||||
public float GetBeatSnapDistanceAt(HitObject referenceObject) => 10;
|
IBindable<double> IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier;
|
||||||
|
|
||||||
|
public float GetBeatSnapDistanceAt(HitObject referenceObject) => beat_snap_distance;
|
||||||
|
|
||||||
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;
|
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;
|
||||||
|
|
||||||
|
@ -1,29 +1,37 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.OnlinePlay;
|
using osu.Game.Screens.OnlinePlay;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Multiplayer
|
namespace osu.Game.Tests.Visual.Multiplayer
|
||||||
{
|
{
|
||||||
public class TestSceneFreeModSelectScreen : MultiplayerTestScene
|
public class TestSceneFreeModSelectScreen : MultiplayerTestScene
|
||||||
{
|
{
|
||||||
|
private FreeModSelectScreen freeModSelectScreen;
|
||||||
|
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> availableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuGameBase osuGameBase)
|
||||||
|
{
|
||||||
|
availableMods.BindTo(osuGameBase.AvailableMods);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestFreeModSelect()
|
public void TestFreeModSelect()
|
||||||
{
|
{
|
||||||
FreeModSelectScreen freeModSelectScreen = null;
|
createFreeModSelect();
|
||||||
|
|
||||||
AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen
|
|
||||||
{
|
|
||||||
State = { Value = Visibility.Visible }
|
|
||||||
});
|
|
||||||
AddUntilStep("all column content loaded",
|
|
||||||
() => freeModSelectScreen.ChildrenOfType<ModColumn>().Any()
|
|
||||||
&& freeModSelectScreen.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));
|
|
||||||
|
|
||||||
AddUntilStep("all visible mods are playable",
|
AddUntilStep("all visible mods are playable",
|
||||||
() => this.ChildrenOfType<ModPanel>()
|
() => this.ChildrenOfType<ModPanel>()
|
||||||
@ -36,5 +44,62 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
freeModSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden;
|
freeModSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCustomisationNotAvailable()
|
||||||
|
{
|
||||||
|
createFreeModSelect();
|
||||||
|
|
||||||
|
AddStep("select difficulty adjust", () => freeModSelectScreen.SelectedMods.Value = new[] { new OsuModDifficultyAdjust() });
|
||||||
|
AddWaitStep("wait some", 3);
|
||||||
|
AddAssert("customisation area not expanded", () => this.ChildrenOfType<ModSettingsArea>().Single().Height == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSelectDeselectAll()
|
||||||
|
{
|
||||||
|
createFreeModSelect();
|
||||||
|
|
||||||
|
AddStep("click select all button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().ElementAt(1));
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("all mods selected", assertAllAvailableModsSelected);
|
||||||
|
|
||||||
|
AddStep("click deselect all button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().Last());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("all mods deselected", () => !freeModSelectScreen.SelectedMods.Value.Any());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createFreeModSelect()
|
||||||
|
{
|
||||||
|
AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen
|
||||||
|
{
|
||||||
|
State = { Value = Visibility.Visible }
|
||||||
|
});
|
||||||
|
AddUntilStep("all column content loaded",
|
||||||
|
() => freeModSelectScreen.ChildrenOfType<ModColumn>().Any()
|
||||||
|
&& freeModSelectScreen.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool assertAllAvailableModsSelected()
|
||||||
|
{
|
||||||
|
var allAvailableMods = availableMods.Value
|
||||||
|
.SelectMany(pair => pair.Value)
|
||||||
|
.Where(mod => mod.UserPlayable && mod.HasImplementation)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var availableMod in allAvailableMods)
|
||||||
|
{
|
||||||
|
if (freeModSelectScreen.SelectedMods.Value.All(selectedMod => selectedMod.GetType() != availableMod.GetType()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -17,6 +18,7 @@ using osu.Framework.Screens;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
@ -56,6 +58,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
|
private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
|
||||||
private TestMultiplayerRoomManager roomManager => multiplayerComponents.RoomManager;
|
private TestMultiplayerRoomManager roomManager => multiplayerComponents.RoomManager;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
@ -668,6 +673,43 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddUntilStep("wait for results", () => multiplayerComponents.CurrentScreen is ResultsScreen);
|
AddUntilStep("wait for results", () => multiplayerComponents.CurrentScreen is ResultsScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestGameplayExitFlow()
|
||||||
|
{
|
||||||
|
Bindable<double> holdDelay = null;
|
||||||
|
|
||||||
|
AddStep("Set hold delay to zero", () =>
|
||||||
|
{
|
||||||
|
holdDelay = config.GetBindable<double>(OsuSetting.UIHoldActivationDelay);
|
||||||
|
holdDelay.Value = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
createRoom(() => new Room
|
||||||
|
{
|
||||||
|
Name = { Value = "Test Room" },
|
||||||
|
Playlist =
|
||||||
|
{
|
||||||
|
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||||
|
{
|
||||||
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
enterGameplay();
|
||||||
|
|
||||||
|
AddUntilStep("wait for playing", () => this.ChildrenOfType<Player>().FirstOrDefault()?.LocalUserPlaying.Value == true);
|
||||||
|
|
||||||
|
AddStep("attempt exit without hold", () => InputManager.Key(Key.Escape));
|
||||||
|
AddAssert("still in gameplay", () => multiplayerComponents.CurrentScreen is Player);
|
||||||
|
|
||||||
|
AddStep("attempt exit with hold", () => InputManager.PressKey(Key.Escape));
|
||||||
|
AddUntilStep("wait for lounge", () => multiplayerComponents.CurrentScreen is Screens.OnlinePlay.Multiplayer.Multiplayer);
|
||||||
|
|
||||||
|
AddStep("stop holding", () => InputManager.ReleaseKey(Key.Escape));
|
||||||
|
AddStep("set hold delay to default", () => holdDelay.SetDefault());
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestGameplayDoesntStartWithNonLoadedUser()
|
public void TestGameplayDoesntStartWithNonLoadedUser()
|
||||||
{
|
{
|
||||||
|
@ -133,22 +133,22 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestChatHeight()
|
public void TestChatHeight()
|
||||||
{
|
{
|
||||||
Bindable<float> configChatHeight = null;
|
BindableFloat configChatHeight = new BindableFloat();
|
||||||
|
config.BindWith(OsuSetting.ChatDisplayHeight, configChatHeight);
|
||||||
float newHeight = 0;
|
float newHeight = 0;
|
||||||
|
|
||||||
AddStep("Bind config chat height", () => configChatHeight = config.GetBindable<float>(OsuSetting.ChatDisplayHeight).GetBoundCopy());
|
AddStep("Reset config chat height", () => configChatHeight.SetDefault());
|
||||||
AddStep("Set config chat height", () => configChatHeight.Value = 0.4f);
|
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
AddStep("Show overlay", () => chatOverlay.Show());
|
||||||
AddAssert("Overlay uses config height", () => chatOverlay.Height == 0.4f);
|
AddAssert("Overlay uses config height", () => chatOverlay.Height == configChatHeight.Default);
|
||||||
AddStep("Drag overlay to new height", () =>
|
AddStep("Click top bar", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(chatOverlayTopBar);
|
InputManager.MoveMouseTo(chatOverlayTopBar);
|
||||||
InputManager.PressButton(MouseButton.Left);
|
InputManager.PressButton(MouseButton.Left);
|
||||||
InputManager.MoveMouseTo(chatOverlayTopBar, new Vector2(0, -300));
|
|
||||||
InputManager.ReleaseButton(MouseButton.Left);
|
|
||||||
});
|
});
|
||||||
|
AddStep("Drag overlay to new height", () => InputManager.MoveMouseTo(chatOverlayTopBar, new Vector2(0, -300)));
|
||||||
|
AddStep("Stop dragging", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
AddStep("Store new height", () => newHeight = chatOverlay.Height);
|
AddStep("Store new height", () => newHeight = chatOverlay.Height);
|
||||||
AddAssert("Config height changed", () => configChatHeight.Value != 0.4f && configChatHeight.Value == newHeight);
|
AddAssert("Config height changed", () => !configChatHeight.IsDefault && configChatHeight.Value == newHeight);
|
||||||
AddStep("Hide overlay", () => chatOverlay.Hide());
|
AddStep("Hide overlay", () => chatOverlay.Hide());
|
||||||
AddStep("Show overlay", () => chatOverlay.Show());
|
AddStep("Show overlay", () => chatOverlay.Show());
|
||||||
AddAssert("Overlay uses new height", () => chatOverlay.Height == newHeight);
|
AddAssert("Overlay uses new height", () => chatOverlay.Height == newHeight);
|
||||||
|
@ -99,15 +99,15 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests expanding a container will expand underlying groups if contracted.
|
/// Tests expanding a container will not expand underlying groups if they were manually contracted by the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestExpandingContainerExpandsContractedGroup()
|
public void TestExpandingContainerDoesNotExpandContractedGroup()
|
||||||
{
|
{
|
||||||
AddStep("contract group", () => toolboxGroup.Expanded.Value = false);
|
AddStep("contract group", () => toolboxGroup.Expanded.Value = false);
|
||||||
|
|
||||||
AddStep("expand container", () => container.Expanded.Value = true);
|
AddStep("expand container", () => container.Expanded.Value = true);
|
||||||
AddAssert("group expanded", () => toolboxGroup.Expanded.Value);
|
AddAssert("group not expanded", () => !toolboxGroup.Expanded.Value);
|
||||||
AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
|
AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
|
||||||
|
|
||||||
AddStep("contract container", () => container.Expanded.Value = false);
|
AddStep("contract container", () => container.Expanded.Value = false);
|
||||||
|
@ -18,6 +18,7 @@ using osu.Game.Rulesets;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.UserInterface
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
@ -164,7 +165,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("select mod requiring configuration", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust() });
|
AddStep("select mod requiring configuration", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust() });
|
||||||
assertCustomisationToggleState(disabled: false, active: true);
|
assertCustomisationToggleState(disabled: false, active: true);
|
||||||
|
|
||||||
AddStep("dismiss mod customisation via mouse", () =>
|
AddStep("dismiss mod customisation via toggle", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType<ShearedToggleButton>().Single());
|
InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType<ShearedToggleButton>().Single());
|
||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
@ -191,6 +192,29 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
assertCustomisationToggleState(disabled: true, active: false); // config was dismissed without explicit user action.
|
assertCustomisationToggleState(disabled: true, active: false); // config was dismissed without explicit user action.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDismissCustomisationViaDimmedArea()
|
||||||
|
{
|
||||||
|
createScreen();
|
||||||
|
assertCustomisationToggleState(disabled: true, active: false);
|
||||||
|
|
||||||
|
AddStep("select mod requiring configuration", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust() });
|
||||||
|
assertCustomisationToggleState(disabled: false, active: true);
|
||||||
|
|
||||||
|
AddStep("move mouse to settings area", () => InputManager.MoveMouseTo(this.ChildrenOfType<ModSettingsArea>().Single()));
|
||||||
|
AddStep("move mouse to dimmed area", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(new Vector2(
|
||||||
|
modSelectScreen.ScreenSpaceDrawQuad.TopLeft.X,
|
||||||
|
(modSelectScreen.ScreenSpaceDrawQuad.TopLeft.Y + modSelectScreen.ScreenSpaceDrawQuad.BottomLeft.Y) / 2));
|
||||||
|
});
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
assertCustomisationToggleState(disabled: false, active: false);
|
||||||
|
|
||||||
|
AddStep("move mouse to first mod panel", () => InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType<ModPanel>().First()));
|
||||||
|
AddAssert("first mod panel is hovered", () => modSelectScreen.ChildrenOfType<ModPanel>().First().IsHovered);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ensure that two mod overlays are not cross polluting via central settings instances.
|
/// Ensure that two mod overlays are not cross polluting via central settings instances.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -391,6 +415,42 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddAssert("unimplemented mod panel is filtered", () => getPanelForMod(typeof(TestUnimplementedMod)).Filtered.Value);
|
AddAssert("unimplemented mod panel is filtered", () => getPanelForMod(typeof(TestUnimplementedMod)).Filtered.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeselectAllViaButton()
|
||||||
|
{
|
||||||
|
createScreen();
|
||||||
|
changeRuleset(0);
|
||||||
|
|
||||||
|
AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() });
|
||||||
|
AddAssert("DT + HD selected", () => modSelectScreen.ChildrenOfType<ModPanel>().Count(panel => panel.Active.Value) == 2);
|
||||||
|
|
||||||
|
AddStep("click deselect all button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().Last());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCloseViaBackButton()
|
||||||
|
{
|
||||||
|
createScreen();
|
||||||
|
changeRuleset(0);
|
||||||
|
|
||||||
|
AddStep("select difficulty adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() });
|
||||||
|
assertCustomisationToggleState(disabled: false, active: true);
|
||||||
|
AddAssert("back button disabled", () => !this.ChildrenOfType<ShearedButton>().First().Enabled.Value);
|
||||||
|
|
||||||
|
AddStep("dismiss customisation area", () => InputManager.Key(Key.Escape));
|
||||||
|
AddStep("click back button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().First());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("mod select hidden", () => modSelectScreen.State.Value == Visibility.Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForColumnLoad() => AddUntilStep("all column content loaded",
|
private void waitForColumnLoad() => AddUntilStep("all column content loaded",
|
||||||
() => modSelectScreen.ChildrenOfType<ModColumn>().Any() && modSelectScreen.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));
|
() => modSelectScreen.ChildrenOfType<ModColumn>().Any() && modSelectScreen.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));
|
||||||
|
|
||||||
|
@ -10,12 +10,32 @@ using osu.Game.Configuration;
|
|||||||
|
|
||||||
namespace osu.Game.Graphics.Containers
|
namespace osu.Game.Graphics.Containers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A container which adds a common "hold-to-perform" pattern to a container.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This container does not handle triggering the hold/abort operations.
|
||||||
|
/// To use this class, please call <see cref="BeginConfirm"/> and <see cref="AbortConfirm"/> when necessary.
|
||||||
|
///
|
||||||
|
/// The <see cref="Progress"/> is exposed as a transforming bindable which smoothly tracks the progress of a hold operation.
|
||||||
|
/// It can be used for animating and displaying progress directly.
|
||||||
|
/// </remarks>
|
||||||
public abstract class HoldToConfirmContainer : Container
|
public abstract class HoldToConfirmContainer : Container
|
||||||
{
|
{
|
||||||
public Action Action;
|
public const double DANGEROUS_HOLD_ACTIVATION_DELAY = 500;
|
||||||
|
|
||||||
private const int fadeout_delay = 200;
|
private const int fadeout_delay = 200;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the associated action is considered dangerous, warranting a longer hold.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDangerousAction { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The action to perform when a hold successfully completes.
|
||||||
|
/// </summary>
|
||||||
|
public Action Action;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether currently in a fired state (and the confirm <see cref="Action"/> has been sent).
|
/// Whether currently in a fired state (and the confirm <see cref="Action"/> has been sent).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -23,46 +43,61 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
private bool confirming;
|
private bool confirming;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current activation delay for this control.
|
||||||
|
/// </summary>
|
||||||
|
public IBindable<double> HoldActivationDelay => holdActivationDelay;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The progress of any ongoing hold operation. 0 means no hold has started; 1 means a hold has been completed.
|
||||||
|
/// </summary>
|
||||||
|
public IBindable<double> Progress => progress;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the overlay should be allowed to return from a fired state.
|
/// Whether the overlay should be allowed to return from a fired state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual bool AllowMultipleFires => false;
|
protected virtual bool AllowMultipleFires => false;
|
||||||
|
|
||||||
/// <summary>
|
private readonly Bindable<double> progress = new BindableDouble();
|
||||||
/// Specify a custom activation delay, overriding the game-wide user setting.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This should be used in special cases where we want to be extra sure the user knows what they are doing. An example is when changes would be lost.
|
|
||||||
/// </remarks>
|
|
||||||
protected virtual double? HoldActivationDelay => null;
|
|
||||||
|
|
||||||
public Bindable<double> Progress = new BindableDouble();
|
private readonly Bindable<double> holdActivationDelay = new Bindable<double>();
|
||||||
|
|
||||||
private Bindable<double> holdActivationDelay;
|
[Resolved]
|
||||||
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
protected HoldToConfirmContainer(bool isDangerousAction = false)
|
||||||
private void load(OsuConfigManager config)
|
|
||||||
{
|
{
|
||||||
holdActivationDelay = HoldActivationDelay != null
|
IsDangerousAction = isDangerousAction;
|
||||||
? new Bindable<double>(HoldActivationDelay.Value)
|
|
||||||
: config.GetBindable<double>(OsuSetting.UIHoldActivationDelay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
if (IsDangerousAction)
|
||||||
|
holdActivationDelay.Value = DANGEROUS_HOLD_ACTIVATION_DELAY;
|
||||||
|
else
|
||||||
|
config.BindWith(OsuSetting.UIHoldActivationDelay, holdActivationDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begin a new confirmation. Should be called when the container is interacted with (ie. the user presses a key).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Calling this method when already in the process of confirming has no effect.
|
||||||
|
/// </remarks>
|
||||||
protected void BeginConfirm()
|
protected void BeginConfirm()
|
||||||
{
|
{
|
||||||
if (confirming || (!AllowMultipleFires && Fired)) return;
|
if (confirming || (!AllowMultipleFires && Fired)) return;
|
||||||
|
|
||||||
confirming = true;
|
confirming = true;
|
||||||
|
|
||||||
this.TransformBindableTo(Progress, 1, holdActivationDelay.Value * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm());
|
this.TransformBindableTo(progress, 1, holdActivationDelay.Value * (1 - progress.Value), Easing.Out).OnComplete(_ => Confirm());
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Confirm()
|
|
||||||
{
|
|
||||||
Action?.Invoke();
|
|
||||||
Fired = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Abort any ongoing confirmation. Should be called when the container's interaction is no longer valid (ie. the user releases a key).
|
||||||
|
/// </summary>
|
||||||
protected void AbortConfirm()
|
protected void AbortConfirm()
|
||||||
{
|
{
|
||||||
if (!AllowMultipleFires && Fired) return;
|
if (!AllowMultipleFires && Fired) return;
|
||||||
@ -71,9 +106,19 @@ namespace osu.Game.Graphics.Containers
|
|||||||
Fired = false;
|
Fired = false;
|
||||||
|
|
||||||
this
|
this
|
||||||
.TransformBindableTo(Progress, Progress.Value)
|
.TransformBindableTo(progress, progress.Value)
|
||||||
.Delay(200)
|
.Delay(200)
|
||||||
.TransformBindableTo(Progress, 0, fadeout_delay, Easing.InSine);
|
.TransformBindableTo(progress, 0, fadeout_delay, Easing.InSine);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A method which is invoked when the confirmation sequence completes successfully.
|
||||||
|
/// By default, will fire the associated <see cref="Action"/>.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void Confirm()
|
||||||
|
{
|
||||||
|
Action?.Invoke();
|
||||||
|
Fired = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,16 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString Importing => new TranslatableString(getKey(@"importing"), @"Importing...");
|
public static LocalisableString Importing => new TranslatableString(getKey(@"importing"), @"Importing...");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Deselect All"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString DeselectAll => new TranslatableString(getKey(@"deselect_all"), @"Deselect All");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Select All"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString SelectAll => new TranslatableString(getKey(@"select_all"), @"Select All");
|
||||||
|
|
||||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs
Normal file
19
osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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 osu.Framework.Localisation;
|
||||||
|
|
||||||
|
namespace osu.Game.Localisation
|
||||||
|
{
|
||||||
|
public static class DifficultyMultiplierDisplayStrings
|
||||||
|
{
|
||||||
|
private const string prefix = @"osu.Game.Resources.Localisation.DifficultyMultiplierDisplay";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Difficulty Multiplier"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString DifficultyMultiplier => new TranslatableString(getKey(@"difficulty_multiplier"), @"Difficulty Multiplier");
|
||||||
|
|
||||||
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
|
}
|
||||||
|
}
|
29
osu.Game/Localisation/ModSelectScreenStrings.cs
Normal file
29
osu.Game/Localisation/ModSelectScreenStrings.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// 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 osu.Framework.Localisation;
|
||||||
|
|
||||||
|
namespace osu.Game.Localisation
|
||||||
|
{
|
||||||
|
public static class ModSelectScreenStrings
|
||||||
|
{
|
||||||
|
private const string prefix = @"osu.Game.Resources.Localisation.ModSelectScreen";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Mod Select"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ModSelectTitle => new TranslatableString(getKey(@"mod_select_title"), @"Mod Select");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ModSelectDescription => new TranslatableString(getKey(@"mod_select_description"), @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Mod Customisation"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ModCustomisation => new TranslatableString(getKey(@"mod_customisation"), @"Mod Customisation");
|
||||||
|
|
||||||
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using System;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using System;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
// 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 Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace osu.Game.Migrations
|
namespace osu.Game.Migrations
|
||||||
{
|
{
|
||||||
|
@ -458,7 +458,7 @@ namespace osu.Game.Online.API
|
|||||||
public GuestUser()
|
public GuestUser()
|
||||||
{
|
{
|
||||||
Username = @"Guest";
|
Username = @"Guest";
|
||||||
Id = 1;
|
Id = SYSTEM_USER_ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1182,6 +1182,7 @@ namespace osu.Game
|
|||||||
horizontalOffset += (Content.ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - Content.DrawWidth) * SIDE_OVERLAY_OFFSET_RATIO;
|
horizontalOffset += (Content.ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - Content.DrawWidth) * SIDE_OVERLAY_OFFSET_RATIO;
|
||||||
|
|
||||||
ScreenOffsetContainer.X = horizontalOffset;
|
ScreenOffsetContainer.X = horizontalOffset;
|
||||||
|
overlayContent.X = horizontalOffset * 1.2f;
|
||||||
|
|
||||||
MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
|
MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x");
|
maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x");
|
||||||
|
|
||||||
ppColumn.Alpha = value.BeatmapInfo.Status.GrantsPerformancePoints() ? 1 : 0;
|
ppColumn.Alpha = value.BeatmapInfo.Status.GrantsPerformancePoints() ? 1 : 0;
|
||||||
ppColumn.Text = value.PP?.ToLocalisableString(@"N0") ?? default(LocalisableString);
|
ppColumn.Text = value.PP?.ToLocalisableString(@"N0") ?? default;
|
||||||
|
|
||||||
statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(createStatisticsColumn);
|
statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(createStatisticsColumn);
|
||||||
modsColumn.Mods = value.Mods;
|
modsColumn.Mods = value.Mods;
|
||||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Overlays
|
|||||||
private ChatTextBar textBar = null!;
|
private ChatTextBar textBar = null!;
|
||||||
private Container<DrawableChannel> currentChannelContainer = null!;
|
private Container<DrawableChannel> currentChannelContainer = null!;
|
||||||
|
|
||||||
private readonly Bindable<float> chatHeight = new Bindable<float>();
|
private readonly BindableFloat chatHeight = new BindableFloat();
|
||||||
|
|
||||||
private bool isDraggingTopBar;
|
private bool isDraggingTopBar;
|
||||||
private float dragStartChatHeight;
|
private float dragStartChatHeight;
|
||||||
|
@ -42,7 +42,10 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
|
|
||||||
private class DangerousConfirmContainer : HoldToConfirmContainer
|
private class DangerousConfirmContainer : HoldToConfirmContainer
|
||||||
{
|
{
|
||||||
protected override double? HoldActivationDelay => 500;
|
public DangerousConfirmContainer()
|
||||||
|
: base(isDangerousAction: true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,7 @@ using osu.Game.Graphics.Sprites;
|
|||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osu.Game.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Mods
|
namespace osu.Game.Overlays.Mods
|
||||||
{
|
{
|
||||||
@ -99,7 +100,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Margin = new MarginPadding { Horizontal = 18 },
|
Margin = new MarginPadding { Horizontal = 18 },
|
||||||
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0),
|
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0),
|
||||||
Text = "Difficulty Multiplier",
|
Text = DifficultyMultiplierDisplayStrings.DifficultyMultiplier,
|
||||||
Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold)
|
Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -22,6 +23,7 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -49,7 +51,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
filter = value;
|
filter = value;
|
||||||
updateFilter();
|
updateState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +222,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Scale = new Vector2(0.8f),
|
Scale = new Vector2(0.8f),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
LabelText = "Enable All",
|
|
||||||
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)
|
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)
|
||||||
});
|
});
|
||||||
panelFlow.Padding = new MarginPadding
|
panelFlow.Padding = new MarginPadding
|
||||||
@ -265,6 +266,19 @@ namespace osu.Game.Overlays.Mods
|
|||||||
contentBackground.Colour = colourProvider.Background4;
|
contentBackground.Colour = colourProvider.Background4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
toggleAllCheckbox?.Current.BindValueChanged(_ => updateToggleAllText(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateToggleAllText()
|
||||||
|
{
|
||||||
|
Debug.Assert(toggleAllCheckbox != null);
|
||||||
|
toggleAllCheckbox.LabelText = toggleAllCheckbox.Current.Value ? CommonStrings.DeselectAll : CommonStrings.SelectAll;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateLocalAvailableMods()
|
private void updateLocalAvailableMods()
|
||||||
{
|
{
|
||||||
var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty<Mod>())
|
var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty<Mod>())
|
||||||
@ -275,32 +289,25 @@ namespace osu.Game.Overlays.Mods
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
localAvailableMods = newMods;
|
localAvailableMods = newMods;
|
||||||
Scheduler.AddOnce(loadPanels);
|
|
||||||
|
if (!IsLoaded)
|
||||||
|
// if we're coming from BDL, perform the first load synchronously to make sure everything is in place as early as possible.
|
||||||
|
onPanelsLoaded(createPanels());
|
||||||
|
else
|
||||||
|
asyncLoadPanels();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CancellationTokenSource? cancellationTokenSource;
|
private CancellationTokenSource? cancellationTokenSource;
|
||||||
|
|
||||||
private void loadPanels()
|
private void asyncLoadPanels()
|
||||||
{
|
{
|
||||||
cancellationTokenSource?.Cancel();
|
cancellationTokenSource?.Cancel();
|
||||||
|
|
||||||
var panels = localAvailableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)));
|
var panels = createPanels();
|
||||||
|
|
||||||
Task? loadTask;
|
Task? loadTask;
|
||||||
|
|
||||||
latestLoadTask = loadTask = LoadComponentsAsync(panels, loaded =>
|
latestLoadTask = loadTask = LoadComponentsAsync(panels, onPanelsLoaded, (cancellationTokenSource = new CancellationTokenSource()).Token);
|
||||||
{
|
|
||||||
panelFlow.ChildrenEnumerable = loaded;
|
|
||||||
|
|
||||||
updateActiveState();
|
|
||||||
updateToggleAllState();
|
|
||||||
updateFilter();
|
|
||||||
|
|
||||||
foreach (var panel in panelFlow)
|
|
||||||
{
|
|
||||||
panel.Active.BindValueChanged(_ => panelStateChanged(panel));
|
|
||||||
}
|
|
||||||
}, (cancellationTokenSource = new CancellationTokenSource()).Token);
|
|
||||||
loadTask.ContinueWith(_ =>
|
loadTask.ContinueWith(_ =>
|
||||||
{
|
{
|
||||||
if (loadTask == latestLoadTask)
|
if (loadTask == latestLoadTask)
|
||||||
@ -308,10 +315,37 @@ namespace osu.Game.Overlays.Mods
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateActiveState()
|
private IEnumerable<ModPanel> createPanels()
|
||||||
|
{
|
||||||
|
var panels = localAvailableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)));
|
||||||
|
return panels;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPanelsLoaded(IEnumerable<ModPanel> loaded)
|
||||||
|
{
|
||||||
|
panelFlow.ChildrenEnumerable = loaded;
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
|
||||||
|
foreach (var panel in panelFlow)
|
||||||
|
{
|
||||||
|
panel.Active.BindValueChanged(_ => panelStateChanged(panel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
{
|
{
|
||||||
foreach (var panel in panelFlow)
|
foreach (var panel in panelFlow)
|
||||||
|
{
|
||||||
panel.Active.Value = SelectedMods.Contains(panel.Mod);
|
panel.Active.Value = SelectedMods.Contains(panel.Mod);
|
||||||
|
panel.ApplyFilter(Filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toggleAllCheckbox != null && !SelectionAnimationRunning)
|
||||||
|
{
|
||||||
|
toggleAllCheckbox.Alpha = panelFlow.Any(panel => !panel.Filtered.Value) ? 1 : 0;
|
||||||
|
toggleAllCheckbox.Current.Value = panelFlow.Where(panel => !panel.Filtered.Value).All(panel => panel.Active.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -323,15 +357,16 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private void panelStateChanged(ModPanel panel)
|
private void panelStateChanged(ModPanel panel)
|
||||||
{
|
{
|
||||||
updateToggleAllState();
|
if (externalSelectionUpdateInProgress)
|
||||||
|
return;
|
||||||
|
|
||||||
var newSelectedMods = panel.Active.Value
|
var newSelectedMods = panel.Active.Value
|
||||||
? SelectedMods.Append(panel.Mod)
|
? SelectedMods.Append(panel.Mod)
|
||||||
: SelectedMods.Except(panel.Mod.Yield());
|
: SelectedMods.Except(panel.Mod.Yield());
|
||||||
|
|
||||||
SelectedMods = newSelectedMods.ToArray();
|
SelectedMods = newSelectedMods.ToArray();
|
||||||
if (!externalSelectionUpdateInProgress)
|
updateState();
|
||||||
SelectionChangedByUser?.Invoke();
|
SelectionChangedByUser?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -364,7 +399,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
SelectedMods = newSelection;
|
SelectedMods = newSelection;
|
||||||
updateActiveState();
|
updateState();
|
||||||
|
|
||||||
externalSelectionUpdateInProgress = false;
|
externalSelectionUpdateInProgress = false;
|
||||||
}
|
}
|
||||||
@ -378,7 +413,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private readonly Queue<Action> pendingSelectionOperations = new Queue<Action>();
|
private readonly Queue<Action> pendingSelectionOperations = new Queue<Action>();
|
||||||
|
|
||||||
protected bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0;
|
internal bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0;
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
@ -403,15 +438,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateToggleAllState()
|
|
||||||
{
|
|
||||||
if (toggleAllCheckbox != null && !SelectionAnimationRunning)
|
|
||||||
{
|
|
||||||
toggleAllCheckbox.Alpha = panelFlow.Any(panel => !panel.Filtered.Value) ? 1 : 0;
|
|
||||||
toggleAllCheckbox.Current.Value = panelFlow.Where(panel => !panel.Filtered.Value).All(panel => panel.Active.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects all mods.
|
/// Selects all mods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -507,18 +533,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Filtering support
|
|
||||||
|
|
||||||
private void updateFilter()
|
|
||||||
{
|
|
||||||
foreach (var modPanel in panelFlow)
|
|
||||||
modPanel.ApplyFilter(Filter);
|
|
||||||
|
|
||||||
updateToggleAllState();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Keyboard selection support
|
#region Keyboard selection support
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Layout;
|
using osu.Framework.Layout;
|
||||||
|
using osu.Framework.Lists;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -23,6 +24,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
using osu.Game.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Mods
|
namespace osu.Game.Overlays.Mods
|
||||||
{
|
{
|
||||||
@ -54,11 +56,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
internal GlobalAction? Hotkey { get; set; }
|
internal GlobalAction? Hotkey { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether configurable <see cref="Mod"/>s can be configured by the local user.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual bool AllowCustomisation => true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the total score multiplier calculated from the current selected set of mods should be shown.
|
/// Whether the total score multiplier calculated from the current selected set of mods should be shown.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -66,12 +63,28 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys);
|
protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys);
|
||||||
|
|
||||||
|
protected virtual IEnumerable<ShearedButton> CreateFooterButtons() => new[]
|
||||||
|
{
|
||||||
|
customisationButton = new ShearedToggleButton(200)
|
||||||
|
{
|
||||||
|
Text = ModSelectScreenStrings.ModCustomisation,
|
||||||
|
Active = { BindTarget = customisationVisible }
|
||||||
|
},
|
||||||
|
new ShearedButton(200)
|
||||||
|
{
|
||||||
|
Text = CommonStrings.DeselectAll,
|
||||||
|
Action = DeselectAll
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private readonly BindableBool customisationVisible = new BindableBool();
|
private readonly BindableBool customisationVisible = new BindableBool();
|
||||||
|
|
||||||
private DifficultyMultiplierDisplay? multiplierDisplay;
|
private DifficultyMultiplierDisplay? multiplierDisplay;
|
||||||
private ModSettingsArea modSettingsArea = null!;
|
private ModSettingsArea modSettingsArea = null!;
|
||||||
private ColumnScrollContainer columnScroll = null!;
|
private ColumnScrollContainer columnScroll = null!;
|
||||||
private ColumnFlowContainer columnFlow = null!;
|
private ColumnFlowContainer columnFlow = null!;
|
||||||
|
private ShearedToggleButton? customisationButton;
|
||||||
|
private FillFlowContainer<ShearedButton> footerButtonFlow = null!;
|
||||||
|
|
||||||
protected ModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green)
|
protected ModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green)
|
||||||
: base(colourScheme)
|
: base(colourScheme)
|
||||||
@ -79,10 +92,10 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
Header.Title = "Mod Select";
|
Header.Title = ModSelectScreenStrings.ModSelectTitle;
|
||||||
Header.Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.";
|
Header.Description = ModSelectScreenStrings.ModSelectDescription;
|
||||||
|
|
||||||
AddRange(new Drawable[]
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
@ -107,6 +120,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING,
|
Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING,
|
||||||
|
Bottom = PADDING
|
||||||
},
|
},
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RelativePositionAxes = Axes.Both,
|
RelativePositionAxes = Axes.Both,
|
||||||
@ -157,17 +171,27 @@ namespace osu.Game.Overlays.Mods
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AllowCustomisation)
|
FooterContent.Child = footerButtonFlow = new FillFlowContainer<ShearedButton>
|
||||||
{
|
{
|
||||||
Footer.Add(new ShearedToggleButton(200)
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Vertical = PADDING,
|
||||||
Origin = Anchor.BottomLeft,
|
Horizontal = 70
|
||||||
Margin = new MarginPadding { Vertical = PADDING, Left = 70 },
|
},
|
||||||
Text = "Mod Customisation",
|
Spacing = new Vector2(10),
|
||||||
Active = { BindTarget = customisationVisible }
|
ChildrenEnumerable = CreateFooterButtons().Prepend(new ShearedButton(200)
|
||||||
});
|
{
|
||||||
}
|
Text = CommonStrings.Back,
|
||||||
|
Action = Hide,
|
||||||
|
DarkerColour = colours.Pink2,
|
||||||
|
LighterColour = colours.Pink1
|
||||||
|
})
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null)
|
private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null)
|
||||||
@ -222,7 +246,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private void updateCustomisation(ValueChangedEvent<IReadOnlyList<Mod>> valueChangedEvent)
|
private void updateCustomisation(ValueChangedEvent<IReadOnlyList<Mod>> valueChangedEvent)
|
||||||
{
|
{
|
||||||
if (!AllowCustomisation)
|
if (customisationButton == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool anyCustomisableMod = false;
|
bool anyCustomisableMod = false;
|
||||||
@ -256,6 +280,12 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
MainAreaContent.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic);
|
MainAreaContent.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic);
|
||||||
|
|
||||||
|
foreach (var button in footerButtonFlow)
|
||||||
|
{
|
||||||
|
if (button != customisationButton)
|
||||||
|
button.Enabled.Value = !customisationVisible.Value;
|
||||||
|
}
|
||||||
|
|
||||||
float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0;
|
float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0;
|
||||||
|
|
||||||
modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic);
|
modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic);
|
||||||
@ -277,7 +307,9 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
var candidateSelection = columnFlow.Columns.SelectMany(column => column.SelectedMods).ToArray();
|
var candidateSelection = columnFlow.Columns.SelectMany(column => column.SelectedMods).ToArray();
|
||||||
|
|
||||||
if (candidateSelection.SequenceEqual(SelectedMods.Value))
|
// the following guard intends to check cases where we've already replaced potentially-external mod references with our own and avoid endless recursion.
|
||||||
|
// TODO: replace custom comparer with System.Collections.Generic.ReferenceEqualityComparer when fully on .NET 6
|
||||||
|
if (candidateSelection.SequenceEqual(SelectedMods.Value, new FuncEqualityComparer<Mod>(ReferenceEquals)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection);
|
SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection);
|
||||||
@ -329,6 +361,18 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void SelectAll()
|
||||||
|
{
|
||||||
|
foreach (var column in columnFlow.Columns)
|
||||||
|
column.SelectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DeselectAll()
|
||||||
|
{
|
||||||
|
foreach (var column in columnFlow.Columns)
|
||||||
|
column.DeselectAll();
|
||||||
|
}
|
||||||
|
|
||||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
{
|
{
|
||||||
if (e.Repeat)
|
if (e.Repeat)
|
||||||
@ -448,6 +492,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
FinishTransforms();
|
FinishTransforms();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate || Column.SelectionAnimationRunning;
|
||||||
|
|
||||||
private void updateDim()
|
private void updateDim()
|
||||||
{
|
{
|
||||||
Colour4 targetColour;
|
Colour4 targetColour;
|
||||||
@ -488,6 +534,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
public Action? OnClicked { get; set; }
|
public Action? OnClicked { get; set; }
|
||||||
|
|
||||||
|
public override bool HandlePositionalInput => base.HandlePositionalInput && HandleMouse.Value;
|
||||||
|
|
||||||
protected override bool Handle(UIEvent e)
|
protected override bool Handle(UIEvent e)
|
||||||
{
|
{
|
||||||
if (!HandleMouse.Value)
|
if (!HandleMouse.Value)
|
||||||
|
@ -44,7 +44,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
BorderThickness = 2,
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
background = new Box
|
background = new Box
|
||||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
private void updateProgress(APIUser user)
|
private void updateProgress(APIUser user)
|
||||||
{
|
{
|
||||||
levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0;
|
levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0;
|
||||||
levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'") ?? default(LocalisableString);
|
levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'") ?? default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
startDateColumn.Value = dateToString(response.Spotlight.StartDate);
|
startDateColumn.Value = dateToString(response.Spotlight.StartDate);
|
||||||
endDateColumn.Value = dateToString(response.Spotlight.EndDate);
|
endDateColumn.Value = dateToString(response.Spotlight.EndDate);
|
||||||
mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString(@"N0");
|
mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString(@"N0");
|
||||||
participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString(@"N0") ?? default(LocalisableString);
|
participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString(@"N0") ?? default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString(@"yyyy-MM-dd");
|
private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString(@"yyyy-MM-dd");
|
||||||
|
@ -5,7 +5,6 @@ using System.Collections.Generic;
|
|||||||
using osu.Framework.Extensions.LocalisationExtensions;
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Localisation;
|
|
||||||
using osu.Game.Resources.Localisation.Web;
|
using osu.Game.Resources.Localisation.Web;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
|
|
||||||
protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[]
|
protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[]
|
||||||
{
|
{
|
||||||
new RowText { Text = item.PP?.ToLocalisableString(@"N0") ?? default(LocalisableString), }
|
new RowText { Text = item.PP?.ToLocalisableString(@"N0") ?? default, }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
@ -35,14 +34,13 @@ namespace osu.Game.Overlays
|
|||||||
private readonly Cached headerTextVisibilityCache = new Cached();
|
private readonly Cached headerTextVisibilityCache = new Cached();
|
||||||
|
|
||||||
private readonly FillFlowContainer content;
|
private readonly FillFlowContainer content;
|
||||||
private readonly IconButton button;
|
|
||||||
|
|
||||||
public BindableBool Expanded { get; } = new BindableBool(true);
|
public BindableBool Expanded { get; } = new BindableBool(true);
|
||||||
|
|
||||||
private Color4 expandedColour;
|
|
||||||
|
|
||||||
private readonly OsuSpriteText headerText;
|
private readonly OsuSpriteText headerText;
|
||||||
|
|
||||||
|
private readonly Container headerContent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new instance.
|
/// Create a new instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -71,7 +69,7 @@ namespace osu.Game.Overlays
|
|||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
headerContent = new Container
|
||||||
{
|
{
|
||||||
Name = @"Header",
|
Name = @"Header",
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
@ -88,7 +86,7 @@ namespace osu.Game.Overlays
|
|||||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17),
|
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17),
|
||||||
Padding = new MarginPadding { Left = 10, Right = 30 },
|
Padding = new MarginPadding { Left = 10, Right = 30 },
|
||||||
},
|
},
|
||||||
button = new IconButton
|
new IconButton
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
@ -117,12 +115,25 @@ namespace osu.Game.Overlays
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
if (invalidation.HasFlagFast(Invalidation.DrawSize))
|
base.LoadComplete();
|
||||||
headerTextVisibilityCache.Invalidate();
|
|
||||||
|
|
||||||
return base.OnInvalidate(invalidation, source);
|
Expanded.BindValueChanged(updateExpandedState, true);
|
||||||
|
|
||||||
|
this.Delay(600).Schedule(updateFadeState);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
updateFadeState();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
updateFadeState();
|
||||||
|
base.OnHoverLost(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -135,72 +146,30 @@ namespace osu.Game.Overlays
|
|||||||
headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint);
|
headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved(canBeNull: true)]
|
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
|
||||||
private IExpandingContainer expandingContainer { get; set; }
|
|
||||||
|
|
||||||
private bool expandedByContainer;
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
if (invalidation.HasFlagFast(Invalidation.DrawSize))
|
||||||
|
headerTextVisibilityCache.Invalidate();
|
||||||
|
|
||||||
expandingContainer?.Expanded.BindValueChanged(containerExpanded =>
|
return base.OnInvalidate(invalidation, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateExpandedState(ValueChangedEvent<bool> expanded)
|
||||||
|
{
|
||||||
|
if (expanded.NewValue)
|
||||||
|
content.AutoSizeAxes = Axes.Y;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (containerExpanded.NewValue && !Expanded.Value)
|
content.AutoSizeAxes = Axes.None;
|
||||||
{
|
content.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
|
||||||
Expanded.Value = true;
|
}
|
||||||
expandedByContainer = true;
|
|
||||||
}
|
|
||||||
else if (!containerExpanded.NewValue && expandedByContainer)
|
|
||||||
{
|
|
||||||
Expanded.Value = false;
|
|
||||||
expandedByContainer = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateActiveState();
|
headerContent.FadeColour(expanded.NewValue ? Color4.White : OsuColour.Gray(0.5f), 200, Easing.OutQuint);
|
||||||
}, true);
|
|
||||||
|
|
||||||
Expanded.BindValueChanged(v =>
|
|
||||||
{
|
|
||||||
// clearing transforms can break autosizing, see: https://github.com/ppy/osu-framework/issues/5064
|
|
||||||
if (v.NewValue != v.OldValue)
|
|
||||||
content.ClearTransforms();
|
|
||||||
|
|
||||||
if (v.NewValue)
|
|
||||||
content.AutoSizeAxes = Axes.Y;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
content.AutoSizeAxes = Axes.None;
|
|
||||||
content.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
|
|
||||||
button.FadeColour(Expanded.Value ? expandedColour : Color4.White, 200, Easing.InOutQuint);
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
this.Delay(600).Schedule(updateActiveState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
private void updateFadeState()
|
||||||
{
|
{
|
||||||
updateActiveState();
|
this.FadeTo(IsHovered ? 1 : inactive_alpha, fade_duration, Easing.OutQuint);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnHoverLost(HoverLostEvent e)
|
|
||||||
{
|
|
||||||
updateActiveState();
|
|
||||||
base.OnHoverLost(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
expandedColour = colours.Yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateActiveState()
|
|
||||||
{
|
|
||||||
this.FadeTo(IsHovered || expandingContainer?.Expanded.Value == true ? 1 : inactive_alpha, fade_duration, Easing.OutQuint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
public abstract class DistancedHitObjectComposer<TObject> : HitObjectComposer<TObject>, IDistanceSnapProvider, IScrollBindingHandler<GlobalAction>
|
public abstract class DistancedHitObjectComposer<TObject> : HitObjectComposer<TObject>, IDistanceSnapProvider, IScrollBindingHandler<GlobalAction>
|
||||||
where TObject : HitObject
|
where TObject : HitObject
|
||||||
{
|
{
|
||||||
protected Bindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1.0)
|
public Bindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1.0)
|
||||||
{
|
{
|
||||||
MinValue = 0.1,
|
MinValue = 0.1,
|
||||||
MaxValue = 6.0,
|
MaxValue = 6.0,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -25,7 +24,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) && anyToolboxHovered(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) && anyToolboxHovered(screenSpacePos);
|
||||||
|
|
||||||
private bool anyToolboxHovered(Vector2 screenSpacePos) => FillFlow.Children.Any(d => d.ScreenSpaceDrawQuad.Contains(screenSpacePos));
|
private bool anyToolboxHovered(Vector2 screenSpacePos) => FillFlow.ScreenSpaceDrawQuad.Contains(screenSpacePos);
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e) => true;
|
protected override bool OnMouseDown(MouseDownEvent e) => true;
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A multiplier which changes the ratio of distance travelled per time unit.
|
/// A multiplier which changes the ratio of distance travelled per time unit.
|
||||||
|
/// Importantly, this is provided for manual usage, and not multiplied into any of the methods exposed by this interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="BeatmapInfo.DistanceSpacing"/>
|
/// <seealso cref="BeatmapInfo.DistanceSpacing"/>
|
||||||
IBindable<double> DistanceSpacingMultiplier { get; }
|
IBindable<double> DistanceSpacingMultiplier { get; }
|
||||||
|
@ -30,14 +30,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
Position = StartPosition,
|
Position = StartPosition,
|
||||||
Width = crosshair_thickness,
|
Width = crosshair_thickness,
|
||||||
EdgeSmoothness = new Vector2(1),
|
EdgeSmoothness = new Vector2(1),
|
||||||
Height = Math.Min(crosshair_max_size, DistanceSpacing * 2),
|
Height = Math.Min(crosshair_max_size, DistanceBetweenTicks * 2),
|
||||||
},
|
},
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Position = StartPosition,
|
Position = StartPosition,
|
||||||
EdgeSmoothness = new Vector2(1),
|
EdgeSmoothness = new Vector2(1),
|
||||||
Width = Math.Min(crosshair_max_size, DistanceSpacing * 2),
|
Width = Math.Min(crosshair_max_size, DistanceBetweenTicks * 2),
|
||||||
Height = crosshair_thickness,
|
Height = crosshair_thickness,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -45,19 +45,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
float dx = Math.Max(StartPosition.X, DrawWidth - StartPosition.X);
|
float dx = Math.Max(StartPosition.X, DrawWidth - StartPosition.X);
|
||||||
float dy = Math.Max(StartPosition.Y, DrawHeight - StartPosition.Y);
|
float dy = Math.Max(StartPosition.Y, DrawHeight - StartPosition.Y);
|
||||||
float maxDistance = new Vector2(dx, dy).Length;
|
float maxDistance = new Vector2(dx, dy).Length;
|
||||||
int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceSpacing));
|
int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceBetweenTicks));
|
||||||
|
|
||||||
for (int i = 0; i < requiredCircles; i++)
|
for (int i = 0; i < requiredCircles; i++)
|
||||||
{
|
{
|
||||||
float radius = (i + 1) * DistanceSpacing * 2;
|
float diameter = (i + 1) * DistanceBetweenTicks * 2;
|
||||||
|
|
||||||
AddInternal(new CircularProgress
|
AddInternal(new CircularProgress
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Position = StartPosition,
|
Position = StartPosition,
|
||||||
Current = { Value = 1 },
|
Current = { Value = 1 },
|
||||||
Size = new Vector2(radius),
|
Size = new Vector2(diameter),
|
||||||
InnerRadius = 4 * 1f / radius,
|
InnerRadius = 4 * 1f / diameter,
|
||||||
Colour = GetColourForIndexFromPlacement(i)
|
Colour = GetColourForIndexFromPlacement(i)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -68,19 +68,37 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (MaxIntervals == 0)
|
if (MaxIntervals == 0)
|
||||||
return (StartPosition, StartTime);
|
return (StartPosition, StartTime);
|
||||||
|
|
||||||
Vector2 direction = position - StartPosition;
|
// This grid implementation factors in the user's distance spacing specification,
|
||||||
if (direction == Vector2.Zero)
|
// which is usually not considered by an `IDistanceSnapProvider`.
|
||||||
direction = new Vector2(0.001f, 0.001f);
|
float distanceSpacingMultiplier = (float)DistanceSpacingMultiplier.Value;
|
||||||
|
|
||||||
float distance = direction.Length;
|
Vector2 travelVector = (position - StartPosition);
|
||||||
|
|
||||||
float radius = DistanceSpacing;
|
if (travelVector == Vector2.Zero)
|
||||||
int radialCount = Math.Clamp((int)MathF.Round(distance / radius), 1, MaxIntervals);
|
return (StartPosition, StartTime);
|
||||||
|
|
||||||
Vector2 normalisedDirection = direction * new Vector2(1f / distance);
|
float travelLength = travelVector.Length;
|
||||||
Vector2 snappedPosition = StartPosition + normalisedDirection * radialCount * radius;
|
|
||||||
|
|
||||||
return (snappedPosition, StartTime + SnapProvider.FindSnappedDuration(ReferenceObject, (snappedPosition - StartPosition).Length));
|
// FindSnappedDistance will always round down, but we want to potentially round upwards.
|
||||||
|
travelLength += DistanceBetweenTicks / 2;
|
||||||
|
|
||||||
|
// When interacting with the resolved snap provider, the distance spacing multiplier should first be removed
|
||||||
|
// to allow for snapping at a non-multiplied ratio.
|
||||||
|
float snappedDistance = SnapProvider.FindSnappedDistance(ReferenceObject, travelLength / distanceSpacingMultiplier);
|
||||||
|
double snappedTime = StartTime + SnapProvider.DistanceToDuration(ReferenceObject, snappedDistance);
|
||||||
|
|
||||||
|
if (snappedTime > LatestEndTime)
|
||||||
|
{
|
||||||
|
double tickLength = Beatmap.GetBeatLengthAtTime(StartTime);
|
||||||
|
|
||||||
|
snappedDistance = SnapProvider.DurationToDistance(ReferenceObject, MaxIntervals * tickLength);
|
||||||
|
snappedTime = StartTime + SnapProvider.DistanceToDuration(ReferenceObject, snappedDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The multiplier can then be reapplied to the final position.
|
||||||
|
Vector2 snappedPosition = StartPosition + travelVector.Normalized() * snappedDistance * distanceSpacingMultiplier;
|
||||||
|
|
||||||
|
return (snappedPosition, snappedTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The spacing between each tick of the beat snapping grid.
|
/// The spacing between each tick of the beat snapping grid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected float DistanceSpacing { get; private set; }
|
protected float DistanceBetweenTicks { get; private set; }
|
||||||
|
|
||||||
|
protected IBindable<double> DistanceSpacingMultiplier { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum number of distance snapping intervals allowed.
|
/// The maximum number of distance snapping intervals allowed.
|
||||||
@ -32,7 +34,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The position which the grid should start.
|
/// The position which the grid should start.
|
||||||
/// The first beat snapping tick is located at <see cref="StartPosition"/> + <see cref="DistanceSpacing"/> away from this point.
|
/// The first beat snapping tick is located at <see cref="StartPosition"/> + <see cref="DistanceBetweenTicks"/> away from this point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Vector2 StartPosition;
|
protected readonly Vector2 StartPosition;
|
||||||
|
|
||||||
@ -41,6 +43,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly double StartTime;
|
protected readonly double StartTime;
|
||||||
|
|
||||||
|
protected readonly double? LatestEndTime;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
protected OsuColour Colours { get; private set; }
|
protected OsuColour Colours { get; private set; }
|
||||||
|
|
||||||
@ -48,15 +52,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
protected IDistanceSnapProvider SnapProvider { get; private set; }
|
protected IDistanceSnapProvider SnapProvider { get; private set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorBeatmap beatmap { get; set; }
|
protected EditorBeatmap Beatmap { get; private set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private BindableBeatDivisor beatDivisor { get; set; }
|
private BindableBeatDivisor beatDivisor { get; set; }
|
||||||
|
|
||||||
private IBindable<double> distanceSpacingMultiplier;
|
|
||||||
|
|
||||||
private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
||||||
private readonly double? endTime;
|
|
||||||
|
|
||||||
protected readonly HitObject ReferenceObject;
|
protected readonly HitObject ReferenceObject;
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
protected DistanceSnapGrid(HitObject referenceObject, Vector2 startPosition, double startTime, double? endTime = null)
|
protected DistanceSnapGrid(HitObject referenceObject, Vector2 startPosition, double startTime, double? endTime = null)
|
||||||
{
|
{
|
||||||
ReferenceObject = referenceObject;
|
ReferenceObject = referenceObject;
|
||||||
this.endTime = endTime;
|
LatestEndTime = endTime;
|
||||||
|
|
||||||
StartPosition = startPosition;
|
StartPosition = startPosition;
|
||||||
StartTime = startTime;
|
StartTime = startTime;
|
||||||
@ -86,22 +87,21 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
beatDivisor.BindValueChanged(_ => updateSpacing());
|
beatDivisor.BindValueChanged(_ => updateSpacing());
|
||||||
|
|
||||||
distanceSpacingMultiplier = SnapProvider.DistanceSpacingMultiplier.GetBoundCopy();
|
DistanceSpacingMultiplier = SnapProvider.DistanceSpacingMultiplier.GetBoundCopy();
|
||||||
distanceSpacingMultiplier.BindValueChanged(_ => updateSpacing(), true);
|
DistanceSpacingMultiplier.BindValueChanged(_ => updateSpacing(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSpacing()
|
private void updateSpacing()
|
||||||
{
|
{
|
||||||
DistanceSpacing = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * distanceSpacingMultiplier.Value);
|
float distanceSpacingMultiplier = (float)DistanceSpacingMultiplier.Value;
|
||||||
|
float beatSnapDistance = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject);
|
||||||
|
|
||||||
if (endTime == null)
|
DistanceBetweenTicks = beatSnapDistance * distanceSpacingMultiplier;
|
||||||
|
|
||||||
|
if (LatestEndTime == null)
|
||||||
MaxIntervals = int.MaxValue;
|
MaxIntervals = int.MaxValue;
|
||||||
else
|
else
|
||||||
{
|
MaxIntervals = (int)((LatestEndTime.Value - StartTime) / SnapProvider.DistanceToDuration(ReferenceObject, beatSnapDistance));
|
||||||
// +1 is added since a snapped hitobject may have its start time slightly less than the snapped time due to floating point errors
|
|
||||||
double maxDuration = endTime.Value - StartTime + 1;
|
|
||||||
MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(ReferenceObject, DistanceSpacing));
|
|
||||||
}
|
|
||||||
|
|
||||||
gridCache.Invalidate();
|
gridCache.Invalidate();
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <returns>The applicable colour.</returns>
|
/// <returns>The applicable colour.</returns>
|
||||||
protected ColourInfo GetColourForIndexFromPlacement(int placementIndex)
|
protected ColourInfo GetColourForIndexFromPlacement(int placementIndex)
|
||||||
{
|
{
|
||||||
var timingPoint = beatmap.ControlPointInfo.TimingPointAt(StartTime);
|
var timingPoint = Beatmap.ControlPointInfo.TimingPointAt(StartTime);
|
||||||
double beatLength = timingPoint.BeatLength / beatDivisor.Value;
|
double beatLength = timingPoint.BeatLength / beatDivisor.Value;
|
||||||
int beatIndex = (int)Math.Round((StartTime - timingPoint.Time) / beatLength);
|
int beatIndex = (int)Math.Round((StartTime - timingPoint.Time) / beatLength);
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osuTK.Graphics;
|
using osu.Framework.Layout;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components
|
namespace osu.Game.Screens.Edit.Compose.Components
|
||||||
{
|
{
|
||||||
@ -41,17 +42,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
InternalChild = Box = CreateBox();
|
InternalChild = Box = CreateBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Drawable CreateBox() => new Container
|
protected virtual Drawable CreateBox() => new BoxWithBorders();
|
||||||
{
|
|
||||||
Masking = true,
|
|
||||||
BorderColour = Color4.White,
|
|
||||||
BorderThickness = SelectionBox.BORDER_RADIUS,
|
|
||||||
Child = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0.1f
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private RectangleF? dragRectangle;
|
private RectangleF? dragRectangle;
|
||||||
|
|
||||||
@ -111,5 +102,75 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
public override void Show() => State = Visibility.Visible;
|
public override void Show() => State = Visibility.Visible;
|
||||||
|
|
||||||
public event Action<Visibility> StateChanged;
|
public event Action<Visibility> StateChanged;
|
||||||
|
|
||||||
|
public class BoxWithBorders : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly LayoutValue cache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
||||||
|
|
||||||
|
public BoxWithBorders()
|
||||||
|
{
|
||||||
|
AddLayout(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (!cache.IsValid)
|
||||||
|
{
|
||||||
|
createContent();
|
||||||
|
cache.Validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createContent()
|
||||||
|
{
|
||||||
|
if (DrawSize == Vector2.Zero)
|
||||||
|
{
|
||||||
|
ClearInternal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make lines the same width independent of display resolution.
|
||||||
|
float lineThickness = DrawWidth > 0
|
||||||
|
? DrawWidth / ScreenSpaceDrawQuad.Width * 2
|
||||||
|
: DrawHeight / ScreenSpaceDrawQuad.Height * 2;
|
||||||
|
|
||||||
|
Padding = new MarginPadding(-lineThickness / 2);
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = lineThickness,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = lineThickness,
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = lineThickness,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = lineThickness,
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0.1f
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
private void updateTooltipText()
|
private void updateTooltipText()
|
||||||
{
|
{
|
||||||
TooltipText = cumulativeRotation.Value?.ToLocalisableString("0.0°") ?? default(LocalisableString);
|
TooltipText = cumulativeRotation.Value?.ToLocalisableString("0.0°") ?? default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,44 +87,46 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// case MatchType.TagCoop:
|
#pragma warning disable IDE0055 // Indentation of commented code
|
||||||
// return new SpriteIcon
|
// case MatchType.TagCoop:
|
||||||
// {
|
// return new SpriteIcon
|
||||||
// Anchor = Anchor.Centre,
|
// {
|
||||||
// Origin = Anchor.Centre,
|
// Anchor = Anchor.Centre,
|
||||||
// Size = new Vector2(size),
|
// Origin = Anchor.Centre,
|
||||||
// Icon = FontAwesome.Solid.Sync,
|
// Size = new Vector2(size),
|
||||||
// Colour = colours.Blue,
|
// Icon = FontAwesome.Solid.Sync,
|
||||||
//
|
// Colour = colours.Blue,
|
||||||
// Shadow = false
|
//
|
||||||
// };
|
// Shadow = false
|
||||||
|
// };
|
||||||
|
|
||||||
// case MatchType.TagTeamCoop:
|
// case MatchType.TagTeamCoop:
|
||||||
// return new FillFlowContainer
|
// return new FillFlowContainer
|
||||||
// {
|
// {
|
||||||
// Anchor = Anchor.Centre,
|
// Anchor = Anchor.Centre,
|
||||||
// Origin = Anchor.Centre,
|
// Origin = Anchor.Centre,
|
||||||
// AutoSizeAxes = Axes.Both,
|
// AutoSizeAxes = Axes.Both,
|
||||||
// Direction = FillDirection.Horizontal,
|
// Direction = FillDirection.Horizontal,
|
||||||
// Spacing = new Vector2(2f),
|
// Spacing = new Vector2(2f),
|
||||||
// Children = new[]
|
// Children = new[]
|
||||||
// {
|
// {
|
||||||
// new SpriteIcon
|
// new SpriteIcon
|
||||||
// {
|
// {
|
||||||
// Icon = FontAwesome.Solid.Sync,
|
// Icon = FontAwesome.Solid.Sync,
|
||||||
// Size = new Vector2(size * 0.75f),
|
// Size = new Vector2(size * 0.75f),
|
||||||
// Colour = colours.Blue,
|
// Colour = colours.Blue,
|
||||||
// Shadow = false,
|
// Shadow = false,
|
||||||
// },
|
// },
|
||||||
// new SpriteIcon
|
// new SpriteIcon
|
||||||
// {
|
// {
|
||||||
// Icon = FontAwesome.Solid.Sync,
|
// Icon = FontAwesome.Solid.Sync,
|
||||||
// Size = new Vector2(size * 0.75f),
|
// Size = new Vector2(size * 0.75f),
|
||||||
// Colour = colours.Pink,
|
// Colour = colours.Pink,
|
||||||
// Shadow = false,
|
// Shadow = false,
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
// };
|
// };
|
||||||
|
#pragma warning restore IDE0055
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,15 +3,18 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
using osu.Game.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay
|
namespace osu.Game.Screens.OnlinePlay
|
||||||
{
|
{
|
||||||
public class FreeModSelectScreen : ModSelectScreen
|
public class FreeModSelectScreen : ModSelectScreen
|
||||||
{
|
{
|
||||||
protected override bool AllowCustomisation => false;
|
|
||||||
protected override bool ShowTotalMultiplier => false;
|
protected override bool ShowTotalMultiplier => false;
|
||||||
|
|
||||||
public new Func<Mod, bool> IsValidMod
|
public new Func<Mod, bool> IsValidMod
|
||||||
@ -27,5 +30,23 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys);
|
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys);
|
||||||
|
|
||||||
|
protected override IEnumerable<ShearedButton> CreateFooterButtons() => new[]
|
||||||
|
{
|
||||||
|
new ShearedButton(200)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Text = CommonStrings.SelectAll,
|
||||||
|
Action = SelectAll
|
||||||
|
},
|
||||||
|
new ShearedButton(200)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Text = CommonStrings.DeselectAll,
|
||||||
|
Action = DeselectAll
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,14 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Threading;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Screens.Play.HUD
|
||||||
{
|
{
|
||||||
@ -28,20 +29,22 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
|
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
|
||||||
|
|
||||||
private readonly Button button;
|
private HoldButton button;
|
||||||
|
|
||||||
public Action Action
|
public Action Action { get; set; }
|
||||||
{
|
|
||||||
set => button.Action = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly OsuSpriteText text;
|
private OsuSpriteText text;
|
||||||
|
|
||||||
public HoldForMenuButton()
|
public HoldForMenuButton()
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Horizontal;
|
Direction = FillDirection.Horizontal;
|
||||||
Spacing = new Vector2(20, 0);
|
Spacing = new Vector2(20, 0);
|
||||||
Margin = new MarginPadding(10);
|
Margin = new MarginPadding(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(Player player)
|
||||||
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
text = new OsuSpriteText
|
text = new OsuSpriteText
|
||||||
@ -50,25 +53,20 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft
|
Origin = Anchor.CentreLeft
|
||||||
},
|
},
|
||||||
button = new Button
|
button = new HoldButton(player?.Configuration.AllowRestart == false)
|
||||||
{
|
{
|
||||||
HoverGained = () => text.FadeIn(500, Easing.OutQuint),
|
HoverGained = () => text.FadeIn(500, Easing.OutQuint),
|
||||||
HoverLost = () => text.FadeOut(500, Easing.OutQuint),
|
HoverLost = () => text.FadeOut(500, Easing.OutQuint),
|
||||||
IsPaused = { BindTarget = IsPaused }
|
IsPaused = { BindTarget = IsPaused },
|
||||||
|
Action = () => Action(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private OsuConfigManager config { get; set; }
|
|
||||||
|
|
||||||
private Bindable<double> activationDelay;
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
activationDelay = config.GetBindable<double>(OsuSetting.UIHoldActivationDelay);
|
button.HoldActivationDelay.BindValueChanged(v =>
|
||||||
activationDelay.BindValueChanged(v =>
|
|
||||||
{
|
{
|
||||||
text.Text = v.NewValue > 0
|
text.Text = v.NewValue > 0
|
||||||
? "hold for menu"
|
? "hold for menu"
|
||||||
@ -102,7 +100,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Button : HoldToConfirmContainer, IKeyBindingHandler<GlobalAction>
|
private class HoldButton : HoldToConfirmContainer, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
private SpriteIcon icon;
|
private SpriteIcon icon;
|
||||||
private CircularProgress circularProgress;
|
private CircularProgress circularProgress;
|
||||||
@ -115,6 +113,16 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
public Action HoverGained;
|
public Action HoverGained;
|
||||||
public Action HoverLost;
|
public Action HoverLost;
|
||||||
|
|
||||||
|
private const double shake_duration = 20;
|
||||||
|
|
||||||
|
private bool pendingAnimation;
|
||||||
|
private ScheduledDelegate shakeOperation;
|
||||||
|
|
||||||
|
public HoldButton(bool isDangerousAction)
|
||||||
|
: base(isDangerousAction)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
@ -161,11 +169,38 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
private void bind()
|
private void bind()
|
||||||
{
|
{
|
||||||
circularProgress.Current.BindTo(Progress);
|
((IBindable<double>)circularProgress.Current).BindTo(Progress);
|
||||||
Progress.ValueChanged += progress => icon.Scale = new Vector2(1 + (float)progress.NewValue * 0.2f);
|
Progress.ValueChanged += progress =>
|
||||||
|
{
|
||||||
|
icon.Scale = new Vector2(1 + (float)progress.NewValue * 0.2f);
|
||||||
|
|
||||||
|
if (IsDangerousAction)
|
||||||
|
{
|
||||||
|
Colour = Interpolation.ValueAt(progress.NewValue, Color4.White, Color4.Red, 0, 1, Easing.OutQuint);
|
||||||
|
|
||||||
|
if (progress.NewValue > 0 && progress.NewValue < 1)
|
||||||
|
{
|
||||||
|
shakeOperation ??= Scheduler.AddDelayed(shake, shake_duration, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Child.MoveTo(Vector2.Zero, shake_duration * 2, Easing.OutQuint);
|
||||||
|
shakeOperation?.Cancel();
|
||||||
|
shakeOperation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool pendingAnimation;
|
private void shake()
|
||||||
|
{
|
||||||
|
const float shake_magnitude = 8;
|
||||||
|
|
||||||
|
Child.MoveTo(new Vector2(
|
||||||
|
RNG.NextSingle(-1, 1) * (float)Progress.Value * shake_magnitude,
|
||||||
|
RNG.NextSingle(-1, 1) * (float)Progress.Value * shake_magnitude
|
||||||
|
), shake_duration);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Confirm()
|
protected override void Confirm()
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>8.0</LangVersion>
|
||||||
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
1
osu.sln
1
osu.sln
@ -56,6 +56,7 @@ EndProject
|
|||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{10DF8F12-50FD-45D8-8A38-17BA764BF54D}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{10DF8F12-50FD-45D8-8A38-17BA764BF54D}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
|
.globalconfig = .globalconfig
|
||||||
Directory.Build.props = Directory.Build.props
|
Directory.Build.props = Directory.Build.props
|
||||||
osu.Android.props = osu.Android.props
|
osu.Android.props = osu.Android.props
|
||||||
osu.iOS.props = osu.iOS.props
|
osu.iOS.props = osu.iOS.props
|
||||||
|
Loading…
x
Reference in New Issue
Block a user