diff --git a/.editorconfig b/.editorconfig
index 8cdb92d11c..67f98f94eb 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -135,7 +135,7 @@ csharp_preferred_modifier_order = public,private,protected,internal,new,abstract
csharp_style_expression_bodied_accessors = true:warning
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_indexers = true:warning
-csharp_style_expression_bodied_methods = true:silent
+csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = true:warning
csharp_style_expression_bodied_properties = true:warning
csharp_style_expression_bodied_local_functions = true:silent
diff --git a/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu_SDL.xml b/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu_SDL.xml
new file mode 100644
index 0000000000..d85a0ae44c
--- /dev/null
+++ b/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu_SDL.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 27a0bd0d48..21b8b402e0 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -40,7 +40,7 @@
https://github.com/ppy/osu
Automated release.
ppy Pty Ltd
- Copyright (c) 2019 ppy Pty Ltd
+ Copyright (c) 2020 ppy Pty Ltd
osu game
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
index ab594aee74..e3954c2681 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
- CFPropertyList (3.0.1)
+ CFPropertyList (3.0.2)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
atomos (0.1.3)
@@ -18,8 +18,8 @@ GEM
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.5)
emoji_regex (1.0.1)
- excon (0.67.0)
- faraday (0.15.4)
+ excon (0.71.1)
+ faraday (0.17.3)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
@@ -27,7 +27,7 @@ GEM
faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.7)
- fastlane (2.133.0)
+ fastlane (2.140.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
@@ -36,13 +36,13 @@ GEM
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 2.0)
- excon (>= 0.45.0, < 1.0.0)
- faraday (< 0.16.0)
+ excon (>= 0.71.0, < 1.0.0)
+ faraday (~> 0.17)
faraday-cookie_jar (~> 0.0.6)
- faraday_middleware (< 0.16.0)
+ faraday_middleware (~> 0.13.1)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
- google-api-client (>= 0.21.2, < 0.24.0)
+ google-api-client (>= 0.29.2, < 0.37.0)
google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
@@ -61,56 +61,58 @@ GEM
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
- xcodeproj (>= 1.8.1, < 2.0.0)
+ xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-clean_testflight_testers (0.3.0)
- fastlane-plugin-souyuz (0.8.1)
- souyuz (>= 0.8.1)
+ fastlane-plugin-souyuz (0.9.1)
+ souyuz (= 0.9.1)
fastlane-plugin-xamarin (0.6.3)
gh_inspector (1.1.3)
- google-api-client (0.23.9)
+ google-api-client (0.36.4)
addressable (~> 2.5, >= 2.5.1)
- googleauth (>= 0.5, < 0.7.0)
+ googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
- mime-types (~> 3.0)
+ mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
- signet (~> 0.9)
- google-cloud-core (1.3.1)
+ signet (~> 0.12)
+ google-cloud-core (1.5.0)
google-cloud-env (~> 1.0)
- google-cloud-env (1.2.1)
+ google-cloud-errors (~> 1.0)
+ google-cloud-env (1.3.0)
faraday (~> 0.11)
- google-cloud-storage (1.16.0)
+ google-cloud-errors (1.0.0)
+ google-cloud-storage (1.25.1)
+ addressable (~> 2.5)
digest-crc (~> 0.4)
- google-api-client (~> 0.23)
+ google-api-client (~> 0.33)
google-cloud-core (~> 1.2)
- googleauth (>= 0.6.2, < 0.10.0)
- googleauth (0.6.7)
+ googleauth (~> 0.9)
+ mini_mime (~> 1.0)
+ googleauth (0.10.0)
faraday (~> 0.12)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
- signet (~> 0.7)
+ signet (~> 0.12)
highline (1.7.10)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
- json (2.2.0)
+ json (2.3.0)
jwt (2.1.0)
- memoist (0.16.0)
- mime-types (3.3)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2019.1009)
- mini_magick (4.9.5)
+ memoist (0.16.2)
+ mini_magick (4.10.1)
+ mini_mime (1.0.2)
mini_portile2 (2.4.0)
- multi_json (1.13.1)
+ multi_json (1.14.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.6)
naturally (2.2.0)
- nokogiri (1.10.4)
+ nokogiri (1.10.7)
mini_portile2 (~> 2.4.0)
os (1.0.1)
plist (3.5.0)
@@ -128,12 +130,12 @@ GEM
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
- simctl (1.6.6)
+ simctl (1.6.7)
CFPropertyList
naturally
slack-notifier (2.3.2)
- souyuz (0.8.1)
- fastlane (>= 2.29.0)
+ souyuz (0.9.1)
+ fastlane (>= 1.103.0)
highline (~> 1.7)
nokogiri (~> 1.7)
terminal-notifier (2.0.0)
@@ -141,15 +143,15 @@ GEM
unicode-display_width (~> 1.1, >= 1.1.1)
tty-cursor (0.7.0)
tty-screen (0.7.0)
- tty-spinner (0.9.1)
+ tty-spinner (0.9.2)
tty-cursor (~> 0.7)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.6)
- unicode-display_width (1.6.0)
+ unicode-display_width (1.6.1)
word_wrap (1.0.0)
- xcodeproj (1.12.0)
+ xcodeproj (1.14.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
diff --git a/LICENCE b/LICENCE
index 21c6a7090f..2435c23545 100644
--- a/LICENCE
+++ b/LICENCE
@@ -1,4 +1,4 @@
-Copyright (c) 2019 ppy Pty Ltd .
+Copyright (c) 2020 ppy Pty Ltd .
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 28a83fbbae..510b53054b 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -111,7 +111,6 @@ platform :ios do
souyuz(
platform: "ios",
- build_target: "osu_iOS",
plist_path: "../osu.iOS/Info.plist"
)
end
diff --git a/osu.Android.props b/osu.Android.props
index f3838644d1..e5a1ec2f4e 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -54,6 +54,6 @@
-
+
diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs
index 141b2cdbbc..bd91bcc933 100644
--- a/osu.Desktop/Program.cs
+++ b/osu.Desktop/Program.cs
@@ -22,8 +22,9 @@ namespace osu.Desktop
{
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
+ bool useSdl = args.Contains("--sdl");
- using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
+ using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true, useSdl: useSdl))
{
host.ExceptionThrown += handleException;
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index da47ad8223..b9294088f4 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -29,7 +29,7 @@
-
+
diff --git a/osu.Desktop/osu.nuspec b/osu.Desktop/osu.nuspec
index a26b35fcd5..a919d54f38 100644
--- a/osu.Desktop/osu.nuspec
+++ b/osu.Desktop/osu.nuspec
@@ -12,7 +12,7 @@
click the circles. to the beat.
click the circles.
testing
- Copyright (c) 2019 ppy Pty Ltd
+ Copyright (c) 2020 ppy Pty Ltd
en-AU
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs
index a47efcc10a..4c72b9fd3e 100644
--- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs
@@ -36,7 +36,10 @@ namespace osu.Game.Rulesets.Catch.Mods
//disable keyboard controls
public bool OnPressed(CatchAction action) => true;
- public bool OnReleased(CatchAction action) => true;
+
+ public void OnReleased(CatchAction action)
+ {
+ }
protected override bool OnMouseMove(MouseMoveEvent e)
{
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index 589503c35b..2d71fb93fb 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -20,7 +20,9 @@ namespace osu.Game.Rulesets.Catch.UI
internal readonly CatcherArea CatcherArea;
- public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || CatcherArea.ReceivePositionalInputAt(screenSpacePos);
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
+ // only check the X position; handle all vertical space.
+ base.ReceivePositionalInputAt(new Vector2(screenSpacePos.X, ScreenSpaceDrawQuad.Centre.Y));
public CatchPlayfield(BeatmapDifficulty difficulty, Func> createDrawableRepresentation)
{
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index 0c8c483048..1de0b6bfa3 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -103,7 +103,9 @@ namespace osu.Game.Rulesets.Catch.UI
MovableCatcher.X = state.CatcherX.Value;
}
- public bool OnReleased(CatchAction action) => false;
+ public void OnReleased(CatchAction action)
+ {
+ }
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
@@ -341,24 +343,22 @@ namespace osu.Game.Rulesets.Catch.UI
return false;
}
- public bool OnReleased(CatchAction action)
+ public void OnReleased(CatchAction action)
{
switch (action)
{
case CatchAction.MoveLeft:
currentDirection++;
- return true;
+ break;
case CatchAction.MoveRight:
currentDirection--;
- return true;
+ break;
case CatchAction.Dash:
Dashing = false;
- return true;
+ break;
}
-
- return false;
}
///
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs
index b28d8bb0e6..7a3b42914e 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaPlacementBlueprint.cs
@@ -54,10 +54,10 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
return true;
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
EndPlacement();
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
public override void UpdatePosition(Vector2 screenSpacePosition)
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs
index 3bd7fb2d49..9f57160f99 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs
@@ -13,7 +13,7 @@ using osuTK;
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
{
- public class ManiaSelectionBlueprint : SelectionBlueprint
+ public class ManiaSelectionBlueprint : OverlaySelectionBlueprint
{
public Vector2 ScreenSpaceDragPosition { get; private set; }
public Vector2 DragPosition { get; private set; }
@@ -55,14 +55,12 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
return base.OnMouseDown(e);
}
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
- var result = base.OnDrag(e);
+ base.OnDrag(e);
ScreenSpaceDragPosition = e.ScreenSpaceMousePosition;
DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition);
-
- return result;
}
public override void Show()
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs
new file mode 100644
index 0000000000..d744036b4c
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs
@@ -0,0 +1,34 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Mania.Edit.Blueprints;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Screens.Edit.Compose.Components;
+
+namespace osu.Game.Rulesets.Mania.Edit
+{
+ public class ManiaBlueprintContainer : ComposeBlueprintContainer
+ {
+ public ManiaBlueprintContainer(IEnumerable drawableHitObjects)
+ : base(drawableHitObjects)
+ {
+ }
+
+ public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
+ {
+ switch (hitObject)
+ {
+ case DrawableNote note:
+ return new NoteSelectionBlueprint(note);
+
+ case DrawableHoldNote holdNote:
+ return new HoldNoteSelectionBlueprint(holdNote);
+ }
+
+ return base.CreateBlueprintFor(hitObject);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
index 1632b6a583..62b609610f 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
@@ -5,11 +5,8 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mania.Objects;
-using osu.Game.Rulesets.Mania.Objects.Drawables;
-using osu.Game.Rulesets.Objects.Drawables;
using System.Collections.Generic;
using osu.Framework.Allocation;
-using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
@@ -52,26 +49,12 @@ namespace osu.Game.Rulesets.Mania.Edit
return drawableRuleset;
}
+ protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ManiaBlueprintContainer(drawableRuleset.Playfield.AllHitObjects);
+
protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[]
{
new NoteCompositionTool(),
new HoldNoteCompositionTool()
};
-
- public override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler();
-
- public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
- {
- switch (hitObject)
- {
- case DrawableNote note:
- return new NoteSelectionBlueprint(note);
-
- case DrawableHoldNote holdNote:
- return new HoldNoteSelectionBlueprint(holdNote);
- }
-
- return base.CreateBlueprintFor(hitObject);
- }
}
}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs
index 618af3e772..9069a636a8 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Timing;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI;
@@ -70,10 +71,12 @@ namespace osu.Game.Rulesets.Mania.Edit
// When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen.
// This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height.
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
- delta -= moveEvent.Blueprint.DrawableObject.Parent.DrawHeight;
+ delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: probably wrong
- foreach (var b in SelectedBlueprints)
+ foreach (var selectionBlueprint in SelectedBlueprints)
{
+ var b = (OverlaySelectionBlueprint)selectionBlueprint;
+
var hitObject = b.DrawableObject;
var objectParent = (HitObjectContainer)hitObject.Parent;
diff --git a/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs
index ff8882124f..433db79ae0 100644
--- a/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Masks/ManiaSelectionBlueprint.cs
@@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Edit.Masks
{
- public abstract class ManiaSelectionBlueprint : SelectionBlueprint
+ public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint
{
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
: base(drawableObject)
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 02c2158383..b7b523a94d 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -237,19 +237,19 @@ namespace osu.Game.Rulesets.Mania
{
LeftKeys = new[]
{
- InputKey.Number1,
- InputKey.Number2,
- InputKey.Number3,
- InputKey.Number4,
+ InputKey.Q,
+ InputKey.W,
+ InputKey.E,
+ InputKey.R,
},
RightKeys = new[]
{
- InputKey.Z,
InputKey.X,
InputKey.C,
- InputKey.V
+ InputKey.V,
+ InputKey.B
},
- SpecialKey = InputKey.Tilde,
+ SpecialKey = InputKey.S,
SpecialAction = ManiaAction.Special1,
NormalActionStart = ManiaAction.Key1
}.GenerateKeyBindingsFor(keys, out var nextNormal);
@@ -265,12 +265,12 @@ namespace osu.Game.Rulesets.Mania
},
RightKeys = new[]
{
- InputKey.O,
- InputKey.P,
- InputKey.BracketLeft,
- InputKey.BracketRight
+ InputKey.K,
+ InputKey.L,
+ InputKey.Semicolon,
+ InputKey.Quote
},
- SpecialKey = InputKey.BackSlash,
+ SpecialKey = InputKey.I,
SpecialAction = ManiaAction.Special2,
NormalActionStart = nextNormal
}.GenerateKeyBindingsFor(keys, out _);
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs
index 39185e6a57..4c125ad6ef 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string Name => "Fade In";
public override string Acronym => "FI";
- public override IconUsage Icon => OsuIcon.ModHidden;
+ public override IconUsage? Icon => OsuIcon.ModHidden;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => @"Keys appear out of nowhere!";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
index b12d3a7a70..14b36fb765 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Random";
public override string Acronym => "RD";
public override ModType Type => ModType.Conversion;
- public override IconUsage Icon => OsuIcon.Dice;
+ public override IconUsage? Icon => OsuIcon.Dice;
public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index 155adb958b..14a7c5fda3 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -171,17 +171,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
bodyPiece.Hitting = true;
}
- public bool OnReleased(ManiaAction action)
+ public void OnReleased(ManiaAction action)
{
if (AllJudged)
- return false;
+ return;
if (action != Action.Value)
- return false;
+ return;
// Make sure a hold was started
if (HoldStartTime == null)
- return false;
+ return;
Tail.UpdateResult();
endHold();
@@ -189,8 +189,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// If the key has been released too early, the user should not receive full score for the release
if (!Tail.IsHit)
HasBroken = true;
-
- return true;
}
private void endHold()
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
index a5d03bf765..390c64c5e2 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
@@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
- public override bool OnReleased(ManiaAction action) => false; // Handled by the hold note
+ public override void OnReleased(ManiaAction action)
+ {
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs
index a660144dd1..568b07c958 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs
@@ -59,6 +59,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
- public override bool OnReleased(ManiaAction action) => false; // Handled by the hold note
+ public override void OnReleased(ManiaAction action)
+ {
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 8f353ae138..85613d3afb 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -77,6 +77,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return UpdateResult(true);
}
- public virtual bool OnReleased(ManiaAction action) => false;
+ public virtual void OnReleased(ManiaAction action)
+ {
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 3d2a070b0f..63c573d344 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -191,7 +191,9 @@ namespace osu.Game.Rulesets.Mania.UI
return true;
}
- public bool OnReleased(ManiaAction action) => false;
+ public void OnReleased(ManiaAction action)
+ {
+ }
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
index 57241da564..75cc351310 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
@@ -98,11 +98,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
return false;
}
- public bool OnReleased(ManiaAction action)
+ public void OnReleased(ManiaAction action)
{
if (action == this.action.Value)
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
- return false;
}
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
index 85880222d7..60fc2713b3 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
@@ -115,11 +115,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
return false;
}
- public bool OnReleased(ManiaAction action)
+ public void OnReleased(ManiaAction action)
{
if (action == this.action.Value)
keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
- return false;
}
}
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs
index 4676f14655..bbb50c287b 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs
@@ -7,12 +7,10 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
-using osu.Framework.Graphics;
using osu.Framework.IO.Stores;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
@@ -21,7 +19,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Tests
{
- public class TestSceneLegacyBeatmapSkin : OsuTestScene
+ public class TestSceneLegacyBeatmapSkin : ScreenTestScene
{
[Resolved]
private AudioManager audio { get; set; }
@@ -65,7 +63,8 @@ namespace osu.Game.Rulesets.Osu.Tests
ExposedPlayer player;
Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours);
- Child = new OsuScreenStack(player = new ExposedPlayer(userHasCustomColours)) { RelativeSizeAxes = Axes.Both };
+
+ LoadScreen(player = new ExposedPlayer(userHasCustomColours));
return player;
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
index b6fc9821a4..94df239267 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
@@ -44,6 +44,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private const double time_during_slide_2 = 3000;
private const double time_during_slide_3 = 3500;
private const double time_during_slide_4 = 3800;
+ private const double time_slider_end = 4000;
private List judgementResults;
private bool allJudgedFired;
@@ -284,6 +285,48 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("Tracking acquired", assertMidSliderJudgements);
}
+ ///
+ /// Scenario:
+ /// - Press a key on the slider head
+ /// - While holding the key, move cursor close to the edge of tracking area
+ /// - Keep the cursor on the edge of tracking area until the slider ends
+ /// Expected Result:
+ /// A passing test case will have the slider track the cursor throughout the whole test.
+ ///
+ [Test]
+ public void TestTrackingAreaEdge()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(0, OsuHitObject.OBJECT_RADIUS * 1.19f), Actions = { OsuAction.LeftButton }, Time = time_slider_start + 250 },
+ new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.199f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
+ });
+
+ AddAssert("Tracking kept", assertGreatJudge);
+ }
+
+ ///
+ /// Scenario:
+ /// - Press a key on the slider head
+ /// - While holding the key, move cursor just outside the tracking area
+ /// - Keep the cursor just outside the tracking area until the slider ends
+ /// Expected Result:
+ /// A passing test case will have the slider drop the tracking on frame 2.
+ ///
+ [Test]
+ public void TestTrackingAreaOutsideEdge()
+ {
+ performTest(new List
+ {
+ new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
+ new OsuReplayFrame { Position = new Vector2(0, OsuHitObject.OBJECT_RADIUS * 1.21f), Actions = { OsuAction.LeftButton }, Time = time_slider_start + 250 },
+ new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.201f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
+ });
+
+ AddAssert("Tracking dropped", assertMidSliderJudgementFail);
+ }
+
private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
@@ -294,6 +337,8 @@ namespace osu.Game.Rulesets.Osu.Tests
private ScoreAccessibleReplayPlayer currentPlayer;
+ private const float slider_path_length = 25;
+
private void performTest(List frames)
{
AddStep("load player", () =>
@@ -309,8 +354,8 @@ namespace osu.Game.Rulesets.Osu.Tests
Path = new SliderPath(PathType.PerfectCurve, new[]
{
Vector2.Zero,
- new Vector2(25, 0),
- }, 25),
+ new Vector2(slider_path_length, 0),
+ }, slider_path_length),
}
},
BeatmapInfo =
diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
index 1d8c4708c1..9d4e016eae 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -4,7 +4,7 @@
-
+
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs
index a864257274..b0e13808a5 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs
@@ -7,10 +7,10 @@ using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints
{
- public abstract class OsuSelectionBlueprint : SelectionBlueprint
+ public abstract class OsuSelectionBlueprint : OverlaySelectionBlueprint
where T : OsuHitObject
{
- protected T HitObject => (T)DrawableObject.HitObject;
+ protected new T HitObject => (T)DrawableObject.HitObject;
protected OsuSelectionBlueprint(DrawableHitObject drawableObject)
: base(drawableObject)
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
index 6a0730db91..af4da5e853 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
@@ -135,13 +135,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return false;
}
- protected override bool OnMouseUp(MouseUpEvent e) => RequestSelection != null;
-
protected override bool OnClick(ClickEvent e) => RequestSelection != null;
protected override bool OnDragStart(DragStartEvent e) => e.Button == MouseButton.Left;
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
if (ControlPoint == slider.Path.ControlPoints[0])
{
@@ -158,12 +156,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
}
else
ControlPoint.Position.Value += e.Delta;
-
- return true;
}
- protected override bool OnDragEnd(DragEndEvent e) => true;
-
///
/// Updates the state of the circular control point marker.
///
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
index 6f583d7983..e293eba9d7 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -108,7 +108,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return false;
}
- public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete;
+ public void OnReleased(PlatformAction action)
+ {
+ }
private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
{
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs
index f09279ed73..a0392fe536 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs
@@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
: base(slider)
{
this.position = position;
+
InternalChild = CirclePiece = new HitCirclePiece();
Select();
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
index 2497e428fc..90512849d4 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
@@ -106,11 +106,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
return true;
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
if (state == PlacementState.Body && e.Button == MouseButton.Right)
endCurve();
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override bool OnDoubleClick(DoubleClickEvent e)
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
index 3165c441fb..c18b3b0ff3 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
@@ -90,19 +90,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
protected override bool OnDragStart(DragStartEvent e) => placementControlPointIndex != null;
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
Debug.Assert(placementControlPointIndex != null);
HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position;
-
- return true;
}
- protected override bool OnDragEnd(DragEndEvent e)
+ protected override void OnDragEnd(DragEndEvent e)
{
placementControlPointIndex = null;
- return true;
}
private BindableList controlPoints => HitObject.Path.ControlPoints;
@@ -173,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
};
- public override Vector2 SelectionPoint => HeadBlueprint.SelectionPoint;
+ public override Vector2 SelectionPoint => ((DrawableSlider)DrawableObject).HeadCircle.ScreenSpaceDrawQuad.Centre;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos);
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
new file mode 100644
index 0000000000..330f34b85c
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
@@ -0,0 +1,41 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Screens.Edit.Compose.Components;
+
+namespace osu.Game.Rulesets.Osu.Edit
+{
+ public class OsuBlueprintContainer : ComposeBlueprintContainer
+ {
+ public OsuBlueprintContainer(IEnumerable drawableHitObjects)
+ : base(drawableHitObjects)
+ {
+ }
+
+ protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
+
+ public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
+ {
+ switch (hitObject)
+ {
+ case DrawableHitCircle circle:
+ return new HitCircleSelectionBlueprint(circle);
+
+ case DrawableSlider slider:
+ return new SliderSelectionBlueprint(slider);
+
+ case DrawableSpinner spinner:
+ return new SpinnerSelectionBlueprint(spinner);
+ }
+
+ return base.CreateBlueprintFor(hitObject);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index 49624ea733..b01488e7c2 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -9,12 +9,7 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
-using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
-using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Compose.Components;
@@ -37,24 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit
new SpinnerCompositionTool()
};
- public override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
-
- public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
- {
- switch (hitObject)
- {
- case DrawableHitCircle circle:
- return new HitCircleSelectionBlueprint(circle);
-
- case DrawableSlider slider:
- return new SliderSelectionBlueprint(slider);
-
- case DrawableSpinner spinner:
- return new SpinnerSelectionBlueprint(spinner);
- }
-
- return base.CreateBlueprintFor(hitObject);
- }
+ protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects);
protected override DistanceSnapGrid CreateDistanceSnapGrid(IEnumerable selectedHitObjects)
{
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs
index 65d7acc911..fe46876050 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Autopilot";
public override string Acronym => "AP";
- public override IconUsage Icon => OsuIcon.ModAutopilot;
+ public override IconUsage? Icon => OsuIcon.ModAutopilot;
public override ModType Type => ModType.Automation;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
index 831e4a700f..937473e824 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Description => "Play with blinds on your screen.";
public override string Acronym => "BL";
- public override IconUsage Icon => FontAwesome.Solid.Adjust;
+ public override IconUsage? Icon => FontAwesome.Solid.Adjust;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => false;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs
index 9bf7525d33..73cb483ef0 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Acronym => "DF";
- public override IconUsage Icon => FontAwesome.Solid.CompressArrowsAlt;
+ public override IconUsage? Icon => FontAwesome.Solid.CompressArrowsAlt;
public override string Description => "Hit them at the right size!";
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
index 76676ce888..f08d4e8f5e 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Acronym => "GR";
- public override IconUsage Icon => FontAwesome.Solid.ArrowsAltV;
+ public override IconUsage? Icon => FontAwesome.Solid.ArrowsAltV;
public override string Description => "Hit them at the right size!";
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
index eae218509e..940c888f3a 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Spin In";
public override string Acronym => "SI";
- public override IconUsage Icon => FontAwesome.Solid.Undo;
+ public override IconUsage? Icon => FontAwesome.Solid.Undo;
public override ModType Type => ModType.Fun;
public override string Description => "Circles spin in. No approach circles.";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs
index 1cdcddbd33..9d5d300a9e 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Spun Out";
public override string Acronym => "SO";
- public override IconUsage Icon => OsuIcon.ModSpunout;
+ public override IconUsage? Icon => OsuIcon.ModSpunout;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => @"Spinners will be automatically completed.";
public override double ScoreMultiplier => 0.9;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs
index 8360e2692e..2464308347 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Name => "Target";
public override string Acronym => "TP";
public override ModType Type => ModType.Conversion;
- public override IconUsage Icon => OsuIcon.ModTarget;
+ public override IconUsage? Icon => OsuIcon.ModTarget;
public override string Description => @"Practice keeping up with the beat of the song.";
public override double ScoreMultiplier => 1;
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
index dff9a77807..774f9cf58b 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
@@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Bindables;
using System.Collections.Generic;
using osu.Framework.Extensions.Color4Extensions;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
@@ -19,7 +18,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Traceable";
public override string Acronym => "TC";
- public override IconUsage Icon => FontAwesome.Brands.SnapchatGhost;
public override ModType Type => ModType.Fun;
public override string Description => "Put your faith in the approach circles...";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
index a9475af638..cc664ae72e 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Transform";
public override string Acronym => "TR";
- public override IconUsage Icon => FontAwesome.Solid.ArrowsAlt;
+ public override IconUsage? Icon => FontAwesome.Solid.ArrowsAlt;
public override ModType Type => ModType.Fun;
public override string Description => "Everything rotates. EVERYTHING.";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
index 1664a37a66..cc2f4c3f70 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Wiggle";
public override string Acronym => "WG";
- public override IconUsage Icon => FontAwesome.Solid.Certificate;
+ public override IconUsage? Icon => FontAwesome.Solid.Certificate;
public override ModType Type => ModType.Fun;
public override string Description => "They just won't stay still...";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
index 6c4fbbac17..a5e89210f6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
///
/// The start time of .
///
- public readonly Bindable StartTime = new Bindable();
+ public readonly Bindable StartTime = new BindableDouble();
///
/// The which s will exit from.
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index f74f2d7bc5..4ef63bb2a0 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly IBindable positionBindable = new Bindable();
private readonly IBindable stackHeightBindable = new Bindable();
- private readonly IBindable scaleBindable = new Bindable();
+ private readonly IBindable scaleBindable = new BindableFloat();
public OsuAction? HitAction => HitArea.HitAction;
@@ -205,7 +205,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return false;
}
- public bool OnReleased(OsuAction action) => false;
+ public void OnReleased(OsuAction action)
+ {
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
index 96b18f2d80..8fdcd060e7 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
@@ -6,13 +6,11 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring;
using osuTK;
-using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -23,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private double animDuration;
- private readonly SkinnableDrawable scaleContainer;
+ private readonly Drawable scaleContainer;
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
: base(repeatPoint)
@@ -36,19 +34,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Blending = BlendingParameters.Additive;
Origin = Anchor.Centre;
- InternalChild = scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon
- {
- RelativeSizeAxes = Axes.Both,
- Icon = FontAwesome.Solid.ChevronRight,
- Size = new Vector2(0.35f)
- }, confineMode: ConfineMode.NoScaling)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- };
+ InternalChild = scaleContainer = new ReverseArrowPiece();
}
- private readonly IBindable scaleBindable = new Bindable();
+ private readonly IBindable scaleBindable = new BindableFloat();
[BackgroundDependencyLoader]
private void load()
@@ -65,11 +54,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateInitialTransforms()
{
- animDuration = Math.Min(150, repeatPoint.SpanDuration / 2);
+ animDuration = Math.Min(300, repeatPoint.SpanDuration);
this.Animate(
d => d.FadeIn(animDuration),
- d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 4, Easing.OutElasticHalf)
+ d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 2, Easing.OutElasticHalf)
);
}
@@ -88,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break;
case ArmedState.Hit:
- this.FadeOut(animDuration, Easing.OutQuint)
+ this.FadeOut(animDuration, Easing.Out)
.ScaleTo(Scale * 1.5f, animDuration, Easing.Out);
break;
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index cd3c572ba0..7403649184 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly IBindable positionBindable = new Bindable();
private readonly IBindable stackHeightBindable = new Bindable();
- private readonly IBindable scaleBindable = new Bindable();
+ private readonly IBindable scaleBindable = new BindableFloat();
public DrawableSlider(Slider s)
: base(s)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index 9d4d9958a1..60b5c335d6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
};
}
- private readonly IBindable scaleBindable = new Bindable();
+ private readonly IBindable scaleBindable = new BindableFloat();
[BackgroundDependencyLoader]
private void load()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs
new file mode 100644
index 0000000000..35a27bb0a6
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ReverseArrowPiece.cs
@@ -0,0 +1,43 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Audio.Track;
+using osu.Framework.Graphics;
+using osuTK;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Graphics.Containers;
+using osu.Game.Skinning;
+
+namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+{
+ public class ReverseArrowPiece : BeatSyncedContainer
+ {
+ public ReverseArrowPiece()
+ {
+ Divisor = 2;
+ MinimumBeatLength = 200;
+
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+
+ Blending = BlendingParameters.Additive;
+
+ Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
+
+ Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon
+ {
+ RelativeSizeAxes = Axes.Both,
+ Icon = FontAwesome.Solid.ChevronRight,
+ Size = new Vector2(0.35f)
+ })
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ };
+ }
+
+ protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) =>
+ Child.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out);
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index ef7b077480..0dc5c9b4a0 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public Func GetInitialHitAction;
private readonly Slider slider;
- public readonly Drawable FollowCircle;
+ private readonly Drawable followCircle;
private readonly DrawableSlider drawableSlider;
public SliderBall(Slider slider, DrawableSlider drawableSlider = null)
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Children = new[]
{
- FollowCircle = new FollowCircleContainer
+ followCircle = new FollowCircleContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
@@ -95,8 +95,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
tracking = value;
- FollowCircle.ScaleTo(tracking ? 2f : 1, 300, Easing.OutQuint);
- FollowCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
+ followCircle.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint);
+ followCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
}
}
@@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
// in valid time range
Time.Current >= slider.StartTime && Time.Current < slider.EndTime &&
// in valid position range
- lastScreenSpaceMousePosition.HasValue && FollowCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
+ lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
// valid action
(actions?.Any(isValidTrackingAction) ?? false);
}
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 0ba712a83f..15af141c99 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public double Radius => OBJECT_RADIUS * Scale;
- public readonly Bindable ScaleBindable = new Bindable(1);
+ public readonly Bindable ScaleBindable = new BindableFloat(1);
public float Scale
{
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
index 6433ced624..79b5d1b7f8 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
autoCursorScale = config.GetBindable(OsuSetting.AutoCursorSize);
autoCursorScale.ValueChanged += _ => calculateScale();
- CursorScale = new Bindable();
+ CursorScale = new BindableFloat();
CursorScale.ValueChanged += e => ActiveCursor.Scale = cursorTrail.Scale = new Vector2(e.NewValue);
calculateScale();
@@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
return false;
}
- public bool OnReleased(OsuAction action)
+ public void OnReleased(OsuAction action)
{
switch (action)
{
@@ -120,8 +120,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
updateExpandedState();
break;
}
-
- return false;
}
public override bool HandlePositionalInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.
diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
index 3b18e41f30..abba444c73 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.UI
{
Add(localCursorContainer = new OsuCursorContainer());
- localCursorScale = new Bindable();
+ localCursorScale = new BindableFloat();
localCursorScale.BindTo(localCursorContainer.CursorScale);
localCursorScale.BindValueChanged(scale => cursorScaleContainer.Scale = new Vector2(scale.NewValue), true);
}
@@ -107,7 +107,9 @@ namespace osu.Game.Rulesets.Osu.UI
return false;
}
- public bool OnReleased(OsuAction action) => false;
+ public void OnReleased(OsuAction action)
+ {
+ }
public void Appear() => Schedule(() =>
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index 4b25ff0ecc..85dfc8d5e0 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -77,11 +77,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return result;
}
- public override bool OnReleased(TaikoAction action)
+ public override void OnReleased(TaikoAction action)
{
if (action == HitAction)
HitAction = null;
- return base.OnReleased(action);
+
+ base.OnReleased(action);
}
protected override void Update()
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index b9d31ff906..5f892dd2fa 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -77,7 +77,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public Drawable CreateProxiedContent() => proxiedContent.CreateProxy();
public abstract bool OnPressed(TaikoAction action);
- public virtual bool OnReleased(TaikoAction action) => false;
+
+ public virtual void OnReleased(TaikoAction action)
+ {
+ }
public override double LifetimeStart
{
diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
index 5234ae1f69..d26ccfe867 100644
--- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
+++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs
@@ -187,7 +187,9 @@ namespace osu.Game.Rulesets.Taiko.UI
return false;
}
- public bool OnReleased(TaikoAction action) => false;
+ public void OnReleased(TaikoAction action)
+ {
+ }
}
}
}
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index 4766411cbd..c1bd73ef05 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
@@ -13,7 +14,9 @@ using osu.Game.IPC;
using osu.Framework.Allocation;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
+using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Resources;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
@@ -552,6 +555,83 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
+ [Test]
+ public async Task TestUpdateBeatmapInfo()
+ {
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapInfo)))
+ {
+ try
+ {
+ var osu = loadOsu(host);
+ var manager = osu.Dependencies.Get();
+
+ var temp = TestResources.GetTestBeatmapForImport();
+ await osu.Dependencies.Get().Import(temp);
+
+ // Update via the beatmap, not the beatmap info, to ensure correct linking
+ BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0];
+ Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap;
+ beatmapToUpdate.BeatmapInfo.Version = "updated";
+
+ manager.Update(setToUpdate);
+
+ BeatmapInfo updatedInfo = manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID);
+ Assert.That(updatedInfo.Version, Is.EqualTo("updated"));
+ }
+ finally
+ {
+ host.Exit();
+ }
+ }
+ }
+
+ [Test]
+ public async Task TestUpdateBeatmapFile()
+ {
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapFile)))
+ {
+ try
+ {
+ var osu = loadOsu(host);
+ var manager = osu.Dependencies.Get();
+
+ var temp = TestResources.GetTestBeatmapForImport();
+ await osu.Dependencies.Get().Import(temp);
+
+ BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0];
+ Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap;
+ BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename));
+
+ using (var stream = new MemoryStream())
+ {
+ using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
+ {
+ beatmapToUpdate.HitObjects.Clear();
+ beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 });
+
+ new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer);
+ }
+
+ stream.Seek(0, SeekOrigin.Begin);
+
+ manager.UpdateFile(setToUpdate, fileToUpdate, stream);
+ }
+
+ // Check that the old file reference has been removed
+ Assert.That(manager.QueryBeatmapSet(s => s.ID == setToUpdate.ID).Files.All(f => f.ID != fileToUpdate.ID));
+
+ // Check that the new file is referenced correctly by attempting a retrieval
+ Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap;
+ Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1));
+ Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000));
+ }
+ finally
+ {
+ host.Exit();
+ }
+ }
+ }
+
public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
{
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
diff --git a/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs
index 2d336bd19c..5a4e76d586 100644
--- a/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs
+++ b/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs
@@ -5,6 +5,7 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Edit;
@@ -19,7 +20,13 @@ namespace osu.Game.Tests.Editor
private TestHitObjectComposer composer;
[Cached(typeof(EditorBeatmap))]
- private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap());
+ [Cached(typeof(IBeatSnapProvider))]
+ private readonly EditorBeatmap editorBeatmap;
+
+ public TestSceneHitObjectComposerDistanceSnapping()
+ {
+ editorBeatmap = new EditorBeatmap(new OsuBeatmap(), BeatDivisor);
+ }
[SetUp]
public void Setup() => Schedule(() =>
@@ -111,17 +118,19 @@ namespace osu.Game.Tests.Editor
[Test]
public void TestGetSnappedDurationFromDistance()
{
- assertSnappedDuration(50, 0);
+ assertSnappedDuration(0, 0);
+ assertSnappedDuration(50, 1000);
assertSnappedDuration(100, 1000);
- assertSnappedDuration(150, 1000);
+ assertSnappedDuration(150, 2000);
assertSnappedDuration(200, 2000);
- assertSnappedDuration(250, 2000);
+ assertSnappedDuration(250, 3000);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
+ assertSnappedDuration(0, 0);
assertSnappedDuration(50, 0);
- assertSnappedDuration(100, 0);
- assertSnappedDuration(150, 0);
+ assertSnappedDuration(100, 1000);
+ assertSnappedDuration(150, 1000);
assertSnappedDuration(200, 1000);
assertSnappedDuration(250, 1000);
@@ -132,8 +141,8 @@ namespace osu.Game.Tests.Editor
});
assertSnappedDuration(50, 0);
- assertSnappedDuration(100, 0);
- assertSnappedDuration(150, 0);
+ assertSnappedDuration(100, 500);
+ assertSnappedDuration(150, 500);
assertSnappedDuration(200, 500);
assertSnappedDuration(250, 500);
assertSnappedDuration(400, 1000);
@@ -142,17 +151,17 @@ namespace osu.Game.Tests.Editor
[Test]
public void GetSnappedDistanceFromDistance()
{
- assertSnappedDistance(50, 0);
+ assertSnappedDistance(50, 100);
assertSnappedDistance(100, 100);
- assertSnappedDistance(150, 100);
+ assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
- assertSnappedDistance(250, 200);
+ assertSnappedDistance(250, 300);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDistance(50, 0);
- assertSnappedDistance(100, 0);
- assertSnappedDistance(150, 0);
+ assertSnappedDistance(100, 200);
+ assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
@@ -163,8 +172,8 @@ namespace osu.Game.Tests.Editor
});
assertSnappedDistance(50, 0);
- assertSnappedDistance(100, 0);
- assertSnappedDistance(150, 0);
+ assertSnappedDistance(100, 200);
+ assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
assertSnappedDistance(400, 400);
diff --git a/osu.Game.Tests/Online/TestAPIModSerialization.cs b/osu.Game.Tests/Online/TestAPIModSerialization.cs
new file mode 100644
index 0000000000..d9318aa822
--- /dev/null
+++ b/osu.Game.Tests/Online/TestAPIModSerialization.cs
@@ -0,0 +1,82 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using Newtonsoft.Json;
+using NUnit.Framework;
+using osu.Framework.Bindables;
+using osu.Game.Beatmaps;
+using osu.Game.Configuration;
+using osu.Game.Online.API;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Tests.Online
+{
+ [TestFixture]
+ public class TestAPIModSerialization
+ {
+ [Test]
+ public void TestAcronymIsPreserved()
+ {
+ var apiMod = new APIMod(new TestMod());
+
+ var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod));
+
+ Assert.That(deserialized.Acronym, Is.EqualTo(apiMod.Acronym));
+ }
+
+ [Test]
+ public void TestRawSettingIsPreserved()
+ {
+ var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
+
+ var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod));
+
+ Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(2.0));
+ }
+
+ [Test]
+ public void TestConvertedModHasCorrectSetting()
+ {
+ var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
+
+ var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod));
+ var converted = (TestMod)deserialized.ToMod(new TestRuleset());
+
+ Assert.That(converted.TestSetting.Value, Is.EqualTo(2));
+ }
+
+ private class TestRuleset : Ruleset
+ {
+ public override IEnumerable GetModsFor(ModType type) => new[] { new TestMod() };
+
+ public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => throw new System.NotImplementedException();
+
+ public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException();
+
+ public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new System.NotImplementedException();
+
+ public override string Description { get; } = string.Empty;
+ public override string ShortName { get; } = string.Empty;
+ }
+
+ private class TestMod : Mod
+ {
+ public override string Name => "Test Mod";
+ public override string Acronym => "TM";
+ public override double ScoreMultiplier => 1;
+
+ [SettingSource("Test")]
+ public BindableNumber TestSetting { get; } = new BindableDouble
+ {
+ MinValue = 0,
+ MaxValue = 10,
+ Default = 5,
+ Precision = 0.01,
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
index 589ec7e8aa..6d014ca1ca 100644
--- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
+++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
@@ -68,10 +68,10 @@ namespace osu.Game.Tests.Visual.Background
[SetUp]
public virtual void SetUp() => Schedule(() =>
{
- Child = new OsuScreenStack(songSelect = new DummySongSelect())
- {
- RelativeSizeAxes = Axes.Both
- };
+ var stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
+ Child = stack;
+
+ stack.Push(songSelect = new DummySongSelect());
});
///
@@ -277,7 +277,7 @@ namespace osu.Game.Tests.Visual.Background
private void setupUserSettings()
{
- AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null);
+ AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmap != null);
AddStep("Set default user settings", () =>
{
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray();
@@ -302,8 +302,8 @@ namespace osu.Game.Tests.Visual.Background
}
public readonly Bindable DimEnabled = new Bindable();
- public readonly Bindable DimLevel = new Bindable();
- public readonly Bindable BlurLevel = new Bindable();
+ public readonly Bindable DimLevel = new BindableDouble();
+ public readonly Bindable BlurLevel = new BindableDouble();
public new BeatmapCarousel Carousel => base.Carousel;
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs b/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs
index 3562689482..a8830824c0 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneComposeScreen.cs
@@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Screens.Edit;
@@ -14,6 +15,7 @@ namespace osu.Game.Tests.Visual.Editor
public class TestSceneComposeScreen : EditorClockTestScene
{
[Cached(typeof(EditorBeatmap))]
+ [Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap =
new EditorBeatmap(new OsuBeatmap
{
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs
index 847d168e51..f49256a633 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs
@@ -85,64 +85,64 @@ namespace osu.Game.Tests.Visual.Editor
{
}
- protected override void CreateContent(Vector2 startPosition)
+ protected override void CreateContent()
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5),
- Position = startPosition
+ Position = StartPosition
});
- int beatIndex = 0;
+ int indexFromPlacement = 0;
- for (float s = startPosition.X + DistanceSpacing; s <= DrawWidth && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++)
+ for (float s = StartPosition.X + DistanceSpacing; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5, 10),
- Position = new Vector2(s, startPosition.Y),
- Colour = GetColourForBeatIndex(beatIndex)
+ Position = new Vector2(s, StartPosition.Y),
+ Colour = GetColourForIndexFromPlacement(indexFromPlacement)
});
}
- beatIndex = 0;
+ indexFromPlacement = 0;
- for (float s = startPosition.X - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++)
+ for (float s = StartPosition.X - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5, 10),
- Position = new Vector2(s, startPosition.Y),
- Colour = GetColourForBeatIndex(beatIndex)
+ Position = new Vector2(s, StartPosition.Y),
+ Colour = GetColourForIndexFromPlacement(indexFromPlacement)
});
}
- beatIndex = 0;
+ indexFromPlacement = 0;
- for (float s = startPosition.Y + DistanceSpacing; s <= DrawHeight && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++)
+ for (float s = StartPosition.Y + DistanceSpacing; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(10, 5),
- Position = new Vector2(startPosition.X, s),
- Colour = GetColourForBeatIndex(beatIndex)
+ Position = new Vector2(StartPosition.X, s),
+ Colour = GetColourForIndexFromPlacement(indexFromPlacement)
});
}
- beatIndex = 0;
+ indexFromPlacement = 0;
- for (float s = startPosition.Y - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++)
+ for (float s = StartPosition.Y - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(10, 5),
- Position = new Vector2(startPosition.X, s),
- Colour = GetColourForBeatIndex(beatIndex)
+ Position = new Vector2(StartPosition.X, s),
+ Colour = GetColourForIndexFromPlacement(indexFromPlacement)
});
}
}
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs
index c001c83877..e41c2427fb 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneHitObjectComposer.cs
@@ -66,6 +66,7 @@ namespace osu.Game.Tests.Visual.Editor
Dependencies.CacheAs(clock);
Dependencies.CacheAs(clock);
Dependencies.CacheAs(editorBeatmap);
+ Dependencies.CacheAs(editorBeatmap);
Child = new OsuHitObjectComposer(new OsuRuleset());
}
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs b/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs
new file mode 100644
index 0000000000..3c75fd5310
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs
@@ -0,0 +1,15 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Game.Screens.Edit.Compose.Components.Timeline;
+
+namespace osu.Game.Tests.Visual.Editor
+{
+ [TestFixture]
+ public class TestSceneTimelineBlueprintContainer : TimelineTestScene
+ {
+ public override Drawable CreateTestComponent() => new TimelineBlueprintContainer();
+ }
+}
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneTimelineTickDisplay.cs b/osu.Game.Tests/Visual/Editor/TestSceneTimelineTickDisplay.cs
new file mode 100644
index 0000000000..43a3cd6122
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editor/TestSceneTimelineTickDisplay.cs
@@ -0,0 +1,32 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Screens.Edit.Compose.Components;
+using osu.Game.Screens.Edit.Compose.Components.Timeline;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Editor
+{
+ [TestFixture]
+ public class TestSceneTimelineTickDisplay : TimelineTestScene
+ {
+ public override Drawable CreateTestComponent() => new TimelineTickDisplay();
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ BeatDivisor.Value = 4;
+
+ Add(new BeatDivisorControl(BeatDivisor)
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Margin = new MarginPadding(30),
+ Size = new Vector2(90)
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TimelineTestScene.cs
similarity index 86%
rename from osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs
rename to osu.Game.Tests/Visual/Editor/TimelineTestScene.cs
index 29575cb42e..b5e526d3c2 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs
+++ b/osu.Game.Tests/Visual/Editor/TimelineTestScene.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
@@ -13,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
@@ -21,26 +21,29 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
{
- [TestFixture]
- public class TestSceneEditorComposeTimeline : EditorClockTestScene
+ public abstract class TimelineTestScene : EditorClockTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
typeof(TimelineArea),
- typeof(TimelineHitObjectDisplay),
typeof(Timeline),
typeof(TimelineButton),
typeof(CentreMarker)
};
+ protected TimelineArea TimelineArea { get; private set; }
+
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
Beatmap.Value = new WaveformTestBeatmap(audio);
- var editorBeatmap = new EditorBeatmap((Beatmap)Beatmap.Value.Beatmap);
+ var editorBeatmap = new EditorBeatmap((Beatmap)Beatmap.Value.Beatmap, BeatDivisor);
- Children = new Drawable[]
+ Dependencies.Cache(editorBeatmap);
+ Dependencies.CacheAs(editorBeatmap);
+
+ AddRange(new Drawable[]
{
new FillFlowContainer
{
@@ -53,17 +56,19 @@ namespace osu.Game.Tests.Visual.Editor
new AudioVisualiser(),
}
},
- new TimelineArea
+ TimelineArea = new TimelineArea
{
- Child = new TimelineHitObjectDisplay(editorBeatmap),
+ Child = CreateTestComponent(),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
- Size = new Vector2(0.8f, 100)
+ Size = new Vector2(0.8f, 100),
}
- };
+ });
}
+ public abstract Drawable CreateTestComponent();
+
private class AudioVisualiser : CompositeDrawable
{
private readonly Drawable marker;
diff --git a/osu.Game/Tests/Visual/AllPlayersTestScene.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs
similarity index 59%
rename from osu.Game/Tests/Visual/AllPlayersTestScene.cs
rename to osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs
index dd65c8c382..83a7b896d2 100644
--- a/osu.Game/Tests/Visual/AllPlayersTestScene.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs
@@ -2,49 +2,66 @@
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
+using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Screens;
using osu.Game.Configuration;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Catch;
+using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Play;
-namespace osu.Game.Tests.Visual
+namespace osu.Game.Tests.Visual.Gameplay
{
///
/// A base class which runs test for all available rulesets.
/// Steps to be run for each ruleset should be added via .
///
- public abstract class AllPlayersTestScene : RateAdjustedBeatmapTestScene
+ public abstract class TestSceneAllRulesetPlayers : RateAdjustedBeatmapTestScene
{
protected Player Player;
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
- foreach (var r in rulesets.AvailableRulesets)
- {
- Player p = null;
- AddStep(r.Name, () => p = loadPlayerFor(r));
- AddUntilStep("player loaded", () =>
- {
- if (p?.IsLoaded == true)
- {
- p = null;
- return true;
- }
-
- return false;
- });
-
- AddCheckSteps();
- }
-
OsuConfigManager manager;
Dependencies.Cache(manager = new OsuConfigManager(LocalStorage));
manager.GetBindable(OsuSetting.DimLevel).Value = 1.0;
}
+ [Test]
+ public void TestOsu() => runForRuleset(new OsuRuleset().RulesetInfo);
+
+ [Test]
+ public void TestTaiko() => runForRuleset(new TaikoRuleset().RulesetInfo);
+
+ [Test]
+ public void TestCatch() => runForRuleset(new CatchRuleset().RulesetInfo);
+
+ [Test]
+ public void TestMania() => runForRuleset(new ManiaRuleset().RulesetInfo);
+
+ private void runForRuleset(RulesetInfo ruleset)
+ {
+ Player p = null;
+ AddStep($"load {ruleset.Name} player", () => p = loadPlayerFor(ruleset));
+ AddUntilStep("player loaded", () =>
+ {
+ if (p?.IsLoaded == true)
+ {
+ p = null;
+ return true;
+ }
+
+ return false;
+ });
+
+ AddCheckSteps();
+ }
+
protected abstract void AddCheckSteps();
private Player loadPlayerFor(RulesetInfo rulesetInfo)
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
index 069b965d9b..4daab8d137 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
@@ -12,7 +12,7 @@ using osu.Game.Storyboards;
namespace osu.Game.Tests.Visual.Gameplay
{
[Description("Player instantiated with an autoplay mod.")]
- public class TestSceneAutoplay : AllPlayersTestScene
+ public class TestSceneAutoplay : TestSceneAllRulesetPlayers
{
private ClockBackedTestWorkingBeatmap.TrackVirtualManual track;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs
index 81050b1637..de257c9e53 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs
@@ -10,7 +10,7 @@ using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay
{
- public class TestSceneFailAnimation : AllPlayersTestScene
+ public class TestSceneFailAnimation : TestSceneAllRulesetPlayers
{
protected override Player CreatePlayer(Ruleset ruleset)
{
@@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(AllPlayersTestScene),
+ typeof(TestSceneAllRulesetPlayers),
typeof(TestPlayer),
typeof(Player),
};
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
index 2045072c79..d80efb2c6e 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
@@ -10,7 +10,7 @@ using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay
{
- public class TestSceneFailJudgement : AllPlayersTestScene
+ public class TestSceneFailJudgement : TestSceneAllRulesetPlayers
{
protected override Player CreatePlayer(Ruleset ruleset)
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs
similarity index 75%
rename from osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs
rename to osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs
index 72fc6d8bd2..8904b54b0d 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs
@@ -19,18 +19,22 @@ using osu.Game.Screens.Play.HUD.HitErrorMeters;
namespace osu.Game.Tests.Visual.Gameplay
{
- public class TestSceneBarHitErrorMeter : OsuTestScene
+ public class TestSceneHitErrorMeter : OsuTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
typeof(HitErrorMeter),
+ typeof(BarHitErrorMeter),
+ typeof(ColourHitErrorMeter)
};
- private HitErrorMeter meter;
- private HitErrorMeter meter2;
+ private BarHitErrorMeter barMeter;
+ private BarHitErrorMeter barMeter2;
+ private ColourHitErrorMeter colourMeter;
+ private ColourHitErrorMeter colourMeter2;
private HitWindows hitWindows;
- public TestSceneBarHitErrorMeter()
+ public TestSceneHitErrorMeter()
{
recreateDisplay(new OsuHitWindows(), 5);
@@ -91,17 +95,31 @@ namespace osu.Game.Tests.Visual.Gameplay
}
});
- Add(meter = new BarHitErrorMeter(hitWindows, true)
+ Add(barMeter = new BarHitErrorMeter(hitWindows, true)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
});
- Add(meter2 = new BarHitErrorMeter(hitWindows, false)
+ Add(barMeter2 = new BarHitErrorMeter(hitWindows, false)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
});
+
+ Add(colourMeter = new ColourHitErrorMeter(hitWindows)
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Margin = new MarginPadding { Right = 50 }
+ });
+
+ Add(colourMeter2 = new ColourHitErrorMeter(hitWindows)
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Margin = new MarginPadding { Left = 50 }
+ });
}
private void newJudgement(double offset = 0)
@@ -112,8 +130,10 @@ namespace osu.Game.Tests.Visual.Gameplay
Type = HitResult.Perfect,
};
- meter.OnNewJudgement(judgement);
- meter2.OnNewJudgement(judgement);
+ barMeter.OnNewJudgement(judgement);
+ barMeter2.OnNewJudgement(judgement);
+ colourMeter.OnNewJudgement(judgement);
+ colourMeter2.OnNewJudgement(judgement);
}
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
index 1a83e35e4f..ad5bab4681 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
@@ -36,6 +36,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public override void SetUpSteps()
{
base.SetUpSteps();
+
AddStep("resume player", () => Player.GameplayClockContainer.Start());
confirmClockRunning(true);
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
index ad5950d9fc..33ecbed62e 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
@@ -207,9 +207,11 @@ namespace osu.Game.Tests.Visual.Gameplay
{
RelativeSizeAxes = Axes.Both;
+ OsuScreenStack stack;
+
InternalChildren = new Drawable[]
{
- new OsuScreenStack(screen)
+ stack = new OsuScreenStack
{
RelativeSizeAxes = Axes.Both,
},
@@ -224,6 +226,8 @@ namespace osu.Game.Tests.Visual.Gameplay
Origin = Anchor.TopLeft,
}
};
+
+ stack.Push(screen);
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs
index 4d701f56a9..8f767659c6 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs
@@ -10,7 +10,7 @@ using osu.Game.Storyboards;
namespace osu.Game.Tests.Visual.Gameplay
{
- public class TestScenePlayerReferenceLeaking : AllPlayersTestScene
+ public class TestScenePlayerReferenceLeaking : TestSceneAllRulesetPlayers
{
private readonly WeakList workingWeakReferences = new WeakList();
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs
index 36335bc54a..e82722e7a2 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplay.cs
@@ -13,7 +13,7 @@ using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay
{
[Description("Player instantiated with a replay.")]
- public class TestSceneReplay : AllPlayersTestScene
+ public class TestSceneReplay : TestSceneAllRulesetPlayers
{
protected override Player CreatePlayer(Ruleset ruleset)
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneResults.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneResults.cs
index 7790126db5..2b7a32ba17 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneResults.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneResults.cs
@@ -75,10 +75,16 @@ namespace osu.Game.Tests.Visual.Gameplay
public void ResultsWithoutPlayer()
{
TestSoloResults screen = null;
+ OsuScreenStack stack;
- AddStep("load results", () => Child = new OsuScreenStack(screen = createResultsScreen())
+ AddStep("load results", () =>
{
- RelativeSizeAxes = Axes.Both
+ Child = stack = new OsuScreenStack
+ {
+ RelativeSizeAxes = Axes.Both
+ };
+
+ stack.Push(screen = createResultsScreen());
});
AddUntilStep("wait for loaded", () => screen.IsLoaded);
AddAssert("retry overlay not present", () => screen.RetryOverlay == null);
@@ -102,11 +108,14 @@ namespace osu.Game.Tests.Visual.Gameplay
public TestResultsContainer(IScreen screen)
{
RelativeSizeAxes = Axes.Both;
+ OsuScreenStack stack;
- InternalChild = new OsuScreenStack(screen)
+ InternalChild = stack = new OsuScreenStack
{
RelativeSizeAxes = Axes.Both,
};
+
+ stack.Push(screen);
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs
index 9a217ae416..b9b13d7bd8 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs
@@ -1,12 +1,17 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Framework.Timing;
+using osu.Game.Graphics;
using osu.Game.Rulesets.Objects;
using osu.Game.Screens.Play;
@@ -15,63 +20,125 @@ namespace osu.Game.Tests.Visual.Gameplay
[TestFixture]
public class TestSceneSongProgress : OsuTestScene
{
- private readonly SongProgress progress;
- private readonly TestSongProgressGraph graph;
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(SongProgressBar),
+ };
+
+ private SongProgress progress;
+ private TestSongProgressGraph graph;
+ private readonly Container progressContainer;
private readonly StopwatchClock clock;
+ private readonly FramedClock framedClock;
[Cached]
private readonly GameplayClock gameplayClock;
- private readonly FramedClock framedClock;
-
public TestSceneSongProgress()
{
- clock = new StopwatchClock(true);
-
+ clock = new StopwatchClock();
gameplayClock = new GameplayClock(framedClock = new FramedClock(clock));
- Add(progress = new SongProgress
+ Add(progressContainer = new Container
{
RelativeSizeAxes = Axes.X,
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ Height = 100,
+ Y = -100,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.Gray(1),
+ }
});
-
- Add(graph = new TestSongProgressGraph
- {
- RelativeSizeAxes = Axes.X,
- Height = 200,
- Anchor = Anchor.TopLeft,
- Origin = Anchor.TopLeft,
- });
-
- AddWaitStep("wait some", 5);
- AddAssert("ensure not created", () => graph.CreationCount == 0);
-
- AddStep("display values", displayNewValues);
- AddWaitStep("wait some", 5);
- AddUntilStep("wait for creation count", () => graph.CreationCount == 1);
-
- AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking);
- AddWaitStep("wait some", 5);
- AddUntilStep("wait for creation count", () => graph.CreationCount == 1);
-
- AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking);
- AddWaitStep("wait some", 5);
- AddUntilStep("wait for creation count", () => graph.CreationCount == 1);
- AddRepeatStep("New Values", displayNewValues, 5);
-
- AddWaitStep("wait some", 5);
- AddAssert("ensure debounced", () => graph.CreationCount == 2);
}
- private void displayNewValues()
+ [SetUpSteps]
+ public void SetupSteps()
{
- List objects = new List();
+ AddStep("add new song progress", () =>
+ {
+ if (progress != null)
+ {
+ progress.Expire();
+ progress = null;
+ }
+
+ progressContainer.Add(progress = new SongProgress
+ {
+ RelativeSizeAxes = Axes.X,
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ });
+ });
+
+ AddStep("add new big graph", () =>
+ {
+ if (graph != null)
+ {
+ graph.Expire();
+ graph = null;
+ }
+
+ Add(graph = new TestSongProgressGraph
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 200,
+ Anchor = Anchor.TopLeft,
+ Origin = Anchor.TopLeft,
+ });
+ });
+
+ AddStep("reset clock", clock.Reset);
+ }
+
+ [Test]
+ public void TestGraphRecreation()
+ {
+ AddAssert("ensure not created", () => graph.CreationCount == 0);
+ AddStep("display values", displayRandomValues);
+ AddUntilStep("wait for creation count", () => graph.CreationCount == 1);
+ AddRepeatStep("new values", displayRandomValues, 5);
+ AddWaitStep("wait some", 5);
+ AddAssert("ensure recreation debounced", () => graph.CreationCount == 2);
+ }
+
+ [Test]
+ public void TestDisplay()
+ {
+ AddStep("display max values", displayMaxValues);
+ AddUntilStep("wait for graph", () => graph.CreationCount == 1);
+ AddStep("start", clock.Start);
+ AddStep("allow seeking", () => progress.AllowSeeking.Value = true);
+ AddStep("hide graph", () => progress.ShowGraph.Value = false);
+ AddStep("disallow seeking", () => progress.AllowSeeking.Value = false);
+ AddStep("allow seeking", () => progress.AllowSeeking.Value = true);
+ AddStep("show graph", () => progress.ShowGraph.Value = true);
+ AddStep("stop", clock.Stop);
+ }
+
+ private void displayRandomValues()
+ {
+ var objects = new List();
for (double i = 0; i < 5000; i += RNG.NextDouble() * 10 + i / 1000)
objects.Add(new HitObject { StartTime = i });
+ replaceObjects(objects);
+ }
+
+ private void displayMaxValues()
+ {
+ var objects = new List();
+ for (double i = 0; i < 5000; i++)
+ objects.Add(new HitObject { StartTime = i });
+
+ replaceObjects(objects);
+ }
+
+ private void replaceObjects(List objects)
+ {
progress.Objects = objects;
graph.Objects = objects;
diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
index d03d341ee4..5870ef9813 100644
--- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
+++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Menus
protected IntroTestScene()
{
- Drawable introStack = null;
+ OsuScreenStack introStack = null;
Children = new Drawable[]
{
@@ -57,10 +57,12 @@ namespace osu.Game.Tests.Visual.Menus
introStack?.Expire();
- Add(introStack = new OsuScreenStack(CreateScreen())
+ Add(introStack = new OsuScreenStack
{
RelativeSizeAxes = Axes.Both,
});
+
+ introStack.Push(CreateScreen());
});
}
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSongTicker.cs b/osu.Game.Tests/Visual/Menus/TestSceneSongTicker.cs
new file mode 100644
index 0000000000..d7f23f5cc0
--- /dev/null
+++ b/osu.Game.Tests/Visual/Menus/TestSceneSongTicker.cs
@@ -0,0 +1,36 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Overlays;
+using osu.Game.Screens.Menu;
+
+namespace osu.Game.Tests.Visual.Menus
+{
+ public class TestSceneSongTicker : OsuTestScene
+ {
+ [Cached]
+ private MusicController musicController = new MusicController();
+
+ public TestSceneSongTicker()
+ {
+ AddRange(new Drawable[]
+ {
+ musicController,
+ new SongTicker
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new NowPlayingOverlay
+ {
+ Origin = Anchor.TopRight,
+ Anchor = Anchor.TopRight,
+ State = { Value = Visibility.Visible }
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs
index 3f89f636b1..76ab402b72 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiHeader.cs
@@ -16,7 +16,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
int index = 0;
- OsuScreenStack screenStack = new OsuScreenStack(new TestMultiplayerSubScreen(index)) { RelativeSizeAxes = Axes.Both };
+ OsuScreenStack screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
+
+ screenStack.Push(new TestMultiplayerSubScreen(index));
Children = new Drawable[]
{
diff --git a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs
new file mode 100644
index 0000000000..8d2e4a614d
--- /dev/null
+++ b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs
@@ -0,0 +1,133 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Configuration;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Platform;
+using osu.Framework.Screens;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Configuration;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
+using osu.Game.Overlays;
+using osu.Game.Rulesets;
+using osu.Game.Screens;
+using osu.Game.Screens.Menu;
+using osuTK.Graphics;
+using IntroSequence = osu.Game.Configuration.IntroSequence;
+
+namespace osu.Game.Tests.Visual.Navigation
+{
+ ///
+ /// A scene which tests full game flow.
+ ///
+ public abstract class OsuGameTestScene : ManualInputManagerTestScene
+ {
+ private GameHost host;
+
+ protected TestOsuGame Game;
+
+ [BackgroundDependencyLoader]
+ private void load(GameHost host)
+ {
+ this.host = host;
+
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ };
+ }
+
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("Create new game instance", () =>
+ {
+ if (Game != null)
+ {
+ Remove(Game);
+ Game.Dispose();
+ }
+
+ RecycleLocalStorage();
+
+ // see MouseSettings
+ var frameworkConfig = host.Dependencies.Get();
+ frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity).Disabled = false;
+
+ Game = new TestOsuGame(LocalStorage, API);
+ Game.SetHost(host);
+
+ // todo: this can be removed once we can run audio tracks without a device present
+ // see https://github.com/ppy/osu/issues/1302
+ Game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
+
+ Add(Game);
+ });
+
+ AddUntilStep("Wait for load", () => Game.IsLoaded);
+ AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroScreen);
+
+ ConfirmAtMainMenu();
+ }
+
+ protected void PushAndConfirm(Func newScreen)
+ {
+ Screen screen = null;
+ AddStep("Push new screen", () => Game.ScreenStack.Push(screen = newScreen()));
+ AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
+ }
+
+ protected void ConfirmAtMainMenu() => AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
+
+ public class TestOsuGame : OsuGame
+ {
+ public new ScreenStack ScreenStack => base.ScreenStack;
+
+ public new BackButton BackButton => base.BackButton;
+
+ public new BeatmapManager BeatmapManager => base.BeatmapManager;
+
+ public new SettingsPanel Settings => base.Settings;
+
+ public new OsuConfigManager LocalConfig => base.LocalConfig;
+
+ public new Bindable Beatmap => base.Beatmap;
+
+ public new Bindable Ruleset => base.Ruleset;
+
+ protected override Loader CreateLoader() => new TestLoader();
+
+ public new void PerformFromScreen(Action action, IEnumerable validScreens = null) => base.PerformFromScreen(action, validScreens);
+
+ public TestOsuGame(Storage storage, IAPIProvider api)
+ {
+ Storage = storage;
+ API = api;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ API.Login("Rhythm Champion", "osu!");
+ }
+ }
+
+ public class TestLoader : Loader
+ {
+ protected override ShaderPrecompiler CreateShaderPrecompiler() => new TestShaderPrecompiler();
+
+ private class TestShaderPrecompiler : ShaderPrecompiler
+ {
+ protected override bool AllLoaded => true;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs
new file mode 100644
index 0000000000..75c6a2b733
--- /dev/null
+++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs
@@ -0,0 +1,72 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Game.Screens.Menu;
+using osu.Game.Screens.Play;
+using osu.Game.Screens.Select;
+
+namespace osu.Game.Tests.Visual.Navigation
+{
+ public class TestScenePerformFromScreen : OsuGameTestScene
+ {
+ [Test]
+ public void TestPerformAtMenu()
+ {
+ AddAssert("could perform immediately", () =>
+ {
+ bool actionPerformed = false;
+ Game.PerformFromScreen(_ => actionPerformed = true);
+ return actionPerformed;
+ });
+ }
+
+ [Test]
+ public void TestPerformAtSongSelect()
+ {
+ PushAndConfirm(() => new PlaySongSelect());
+
+ AddAssert("could perform immediately", () =>
+ {
+ bool actionPerformed = false;
+ Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(PlaySongSelect) });
+ return actionPerformed;
+ });
+ }
+
+ [Test]
+ public void TestPerformAtMenuFromSongSelect()
+ {
+ PushAndConfirm(() => new PlaySongSelect());
+
+ bool actionPerformed = false;
+ AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true));
+ AddUntilStep("returned to menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
+ AddAssert("did perform", () => actionPerformed);
+ }
+
+ [Test]
+ public void TestPerformAtSongSelectFromPlayerLoader()
+ {
+ PushAndConfirm(() => new PlaySongSelect());
+ PushAndConfirm(() => new PlayerLoader(() => new Player()));
+
+ bool actionPerformed = false;
+ AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(PlaySongSelect) }));
+ AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
+ AddAssert("did perform", () => actionPerformed);
+ }
+
+ [Test]
+ public void TestPerformAtMenuFromPlayerLoader()
+ {
+ PushAndConfirm(() => new PlaySongSelect());
+ PushAndConfirm(() => new PlayerLoader(() => new Player()));
+
+ bool actionPerformed = false;
+ AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true));
+ AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is MainMenu);
+ AddAssert("did perform", () => actionPerformed);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs
new file mode 100644
index 0000000000..909409835c
--- /dev/null
+++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs
@@ -0,0 +1,110 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Screens;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.Menu;
+
+namespace osu.Game.Tests.Visual.Navigation
+{
+ public class TestScenePresentBeatmap : OsuGameTestScene
+ {
+ [Test]
+ public void TestFromMainMenu()
+ {
+ var firstImport = importBeatmap(1);
+ presentAndConfirm(firstImport);
+
+ AddStep("return to menu", () => Game.ScreenStack.CurrentScreen.Exit());
+ AddUntilStep("wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
+
+ var secondimport = importBeatmap(2);
+ presentAndConfirm(secondimport);
+ }
+
+ [Test]
+ public void TestFromMainMenuDifferentRuleset()
+ {
+ var firstImport = importBeatmap(1);
+ presentAndConfirm(firstImport);
+
+ AddStep("return to menu", () => Game.ScreenStack.CurrentScreen.Exit());
+ AddUntilStep("wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
+
+ var secondimport = importBeatmap(2, new ManiaRuleset().RulesetInfo);
+ presentAndConfirm(secondimport);
+ }
+
+ [Test]
+ public void TestFromSongSelect()
+ {
+ var firstImport = importBeatmap(1);
+ presentAndConfirm(firstImport);
+
+ var secondimport = importBeatmap(2);
+ presentAndConfirm(secondimport);
+ }
+
+ [Test]
+ public void TestFromSongSelectDifferentRuleset()
+ {
+ var firstImport = importBeatmap(1);
+ presentAndConfirm(firstImport);
+
+ var secondimport = importBeatmap(2, new ManiaRuleset().RulesetInfo);
+ presentAndConfirm(secondimport);
+ }
+
+ private Func importBeatmap(int i, RulesetInfo ruleset = null)
+ {
+ BeatmapSetInfo imported = null;
+ AddStep($"import beatmap {i}", () =>
+ {
+ var difficulty = new BeatmapDifficulty();
+ var metadata = new BeatmapMetadata
+ {
+ Artist = "SomeArtist",
+ AuthorString = "SomeAuthor",
+ Title = $"import {i}"
+ };
+
+ imported = Game.BeatmapManager.Import(new BeatmapSetInfo
+ {
+ Hash = Guid.NewGuid().ToString(),
+ OnlineBeatmapSetID = i,
+ Metadata = metadata,
+ Beatmaps = new List
+ {
+ new BeatmapInfo
+ {
+ OnlineBeatmapID = i * 1024,
+ Metadata = metadata,
+ BaseDifficulty = difficulty,
+ Ruleset = ruleset ?? new OsuRuleset().RulesetInfo
+ },
+ }
+ }).Result;
+ });
+
+ AddAssert($"import {i} succeeded", () => imported != null);
+
+ return () => imported;
+ }
+
+ private void presentAndConfirm(Func getImport)
+ {
+ AddStep("present beatmap", () => Game.PresentBeatmap(getImport()));
+
+ AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect);
+ AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapSetInfo.ID == getImport().ID);
+ AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
similarity index 50%
rename from osu.Game.Tests/Visual/Menus/TestSceneScreenNavigation.cs
rename to osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
index 471f67b7b6..8258cc9465 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneScreenNavigation.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
@@ -1,90 +1,36 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Platform;
-using osu.Framework.Screens;
-using osu.Framework.Testing;
using osu.Game.Beatmaps;
-using osu.Game.Configuration;
-using osu.Game.Graphics.UserInterface;
-using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
-using osu.Game.Screens;
-using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
-using osuTK.Graphics;
using osuTK.Input;
-using IntroSequence = osu.Game.Configuration.IntroSequence;
-namespace osu.Game.Tests.Visual.Menus
+namespace osu.Game.Tests.Visual.Navigation
{
- public class TestSceneScreenNavigation : ManualInputManagerTestScene
+ public class TestSceneScreenNavigation : OsuGameTestScene
{
private const float click_padding = 25;
- private GameHost host;
- private TestOsuGame game;
+ private Vector2 backButtonPosition => Game.ToScreenSpace(new Vector2(click_padding, Game.LayoutRectangle.Bottom - click_padding));
- private Vector2 backButtonPosition => game.ToScreenSpace(new Vector2(click_padding, game.LayoutRectangle.Bottom - click_padding));
-
- private Vector2 optionsButtonPosition => game.ToScreenSpace(new Vector2(click_padding, click_padding));
-
- [BackgroundDependencyLoader]
- private void load(GameHost host)
- {
- this.host = host;
-
- Child = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- };
- }
-
- [SetUpSteps]
- public void SetUpSteps()
- {
- AddStep("Create new game instance", () =>
- {
- if (game != null)
- {
- Remove(game);
- game.Dispose();
- }
-
- game = new TestOsuGame(LocalStorage, API);
- game.SetHost(host);
-
- // todo: this can be removed once we can run audio trakcs without a device present
- // see https://github.com/ppy/osu/issues/1302
- game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
-
- Add(game);
- });
- AddUntilStep("Wait for load", () => game.IsLoaded);
- AddUntilStep("Wait for intro", () => game.ScreenStack.CurrentScreen is IntroScreen);
- confirmAtMainMenu();
- }
+ private Vector2 optionsButtonPosition => Game.ToScreenSpace(new Vector2(click_padding, click_padding));
[Test]
public void TestExitSongSelectWithEscape()
{
TestSongSelect songSelect = null;
- pushAndConfirm(() => songSelect = new TestSongSelect());
+ PushAndConfirm(() => songSelect = new TestSongSelect());
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
pushEscape();
@@ -98,21 +44,21 @@ namespace osu.Game.Tests.Visual.Menus
{
Player player = null;
- WorkingBeatmap beatmap() => game.Beatmap.Value;
+ WorkingBeatmap beatmap() => Game.Beatmap.Value;
Track track() => beatmap().Track;
- pushAndConfirm(() => new TestSongSelect());
+ PushAndConfirm(() => new TestSongSelect());
- AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Wait());
+ AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait());
- AddUntilStep("wait for selected", () => !game.Beatmap.IsDefault);
+ AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
if (withUserPause)
- AddStep("pause", () => game.Dependencies.Get().Stop());
+ AddStep("pause", () => Game.Dependencies.Get().Stop());
AddStep("press enter", () => pressAndRelease(Key.Enter));
- AddUntilStep("wait for player", () => (player = game.ScreenStack.CurrentScreen as Player) != null);
+ AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
AddUntilStep("wait for fail", () => player.HasFailed);
AddUntilStep("wait for track stop", () => !track().IsRunning);
@@ -129,13 +75,13 @@ namespace osu.Game.Tests.Visual.Menus
{
TestSongSelect songSelect = null;
- pushAndConfirm(() => songSelect = new TestSongSelect());
+ PushAndConfirm(() => songSelect = new TestSongSelect());
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition));
// BackButton handles hover using its child button, so this checks whether or not any of BackButton's children are hovered.
- AddUntilStep("Back button is hovered", () => InputManager.HoveredDrawables.Any(d => d.Parent == game.BackButton));
+ AddUntilStep("Back button is hovered", () => InputManager.HoveredDrawables.Any(d => d.Parent == Game.BackButton));
AddStep("Click back button", () => InputManager.Click(MouseButton.Left));
AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
@@ -145,34 +91,27 @@ namespace osu.Game.Tests.Visual.Menus
[Test]
public void TestExitMultiWithEscape()
{
- pushAndConfirm(() => new Screens.Multi.Multiplayer());
+ PushAndConfirm(() => new Screens.Multi.Multiplayer());
exitViaEscapeAndConfirm();
}
[Test]
public void TestExitMultiWithBackButton()
{
- pushAndConfirm(() => new Screens.Multi.Multiplayer());
+ PushAndConfirm(() => new Screens.Multi.Multiplayer());
exitViaBackButtonAndConfirm();
}
[Test]
public void TestOpenOptionsAndExitWithEscape()
{
- AddUntilStep("Wait for options to load", () => game.Settings.IsLoaded);
+ AddUntilStep("Wait for options to load", () => Game.Settings.IsLoaded);
AddStep("Enter menu", () => pressAndRelease(Key.Enter));
AddStep("Move mouse to options overlay", () => InputManager.MoveMouseTo(optionsButtonPosition));
AddStep("Click options overlay", () => InputManager.Click(MouseButton.Left));
- AddAssert("Options overlay was opened", () => game.Settings.State.Value == Visibility.Visible);
+ AddAssert("Options overlay was opened", () => Game.Settings.State.Value == Visibility.Visible);
AddStep("Hide options overlay using escape", () => pressAndRelease(Key.Escape));
- AddAssert("Options overlay was closed", () => game.Settings.State.Value == Visibility.Hidden);
- }
-
- private void pushAndConfirm(Func newScreen)
- {
- Screen screen = null;
- AddStep("Push new screen", () => game.ScreenStack.Push(screen = newScreen()));
- AddUntilStep("Wait for new screen", () => game.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
+ AddAssert("Options overlay was closed", () => Game.Settings.State.Value == Visibility.Hidden);
}
private void pushEscape() =>
@@ -181,64 +120,25 @@ namespace osu.Game.Tests.Visual.Menus
private void exitViaEscapeAndConfirm()
{
pushEscape();
- confirmAtMainMenu();
+ ConfirmAtMainMenu();
}
private void exitViaBackButtonAndConfirm()
{
AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition));
AddStep("Click back button", () => InputManager.Click(MouseButton.Left));
- confirmAtMainMenu();
+ ConfirmAtMainMenu();
}
- private void confirmAtMainMenu() => AddUntilStep("Wait for main menu", () => game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
-
private void pressAndRelease(Key key)
{
InputManager.PressKey(key);
InputManager.ReleaseKey(key);
}
- private class TestOsuGame : OsuGame
- {
- public new ScreenStack ScreenStack => base.ScreenStack;
-
- public new BackButton BackButton => base.BackButton;
-
- public new SettingsPanel Settings => base.Settings;
-
- public new OsuConfigManager LocalConfig => base.LocalConfig;
-
- public new Bindable Beatmap => base.Beatmap;
-
- protected override Loader CreateLoader() => new TestLoader();
-
- public TestOsuGame(Storage storage, IAPIProvider api)
- {
- Storage = storage;
- API = api;
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- API.Login("Rhythm Champion", "osu!");
- }
- }
-
private class TestSongSelect : PlaySongSelect
{
public ModSelectOverlay ModSelectOverlay => ModSelect;
}
-
- private class TestLoader : Loader
- {
- protected override ShaderPrecompiler CreateShaderPrecompiler() => new TestShaderPrecompiler();
-
- private class TestShaderPrecompiler : ShaderPrecompiler
- {
- protected override bool AllLoaded => true;
- }
- }
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
index 658f678b10..7a8570c09b 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneChangelogOverlay : OsuTestScene
{
- private ChangelogOverlay changelog;
+ private TestChangelogOverlay changelog;
public override IReadOnlyList RequiredTypes => new[]
{
@@ -29,23 +29,40 @@ namespace osu.Game.Tests.Visual.Online
protected override bool UseOnlineAPI => true;
- protected override void LoadComplete()
+ [SetUp]
+ public void SetUp() => Schedule(() =>
{
- base.LoadComplete();
+ Child = changelog = new TestChangelogOverlay();
+ });
- Add(changelog = new ChangelogOverlay());
- AddStep(@"Show", changelog.Show);
- AddStep(@"Hide", changelog.Hide);
+ [Test]
+ public void ShowWithNoFetch()
+ {
+ AddStep(@"Show", () => changelog.Show());
+ AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0);
+ AddAssert(@"listing displayed", () => changelog.Current.Value == null);
+ AddAssert(@"no stream selected", () => changelog.Header.Streams.Current.Value == null);
+ }
- AddWaitStep("wait for hide", 3);
+ [Test]
+ public void ShowWithListing()
+ {
+ AddStep(@"Show with listing", () => changelog.ShowListing());
+ AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0);
+ AddAssert(@"listing displayed", () => changelog.Current.Value == null);
+ AddAssert(@"no stream selected", () => changelog.Header.Streams.Current.Value == null);
+ }
+ [Test]
+ public void ShowWithBuild()
+ {
AddStep(@"Show with Lazer 2018.712.0", () =>
{
changelog.ShowBuild(new APIChangelogBuild
{
Version = "2018.712.0",
DisplayVersion = "2018.712.0",
- UpdateStream = new APIUpdateStream { Name = OsuGameBase.CLIENT_STREAM_NAME },
+ UpdateStream = new APIUpdateStream { Id = 7, Name = OsuGameBase.CLIENT_STREAM_NAME },
ChangelogEntries = new List
{
new APIChangelogEntry
@@ -56,19 +73,16 @@ namespace osu.Game.Tests.Visual.Online
}
}
});
- changelog.Show();
});
- AddWaitStep("wait for show", 3);
- AddStep(@"Hide", changelog.Hide);
- AddWaitStep("wait for hide", 3);
-
- AddStep(@"Show with listing", () =>
- {
- changelog.ShowListing();
- changelog.Show();
- });
+ AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0);
+ AddAssert(@"correct build displayed", () => changelog.Current.Value.Version == "2018.712.0");
+ AddAssert(@"correct stream selected", () => changelog.Header.Streams.Current.Value.Id == 7);
+ }
+ [Test]
+ public void TestHTMLUnescaping()
+ {
AddStep(@"Ensure HTML string unescaping", () =>
{
changelog.ShowBuild(new APIChangelogBuild
@@ -97,5 +111,12 @@ namespace osu.Game.Tests.Visual.Online
});
});
}
+
+ private class TestChangelogOverlay : ChangelogOverlay
+ {
+ public new List Streams => base.Streams;
+
+ public new ChangelogHeader Header => base.Header;
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs
index c98b65ded7..1fb3f4ba45 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChannelTabControl.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -25,7 +26,7 @@ namespace osu.Game.Tests.Visual.Online
typeof(ChannelTabControl),
};
- private readonly ChannelTabControl channelTabControl;
+ private readonly TestTabControl channelTabControl;
public TestSceneChannelTabControl()
{
@@ -37,7 +38,7 @@ namespace osu.Game.Tests.Visual.Online
Anchor = Anchor.Centre,
Children = new Drawable[]
{
- channelTabControl = new ChannelTabControl
+ channelTabControl = new TestTabControl
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.Centre,
@@ -73,32 +74,40 @@ namespace osu.Game.Tests.Visual.Online
channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.NewValue;
AddStep("Add random private channel", addRandomPrivateChannel);
- AddAssert("There is only one channels", () => channelTabControl.Items.Count() == 2);
+ AddAssert("There is only one channels", () => channelTabControl.Items.Count == 2);
AddRepeatStep("Add 3 random private channels", addRandomPrivateChannel, 3);
- AddAssert("There are four channels", () => channelTabControl.Items.Count() == 5);
+ AddAssert("There are four channels", () => channelTabControl.Items.Count == 5);
AddStep("Add random public channel", () => addChannel(RNG.Next().ToString()));
- AddRepeatStep("Select a random channel", () => channelTabControl.Current.Value = channelTabControl.Items.ElementAt(RNG.Next(channelTabControl.Items.Count() - 1)), 20);
+ AddRepeatStep("Select a random channel", () =>
+ {
+ List validChannels = channelTabControl.Items.Where(c => !(c is ChannelSelectorTabItem.ChannelSelectorTabChannel)).ToList();
+ channelTabControl.SelectChannel(validChannels[RNG.Next(0, validChannels.Count)]);
+ }, 20);
- Channel channelBefore = channelTabControl.Items.First();
- AddStep("set first channel", () => channelTabControl.Current.Value = channelBefore);
+ Channel channelBefore = null;
+ AddStep("set first channel", () => channelTabControl.SelectChannel(channelBefore = channelTabControl.Items.First(c => !(c is ChannelSelectorTabItem.ChannelSelectorTabChannel))));
- AddStep("select selector tab", () => channelTabControl.Current.Value = channelTabControl.Items.Last());
+ AddStep("select selector tab", () => channelTabControl.SelectChannel(channelTabControl.Items.Single(c => c is ChannelSelectorTabItem.ChannelSelectorTabChannel)));
AddAssert("selector tab is active", () => channelTabControl.ChannelSelectorActive.Value);
AddAssert("check channel unchanged", () => channelBefore == channelTabControl.Current.Value);
- AddStep("set second channel", () => channelTabControl.Current.Value = channelTabControl.Items.Skip(1).First());
+ AddStep("set second channel", () => channelTabControl.SelectChannel(channelTabControl.Items.GetNext(channelBefore)));
AddAssert("selector tab is inactive", () => !channelTabControl.ChannelSelectorActive.Value);
AddUntilStep("remove all channels", () =>
{
- var first = channelTabControl.Items.First();
- if (first is ChannelSelectorTabItem.ChannelSelectorTabChannel)
- return true;
+ foreach (var item in channelTabControl.Items.ToList())
+ {
+ if (item is ChannelSelectorTabItem.ChannelSelectorTabChannel)
+ continue;
- channelTabControl.RemoveChannel(first);
- return false;
+ channelTabControl.RemoveChannel(item);
+ return false;
+ }
+
+ return true;
});
AddAssert("selector tab is active", () => channelTabControl.ChannelSelectorActive.Value);
@@ -117,5 +126,10 @@ namespace osu.Game.Tests.Visual.Online
Type = ChannelType.Public,
Name = name
});
+
+ private class TestTabControl : ChannelTabControl
+ {
+ public void SelectChannel(Channel channel) => base.SelectTab(TabMap[channel]);
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
index 9196513a55..19bdaff6ff 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
@@ -3,12 +3,15 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Testing;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat;
using osu.Game.Overlays;
using osu.Game.Overlays.Chat;
@@ -35,8 +38,21 @@ namespace osu.Game.Tests.Visual.Online
private TestChatOverlay chatOverlay;
private ChannelManager channelManager;
- private readonly Channel channel1 = new Channel(new User()) { Name = "test really long username" };
- private readonly Channel channel2 = new Channel(new User()) { Name = "test2" };
+ private readonly List channels;
+
+ private Channel channel1 => channels[0];
+ private Channel channel2 => channels[1];
+
+ public TestSceneChatOverlay()
+ {
+ channels = Enumerable.Range(1, 10)
+ .Select(index => new Channel(new User())
+ {
+ Name = $"Channel no. {index}",
+ Topic = index == 3 ? null : $"We talk about the number {index} here"
+ })
+ .ToList();
+ }
[SetUp]
public void Setup()
@@ -45,7 +61,7 @@ namespace osu.Game.Tests.Visual.Online
{
ChannelManagerContainer container;
- Child = container = new ChannelManagerContainer(new List { channel1, channel2 })
+ Child = container = new ChannelManagerContainer(channels)
{
RelativeSizeAxes = Axes.Both,
};
@@ -96,6 +112,47 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
}
+ [Test]
+ public void TestSearchInSelector()
+ {
+ AddStep("search for 'no. 2'", () => chatOverlay.ChildrenOfType().First().Text = "no. 2");
+ AddUntilStep("only channel 2 visible", () =>
+ {
+ var listItems = chatOverlay.ChildrenOfType().Where(c => c.IsPresent);
+ return listItems.Count() == 1 && listItems.Single().Channel == channel2;
+ });
+ }
+
+ [Test]
+ public void TestChannelShortcutKeys()
+ {
+ AddStep("join 10 channels", () => channels.ForEach(channel => channelManager.JoinChannel(channel)));
+ AddStep("close channel selector", () =>
+ {
+ InputManager.PressKey(Key.Escape);
+ InputManager.ReleaseKey(Key.Escape);
+ });
+ AddUntilStep("wait for close", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
+
+ for (int zeroBasedIndex = 0; zeroBasedIndex < 10; ++zeroBasedIndex)
+ {
+ var oneBasedIndex = zeroBasedIndex + 1;
+ var targetNumberKey = oneBasedIndex % 10;
+ var targetChannel = channels[zeroBasedIndex];
+ AddStep($"press Alt+{targetNumberKey}", () => pressChannelHotkey(targetNumberKey));
+ AddAssert($"channel #{oneBasedIndex} is selected", () => channelManager.CurrentChannel.Value == targetChannel);
+ }
+ }
+
+ private void pressChannelHotkey(int number)
+ {
+ var channelKey = Key.Number0 + number;
+ InputManager.PressKey(Key.AltLeft);
+ InputManager.PressKey(channelKey);
+ InputManager.ReleaseKey(Key.AltLeft);
+ InputManager.ReleaseKey(channelKey);
+ }
+
private void clickDrawable(Drawable d)
{
InputManager.MoveMouseTo(d);
diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
index 86bd0ddd11..9c526c4f81 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
@@ -8,6 +8,8 @@ using osu.Game.Online.API.Requests;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Game.Overlays.Comments;
+using osu.Game.Overlays;
+using osu.Framework.Allocation;
namespace osu.Game.Tests.Visual.Online
{
@@ -22,37 +24,35 @@ namespace osu.Game.Tests.Visual.Online
typeof(HeaderButton),
typeof(SortTabControl),
typeof(ShowChildrenButton),
- typeof(DeletedChildrenPlaceholder),
- typeof(VotePill)
+ typeof(DeletedCommentsCounter),
+ typeof(VotePill),
+ typeof(CommentsPage),
};
protected override bool UseOnlineAPI => true;
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
+
public TestSceneCommentsContainer()
{
- BasicScrollContainer scrollFlow;
+ BasicScrollContainer scroll;
+ CommentsContainer comments;
- Add(scrollFlow = new BasicScrollContainer
+ Add(scroll = new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
+ Child = comments = new CommentsContainer()
});
- AddStep("Big Black comments", () =>
+ AddStep("Big Black comments", () => comments.ShowComments(CommentableType.Beatmapset, 41823));
+ AddStep("Airman comments", () => comments.ShowComments(CommentableType.Beatmapset, 24313));
+ AddStep("Lazer build comments", () => comments.ShowComments(CommentableType.Build, 4772));
+ AddStep("News comments", () => comments.ShowComments(CommentableType.NewsPost, 715));
+ AddStep("Idle state", () =>
{
- scrollFlow.Clear();
- scrollFlow.Add(new CommentsContainer(CommentableType.Beatmapset, 41823));
- });
-
- AddStep("Airman comments", () =>
- {
- scrollFlow.Clear();
- scrollFlow.Add(new CommentsContainer(CommentableType.Beatmapset, 24313));
- });
-
- AddStep("lazer build comments", () =>
- {
- scrollFlow.Clear();
- scrollFlow.Add(new CommentsContainer(CommentableType.Build, 4772));
+ scroll.Clear();
+ scroll.Add(comments = new CommentsContainer());
});
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsHeader.cs
index bc3e0eff1a..a60f220e4b 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneCommentsHeader.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsHeader.cs
@@ -4,7 +4,9 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Game.Overlays;
using osu.Game.Overlays.Comments;
namespace osu.Game.Tests.Visual.Online
@@ -19,6 +21,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(SortTabControl),
};
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
+
private readonly Bindable sort = new Bindable();
private readonly BindableBool showDeleted = new BindableBool();
diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs
new file mode 100644
index 0000000000..1217ce6b42
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs
@@ -0,0 +1,162 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Game.Overlays.Comments;
+using osu.Game.Overlays;
+using osu.Framework.Allocation;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Users;
+using osu.Game.Graphics.UserInterface;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneCommentsPage : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DrawableComment),
+ typeof(CommentsPage),
+ };
+
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
+
+ private readonly BindableBool showDeleted = new BindableBool();
+ private readonly Container content;
+
+ public TestSceneCommentsPage()
+ {
+ Add(new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 10),
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ AutoSizeAxes = Axes.Y,
+ Width = 200,
+ Child = new OsuCheckbox
+ {
+ Current = showDeleted,
+ LabelText = @"Show Deleted"
+ }
+ },
+ content = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ }
+ }
+ });
+
+ AddStep("load comments", () => createPage(comment_bundle));
+ AddStep("load empty comments", () => createPage(empty_comment_bundle));
+ }
+
+ private void createPage(CommentBundle commentBundle)
+ {
+ content.Clear();
+ content.Add(new CommentsPage(commentBundle)
+ {
+ ShowDeleted = { BindTarget = showDeleted }
+ });
+ }
+
+ private static readonly CommentBundle empty_comment_bundle = new CommentBundle
+ {
+ Comments = new List(),
+ Total = 0,
+ };
+
+ private static readonly CommentBundle comment_bundle = new CommentBundle
+ {
+ Comments = new List
+ {
+ new Comment
+ {
+ Id = 1,
+ Message = "Simple test comment",
+ LegacyName = "TestUser1",
+ CreatedAt = DateTimeOffset.Now,
+ VotesCount = 5
+ },
+ new Comment
+ {
+ Id = 2,
+ Message = "This comment has been deleted :( but visible for admins",
+ LegacyName = "TestUser2",
+ CreatedAt = DateTimeOffset.Now,
+ DeletedAt = DateTimeOffset.Now,
+ VotesCount = 5
+ },
+ new Comment
+ {
+ Id = 3,
+ Message = "This comment is a top level",
+ LegacyName = "TestUser3",
+ CreatedAt = DateTimeOffset.Now,
+ RepliesCount = 2,
+ },
+ new Comment
+ {
+ Id = 4,
+ ParentId = 3,
+ Message = "And this is a reply",
+ RepliesCount = 1,
+ LegacyName = "TestUser1",
+ CreatedAt = DateTimeOffset.Now,
+ },
+ new Comment
+ {
+ Id = 15,
+ ParentId = 4,
+ Message = "Reply to reply",
+ LegacyName = "TestUser1",
+ CreatedAt = DateTimeOffset.Now,
+ },
+ new Comment
+ {
+ Id = 6,
+ ParentId = 3,
+ LegacyName = "TestUser11515",
+ CreatedAt = DateTimeOffset.Now,
+ DeletedAt = DateTimeOffset.Now,
+ },
+ new Comment
+ {
+ Id = 5,
+ Message = "This comment is voted and edited",
+ LegacyName = "BigBrainUser",
+ CreatedAt = DateTimeOffset.Now,
+ EditedAt = DateTimeOffset.Now,
+ VotesCount = 1000,
+ EditedById = 1,
+ }
+ },
+ IncludedComments = new List(),
+ UserVotes = new List
+ {
+ 5
+ },
+ Users = new List
+ {
+ new User
+ {
+ Id = 1,
+ Username = "Good_Admin"
+ }
+ },
+ TopLevelCount = 4,
+ Total = 7
+ };
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs
index fe8437be17..e60adcee34 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs
@@ -41,6 +41,7 @@ namespace osu.Game.Tests.Visual.Online
private class TestFullscreenOverlay : FullscreenOverlay
{
public TestFullscreenOverlay()
+ : base(OverlayColourScheme.Pink)
{
Children = new Drawable[]
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
index d3b037f499..d098ea8b16 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs
@@ -4,10 +4,12 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
+using osu.Game.Overlays;
using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Historical;
using osu.Game.Users;
@@ -24,9 +26,11 @@ namespace osu.Game.Tests.Visual.Online
typeof(HistoricalSection),
typeof(PaginatedMostPlayedBeatmapContainer),
typeof(DrawableMostPlayedBeatmap),
- typeof(DrawableProfileRow)
};
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
+
public TestSceneHistoricalSection()
{
HistoricalSection section;
diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs
index 1f5ba67e03..826624f686 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs
@@ -11,6 +11,8 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Users;
using osu.Framework.Bindables;
+using osu.Game.Overlays;
+using osu.Framework.Allocation;
namespace osu.Game.Tests.Visual.Online
{
@@ -22,10 +24,13 @@ namespace osu.Game.Tests.Visual.Online
typeof(ProfileRulesetTabItem),
};
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
+
public TestSceneProfileRulesetSelector()
{
ProfileRulesetSelector selector;
- Bindable user = new Bindable();
+ var user = new Bindable();
Child = selector = new ProfileRulesetSelector
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs
index c70cc4ae4e..8f7e7498a9 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs
@@ -4,11 +4,13 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Users;
using osuTK;
@@ -24,6 +26,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(LineGraph)
};
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
+
public TestSceneRankGraph()
{
RankGraph graph;
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs
new file mode 100644
index 0000000000..7ac65181f9
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs
@@ -0,0 +1,76 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Overlays.Rankings;
+using osu.Game.Users;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shapes;
+using osuTK.Graphics;
+using osu.Game.Graphics.Sprites;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsCountryFilter : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(CountryFilter),
+ typeof(CountryPill)
+ };
+
+ public TestSceneRankingsCountryFilter()
+ {
+ var countryBindable = new Bindable();
+
+ AddRange(new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Gray,
+ },
+ new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new CountryFilter
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Current = { BindTarget = countryBindable }
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = "Some content",
+ Margin = new MarginPadding { Vertical = 20 }
+ }
+ }
+ }
+ });
+
+ var country = new Country
+ {
+ FlagName = "BY",
+ FullName = "Belarus"
+ };
+ var unknownCountry = new Country
+ {
+ FlagName = "CK",
+ FullName = "Cook Islands"
+ };
+
+ AddStep("Set country", () => countryBindable.Value = country);
+ AddStep("Set null country", () => countryBindable.Value = null);
+ AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs b/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs
index f14c75084f..8ecbf0891b 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneTotalCommentsCounter.cs
@@ -7,6 +7,8 @@ using osu.Framework.Graphics;
using osu.Framework.Bindables;
using osu.Game.Overlays.Comments;
using osu.Framework.Utils;
+using osu.Framework.Allocation;
+using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Online
{
@@ -17,6 +19,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(TotalCommentsCounter),
};
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
+
public TestSceneTotalCommentsCounter()
{
var count = new BindableInt();
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
index 63b46c991f..523de4e38f 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs
@@ -24,13 +24,16 @@ namespace osu.Game.Tests.Visual.Online
typeof(ProfileHeader),
typeof(RankGraph),
typeof(LineGraph),
- typeof(TabControlOverlayHeader.OverlayHeaderTabControl),
+ typeof(TabControlOverlayHeader<>.OverlayHeaderTabControl),
typeof(CentreHeaderContainer),
typeof(BottomHeaderContainer),
typeof(DetailHeaderContainer),
typeof(ProfileHeaderButton)
};
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
+
[Resolved]
private IAPIProvider api { get; set; }
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs
index f022425bf6..06091f3c81 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileRecentSection.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -12,6 +13,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Overlays;
using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Recent;
@@ -28,6 +30,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(MedalIcon)
};
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
+
public TestSceneUserProfileRecentSection()
{
Children = new Drawable[]
@@ -131,6 +136,22 @@ namespace osu.Game.Tests.Visual.Online
Beatmap = dummyBeatmap,
},
new APIRecentActivity
+ {
+ User = dummyUser,
+ Type = RecentActivityType.Rank,
+ Rank = 1,
+ Mode = "vitaru",
+ Beatmap = dummyBeatmap,
+ },
+ new APIRecentActivity
+ {
+ User = dummyUser,
+ Type = RecentActivityType.Rank,
+ Rank = 1,
+ Mode = "fruits",
+ Beatmap = dummyBeatmap,
+ },
+ new APIRecentActivity
{
User = dummyUser,
Type = RecentActivityType.RankLost,
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs
new file mode 100644
index 0000000000..19b72e7071
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs
@@ -0,0 +1,101 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Game.Overlays.Profile.Sections;
+using osu.Game.Overlays.Profile.Sections.Ranks;
+using osu.Framework.Graphics;
+using osu.Game.Scoring;
+using osu.Framework.Graphics.Containers;
+using osuTK;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Overlays;
+using osu.Framework.Allocation;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneUserProfileScores : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DrawableProfileScore),
+ typeof(DrawableProfileWeightedScore),
+ typeof(ProfileItemContainer),
+ };
+
+ public TestSceneUserProfileScores()
+ {
+ var score = new ScoreInfo
+ {
+ PP = 134.32,
+ Rank = ScoreRank.A,
+ Beatmap = new BeatmapInfo
+ {
+ Metadata = new BeatmapMetadata
+ {
+ Title = "Triumph & Regret",
+ Artist = "typeMARS"
+ },
+ Version = "[4K] Regret"
+ },
+ Date = DateTimeOffset.Now,
+ Mods = new Mod[]
+ {
+ new OsuModHardRock(),
+ new OsuModDoubleTime(),
+ },
+ Accuracy = 0.998546
+ };
+
+ var noPPScore = new ScoreInfo
+ {
+ Rank = ScoreRank.B,
+ Beatmap = new BeatmapInfo
+ {
+ Metadata = new BeatmapMetadata
+ {
+ Title = "C18H27NO3(extend)",
+ Artist = "Team Grimoire"
+ },
+ Version = "[4K] Cataclysmic Hypernova"
+ },
+ Date = DateTimeOffset.Now,
+ Accuracy = 0.55879
+ };
+
+ Add(new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 10),
+ Children = new[]
+ {
+ new ColourProvidedContainer(OverlayColourScheme.Green, new DrawableProfileScore(score)),
+ new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileScore(noPPScore)),
+ new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileWeightedScore(score, 0.85))
+ }
+ });
+ }
+
+ private class ColourProvidedContainer : Container
+ {
+ [Cached]
+ private readonly OverlayColourProvider colourProvider;
+
+ public ColourProvidedContainer(OverlayColourScheme colourScheme, DrawableProfileScore score)
+ {
+ colourProvider = new OverlayColourProvider(colourScheme);
+
+ AutoSizeAxes = Axes.Y;
+ RelativeSizeAxes = Axes.X;
+ Add(score);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
index 2951f6b63e..c8e94b2915 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs
@@ -4,11 +4,13 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
+using osu.Game.Overlays;
using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Users;
@@ -20,7 +22,15 @@ namespace osu.Game.Tests.Visual.Online
{
protected override bool UseOnlineAPI => true;
- public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) };
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DrawableProfileScore),
+ typeof(DrawableProfileWeightedScore),
+ typeof(RanksSection)
+ };
+
+ [Cached]
+ private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
public TestSceneUserRanks()
{
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
new file mode 100644
index 0000000000..3d3517ada4
--- /dev/null
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
@@ -0,0 +1,175 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Screens.Select.Details;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Visual.SongSelect
+{
+ [System.ComponentModel.Description("Advanced beatmap statistics display")]
+ public class TestSceneAdvancedStats : OsuTestScene
+ {
+ private TestAdvancedStats advancedStats;
+
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
+
+ [Resolved]
+ private OsuColour colours { get; set; }
+
+ [SetUp]
+ public void Setup() => Schedule(() => Child = advancedStats = new TestAdvancedStats
+ {
+ Width = 500
+ });
+
+ private BeatmapInfo exampleBeatmapInfo => new BeatmapInfo
+ {
+ RulesetID = 0,
+ Ruleset = rulesets.AvailableRulesets.First(),
+ BaseDifficulty = new BeatmapDifficulty
+ {
+ CircleSize = 7.2f,
+ DrainRate = 3,
+ OverallDifficulty = 5.7f,
+ ApproachRate = 3.5f
+ },
+ StarDifficulty = 4.5f
+ };
+
+ [Test]
+ public void TestNoMod()
+ {
+ AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
+
+ AddStep("no mods selected", () => SelectedMods.Value = Array.Empty());
+
+ AddAssert("first bar text is Circle Size", () => advancedStats.ChildrenOfType().First().Text == "Circle Size");
+ AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
+ AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.HpDrain));
+ AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy));
+ AddAssert("approach rate bar is white", () => barIsWhite(advancedStats.ApproachRate));
+ }
+
+ [Test]
+ public void TestManiaFirstBarText()
+ {
+ AddStep("set beatmap", () => advancedStats.Beatmap = new BeatmapInfo
+ {
+ Ruleset = rulesets.GetRuleset(3),
+ BaseDifficulty = new BeatmapDifficulty
+ {
+ CircleSize = 5,
+ DrainRate = 4.3f,
+ OverallDifficulty = 4.5f,
+ ApproachRate = 3.1f
+ },
+ StarDifficulty = 8
+ });
+
+ AddAssert("first bar text is Key Count", () => advancedStats.ChildrenOfType().First().Text == "Key Count");
+ }
+
+ [Test]
+ public void TestEasyMod()
+ {
+ AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
+
+ AddStep("select EZ mod", () =>
+ {
+ var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
+ SelectedMods.Value = new[] { ruleset.GetAllMods().OfType().Single() };
+ });
+
+ AddAssert("circle size bar is blue", () => barIsBlue(advancedStats.FirstValue));
+ AddAssert("HP drain bar is blue", () => barIsBlue(advancedStats.HpDrain));
+ AddAssert("accuracy bar is blue", () => barIsBlue(advancedStats.Accuracy));
+ AddAssert("approach rate bar is blue", () => barIsBlue(advancedStats.ApproachRate));
+ }
+
+ [Test]
+ public void TestHardRockMod()
+ {
+ AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
+
+ AddStep("select HR mod", () =>
+ {
+ var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
+ SelectedMods.Value = new[] { ruleset.GetAllMods().OfType().Single() };
+ });
+
+ AddAssert("circle size bar is red", () => barIsRed(advancedStats.FirstValue));
+ AddAssert("HP drain bar is red", () => barIsRed(advancedStats.HpDrain));
+ AddAssert("accuracy bar is red", () => barIsRed(advancedStats.Accuracy));
+ AddAssert("approach rate bar is red", () => barIsRed(advancedStats.ApproachRate));
+ }
+
+ [Test]
+ public void TestUnchangedDifficultyAdjustMod()
+ {
+ AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
+
+ AddStep("select unchanged Difficulty Adjust mod", () =>
+ {
+ var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
+ var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single();
+ difficultyAdjustMod.ReadFromDifficulty(advancedStats.Beatmap.BaseDifficulty);
+ SelectedMods.Value = new[] { difficultyAdjustMod };
+ });
+
+ AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
+ AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.HpDrain));
+ AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy));
+ AddAssert("approach rate bar is white", () => barIsWhite(advancedStats.ApproachRate));
+ }
+
+ [Test]
+ public void TestChangedDifficultyAdjustMod()
+ {
+ AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
+
+ AddStep("select changed Difficulty Adjust mod", () =>
+ {
+ var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
+ var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single();
+ var originalDifficulty = advancedStats.Beatmap.BaseDifficulty;
+ var adjustedDifficulty = new BeatmapDifficulty
+ {
+ CircleSize = originalDifficulty.CircleSize,
+ DrainRate = originalDifficulty.DrainRate - 0.5f,
+ OverallDifficulty = originalDifficulty.OverallDifficulty,
+ ApproachRate = originalDifficulty.ApproachRate + 2.2f,
+ };
+ difficultyAdjustMod.ReadFromDifficulty(adjustedDifficulty);
+ SelectedMods.Value = new[] { difficultyAdjustMod };
+ });
+
+ AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
+ AddAssert("drain rate bar is blue", () => barIsBlue(advancedStats.HpDrain));
+ AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy));
+ AddAssert("approach rate bar is red", () => barIsRed(advancedStats.ApproachRate));
+ }
+
+ private bool barIsWhite(AdvancedStats.StatisticRow row) => row.ModBar.AccentColour == Color4.White;
+ private bool barIsBlue(AdvancedStats.StatisticRow row) => row.ModBar.AccentColour == colours.BlueDark;
+ private bool barIsRed(AdvancedStats.StatisticRow row) => row.ModBar.AccentColour == colours.Red;
+
+ private class TestAdvancedStats : AdvancedStats
+ {
+ public new StatisticRow FirstValue => base.FirstValue;
+ public new StatisticRow HpDrain => base.HpDrain;
+ public new StatisticRow Accuracy => base.Accuracy;
+ public new StatisticRow ApproachRate => base.ApproachRate;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
index 132b104afb..71ae47dc66 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
@@ -437,6 +437,53 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1);
}
+ [Test]
+ public void TestFilteringByUserStarDifficulty()
+ {
+ BeatmapSetInfo set = null;
+
+ loadBeatmaps(new List());
+
+ AddStep("add mixed difficulty set", () =>
+ {
+ set = createTestBeatmapSet(1);
+ set.Beatmaps.Clear();
+
+ for (int i = 1; i <= 15; i++)
+ {
+ set.Beatmaps.Add(new BeatmapInfo
+ {
+ Version = $"Stars: {i}",
+ StarDifficulty = i,
+ });
+ }
+
+ carousel.UpdateBeatmapSet(set);
+ });
+
+ AddStep("select added set", () => carousel.SelectBeatmap(set.Beatmaps[0], false));
+
+ AddStep("filter [5..]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 5 } }));
+ AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask);
+ checkVisibleItemCount(true, 11);
+
+ AddStep("filter to [0..7]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Max = 7 } }));
+ AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask);
+ checkVisibleItemCount(true, 7);
+
+ AddStep("filter to [5..7]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 5, Max = 7 } }));
+ AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask);
+ checkVisibleItemCount(true, 3);
+
+ AddStep("filter [2..2]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 2, Max = 2 } }));
+ AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask);
+ checkVisibleItemCount(true, 1);
+
+ AddStep("filter to [0..]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 0 } }));
+ AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask);
+ checkVisibleItemCount(true, 15);
+ }
+
private void loadBeatmaps(List beatmapSets = null)
{
createCarousel();
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
index 6aa5a76490..acf037198f 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
@@ -3,14 +3,8 @@
using System.Linq;
using NUnit.Framework;
-using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Testing;
using osu.Game.Beatmaps;
-using osu.Game.Graphics;
-using osu.Game.Graphics.UserInterface;
-using osu.Game.Rulesets;
-using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Select;
namespace osu.Game.Tests.Visual.SongSelect
@@ -180,27 +174,5 @@ namespace osu.Game.Tests.Visual.SongSelect
OnlineBeatmapID = 162,
});
}
-
- [Resolved]
- private RulesetStore rulesets { get; set; }
-
- [Resolved]
- private OsuColour colours { get; set; }
-
- [Test]
- public void TestModAdjustments()
- {
- TestAllMetrics();
-
- Ruleset ruleset = rulesets.AvailableRulesets.First().CreateInstance();
-
- AddStep("with EZ mod", () => SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModEasy) });
-
- AddAssert("first bar coloured blue", () => details.ChildrenOfType().Skip(1).First().AccentColour == colours.BlueDark);
-
- AddStep("with HR mod", () => SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModHardRock) });
-
- AddAssert("first bar coloured red", () => details.ChildrenOfType().Skip(1).First().AccentColour == colours.Red);
- }
}
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index eb812f5d5a..80192b9ebc 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -14,6 +14,7 @@ using osu.Framework.Extensions;
using osu.Framework.Utils;
using osu.Framework.Platform;
using osu.Framework.Screens;
+using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Overlays;
@@ -22,9 +23,11 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Taiko;
+using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
+using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect
{
@@ -70,19 +73,23 @@ namespace osu.Game.Tests.Visual.SongSelect
// required to get bindables attached
Add(music);
- Beatmap.SetDefault();
-
Dependencies.Cache(config = new OsuConfigManager(LocalStorage));
}
private OsuConfigManager config;
- [SetUp]
- public virtual void SetUp() => Schedule(() =>
+ public override void SetUpSteps()
{
- Ruleset.Value = new OsuRuleset().RulesetInfo;
- manager?.Delete(manager.GetAllUsableBeatmapSets());
- });
+ base.SetUpSteps();
+
+ AddStep("delete all beatmaps", () =>
+ {
+ Ruleset.Value = new OsuRuleset().RulesetInfo;
+ manager?.Delete(manager.GetAllUsableBeatmapSets());
+
+ Beatmap.SetDefault();
+ });
+ }
[Test]
public void TestSingleFilterOnEnter()
@@ -95,6 +102,115 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("filter count is 1", () => songSelect.FilterCount == 1);
}
+ [Test]
+ public void TestChangeBeatmapBeforeEnter()
+ {
+ addRulesetImportStep(0);
+
+ createSongSelect();
+
+ AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
+
+ WorkingBeatmap selected = null;
+
+ AddStep("store selected beatmap", () => selected = Beatmap.Value);
+
+ AddStep("select next and enter", () =>
+ {
+ InputManager.PressKey(Key.Down);
+ InputManager.ReleaseKey(Key.Down);
+ InputManager.PressKey(Key.Enter);
+ InputManager.ReleaseKey(Key.Enter);
+ });
+
+ AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
+ AddAssert("ensure selection changed", () => selected != Beatmap.Value);
+ }
+
+ [Test]
+ public void TestChangeBeatmapAfterEnter()
+ {
+ addRulesetImportStep(0);
+
+ createSongSelect();
+
+ AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
+
+ WorkingBeatmap selected = null;
+
+ AddStep("store selected beatmap", () => selected = Beatmap.Value);
+
+ AddStep("select next and enter", () =>
+ {
+ InputManager.PressKey(Key.Enter);
+ InputManager.ReleaseKey(Key.Enter);
+ InputManager.PressKey(Key.Down);
+ InputManager.ReleaseKey(Key.Down);
+ });
+
+ AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
+ AddAssert("ensure selection didn't change", () => selected == Beatmap.Value);
+ }
+
+ [Test]
+ public void TestChangeBeatmapViaMouseBeforeEnter()
+ {
+ addRulesetImportStep(0);
+
+ createSongSelect();
+
+ AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
+
+ WorkingBeatmap selected = null;
+
+ AddStep("store selected beatmap", () => selected = Beatmap.Value);
+
+ AddStep("select next and enter", () =>
+ {
+ InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType()
+ .First(b => ((CarouselBeatmap)b.Item).Beatmap != songSelect.Carousel.SelectedBeatmap));
+
+ InputManager.PressButton(MouseButton.Left);
+ InputManager.ReleaseButton(MouseButton.Left);
+
+ InputManager.PressKey(Key.Enter);
+ InputManager.ReleaseKey(Key.Enter);
+ });
+
+ AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
+ AddAssert("ensure selection changed", () => selected != Beatmap.Value);
+ }
+
+ [Test]
+ public void TestChangeBeatmapViaMouseAfterEnter()
+ {
+ addRulesetImportStep(0);
+
+ createSongSelect();
+
+ AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
+
+ WorkingBeatmap selected = null;
+
+ AddStep("store selected beatmap", () => selected = Beatmap.Value);
+
+ AddStep("select next and enter", () =>
+ {
+ InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType()
+ .First(b => ((CarouselBeatmap)b.Item).Beatmap != songSelect.Carousel.SelectedBeatmap));
+
+ InputManager.PressButton(MouseButton.Left);
+
+ InputManager.PressKey(Key.Enter);
+ InputManager.ReleaseKey(Key.Enter);
+
+ InputManager.ReleaseButton(MouseButton.Left);
+ });
+
+ AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
+ AddAssert("ensure selection didn't change", () => selected == Beatmap.Value);
+ }
+
[Test]
public void TestNoFilterOnSimpleResume()
{
@@ -310,6 +426,31 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("start not requested", () => !startRequested);
}
+ [Test]
+ public void TestAutoplayViaCtrlEnter()
+ {
+ addRulesetImportStep(0);
+
+ createSongSelect();
+
+ AddStep("press ctrl+enter", () =>
+ {
+ InputManager.PressKey(Key.ControlLeft);
+ InputManager.PressKey(Key.Enter);
+
+ InputManager.ReleaseKey(Key.ControlLeft);
+ InputManager.ReleaseKey(Key.Enter);
+ });
+
+ AddUntilStep("wait for player", () => Stack.CurrentScreen is PlayerLoader);
+
+ AddAssert("autoplay enabled", () => songSelect.Mods.Value.FirstOrDefault() is ModAutoplay);
+
+ AddUntilStep("wait for return to ss", () => songSelect.IsCurrentScreen());
+
+ AddAssert("mod disabled", () => songSelect.Mods.Value.Count == 0);
+ }
+
[Test]
public void TestHideSetSelectsCorrectBeatmap()
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControl.cs
index 19eebc89b6..3967b62c95 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControl.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControl.cs
@@ -1,9 +1,11 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
@@ -12,11 +14,11 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneBreadcrumbControl : OsuTestScene
{
- private readonly BreadcrumbControl breadcrumbs;
+ private readonly TestBreadcrumbControl breadcrumbs;
public TestSceneBreadcrumbControl()
{
- Add(breadcrumbs = new BreadcrumbControl
+ Add(breadcrumbs = new TestBreadcrumbControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -25,8 +27,13 @@ namespace osu.Game.Tests.Visual.UserInterface
});
AddStep(@"first", () => breadcrumbs.Current.Value = BreadcrumbTab.Click);
+ assertVisible(1);
+
AddStep(@"second", () => breadcrumbs.Current.Value = BreadcrumbTab.The);
+ assertVisible(2);
+
AddStep(@"third", () => breadcrumbs.Current.Value = BreadcrumbTab.Circles);
+ assertVisible(3);
}
[BackgroundDependencyLoader]
@@ -35,11 +42,27 @@ namespace osu.Game.Tests.Visual.UserInterface
breadcrumbs.StripColour = colours.Blue;
}
+ private void assertVisible(int count) => AddAssert($"first {count} item(s) visible", () =>
+ {
+ for (int i = 0; i < count; i++)
+ {
+ if (breadcrumbs.GetDrawable((BreadcrumbTab)i).State != Visibility.Visible)
+ return false;
+ }
+
+ return true;
+ });
+
private enum BreadcrumbTab
{
Click,
The,
Circles,
}
+
+ private class TestBreadcrumbControl : BreadcrumbControl
+ {
+ public BreadcrumbTabItem GetDrawable(BreadcrumbTab tab) => (BreadcrumbTabItem)TabContainer.First(t => t.Value == tab);
+ }
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs
index 6eb621ca3b..63197ed26a 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs
@@ -16,7 +16,8 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(FooterButtonMods)
+ typeof(FooterButtonMods),
+ typeof(FooterButton)
};
private readonly TestFooterButtonMods footerButtonMods;
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs
new file mode 100644
index 0000000000..443cf59003
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs
@@ -0,0 +1,62 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Overlays.Mods;
+using osu.Game.Rulesets.Mods;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneModButton : OsuTestScene
+ {
+ public TestSceneModButton()
+ {
+ Children = new Drawable[]
+ {
+ new ModButton(new MultiMod(new TestMod1(), new TestMod2(), new TestMod3(), new TestMod4()))
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre
+ }
+ };
+ }
+
+ private class TestMod1 : TestMod
+ {
+ public override string Name => "Test mod 1";
+
+ public override string Acronym => "M1";
+ }
+
+ private class TestMod2 : TestMod
+ {
+ public override string Name => "Test mod 2";
+
+ public override string Acronym => "M2";
+
+ public override IconUsage? Icon => FontAwesome.Solid.Exclamation;
+ }
+
+ private class TestMod3 : TestMod
+ {
+ public override string Name => "Test mod 3";
+
+ public override string Acronym => "M3";
+
+ public override IconUsage? Icon => FontAwesome.Solid.ArrowRight;
+ }
+
+ private class TestMod4 : TestMod
+ {
+ public override string Name => "Test mod 4";
+
+ public override string Acronym => "M4";
+ }
+
+ private abstract class TestMod : Mod, IApplicableMod
+ {
+ public override double ScoreMultiplier => 1.0;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
index 12ee4ceb2e..cd0ce26ea5 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
@@ -63,6 +63,7 @@ namespace osu.Game.Tests.Visual.UserInterface
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
+ SelectedMods = { BindTarget = SelectedMods }
},
modDisplay = new ModDisplay
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
index 8dcb7dcbf8..a89b4f5ba9 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
@@ -25,6 +25,15 @@ namespace osu.Game.Tests.Visual.UserInterface
private readonly Mod testCustomisableMod = new TestModCustomisable1();
+ private readonly Mod testCustomisableAutoOpenMod = new TestModCustomisable2();
+
+ [SetUp]
+ public void SetUp() => Schedule(() =>
+ {
+ SelectedMods.Value = Array.Empty();
+ Ruleset.Value = new TestRulesetInfo();
+ });
+
[Test]
public void TestButtonShowsOnCustomisableMod()
{
@@ -53,17 +62,27 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value);
}
+ [Test]
+ public void TestCustomisationOpensOnModSelect()
+ {
+ createModSelect();
+
+ AddStep("open", () => modSelect.Show());
+ AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.Alpha == 0);
+ AddStep("select mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod));
+ AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.Alpha == 1);
+ }
+
private void createModSelect()
{
AddStep("create mod select", () =>
{
- Ruleset.Value = new TestRulesetInfo();
-
Child = modSelect = new TestModSelectOverlay
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
+ SelectedMods = { BindTarget = SelectedMods }
};
});
}
@@ -128,6 +147,8 @@ namespace osu.Game.Tests.Visual.UserInterface
public override string Name => "Customisable Mod 2";
public override string Acronym => "CM2";
+
+ public override bool RequiresConfiguration => true;
}
private abstract class TestModCustomisable : Mod, IApplicableMod
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs
new file mode 100644
index 0000000000..1cd68d1fdd
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs
@@ -0,0 +1,165 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics.Containers;
+using osu.Game.Overlays;
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Framework.Allocation;
+using osu.Game.Graphics.UserInterface;
+using osu.Framework.Graphics.Shapes;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneOverlayHeader : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(OverlayHeader),
+ typeof(TabControlOverlayHeader<>),
+ typeof(BreadcrumbControlOverlayHeader),
+ typeof(TestNoControlHeader),
+ typeof(TestStringTabControlHeader),
+ typeof(TestEnumTabControlHeader),
+ typeof(TestBreadcrumbControlHeader),
+ typeof(OverlayHeaderBackground)
+ };
+
+ private readonly FillFlowContainer flow;
+
+ public TestSceneOverlayHeader()
+ {
+ AddRange(new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ new BasicScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = flow = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical
+ }
+ }
+ });
+
+ addHeader("Orange OverlayHeader (no background)", new TestNoBackgroundHeader(), OverlayColourScheme.Orange);
+ addHeader("Blue OverlayHeader", new TestNoControlHeader(), OverlayColourScheme.Blue);
+ addHeader("Green TabControlOverlayHeader (string) with ruleset selector", new TestStringTabControlHeader(), OverlayColourScheme.Green);
+ addHeader("Pink TabControlOverlayHeader (enum)", new TestEnumTabControlHeader(), OverlayColourScheme.Pink);
+ addHeader("Red BreadcrumbControlOverlayHeader (no background)", new TestBreadcrumbControlHeader(), OverlayColourScheme.Red);
+ }
+
+ private void addHeader(string name, OverlayHeader header, OverlayColourScheme colourScheme)
+ {
+ flow.Add(new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Margin = new MarginPadding(20),
+ Text = name,
+ },
+ new ColourProvidedContainer(colourScheme, header)
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ }
+ }
+ });
+ }
+
+ private class ColourProvidedContainer : Container
+ {
+ [Cached]
+ private readonly OverlayColourProvider colourProvider;
+
+ public ColourProvidedContainer(OverlayColourScheme colourScheme, OverlayHeader header)
+ {
+ colourProvider = new OverlayColourProvider(colourScheme);
+
+ AutoSizeAxes = Axes.Y;
+ RelativeSizeAxes = Axes.X;
+ Add(header);
+ }
+ }
+
+ private class TestNoBackgroundHeader : OverlayHeader
+ {
+ protected override ScreenTitle CreateTitle() => new TestTitle();
+ }
+
+ private class TestNoControlHeader : OverlayHeader
+ {
+ protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/changelog");
+
+ protected override ScreenTitle CreateTitle() => new TestTitle();
+ }
+
+ private class TestStringTabControlHeader : TabControlOverlayHeader
+ {
+ protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news");
+
+ protected override ScreenTitle CreateTitle() => new TestTitle();
+
+ protected override Drawable CreateTitleContent() => new OverlayRulesetSelector();
+
+ public TestStringTabControlHeader()
+ {
+ TabControl.AddItem("tab1");
+ TabControl.AddItem("tab2");
+ }
+ }
+
+ private class TestEnumTabControlHeader : TabControlOverlayHeader
+ {
+ protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/rankings");
+
+ protected override ScreenTitle CreateTitle() => new TestTitle();
+ }
+
+ private enum TestEnum
+ {
+ Some,
+ Cool,
+ Tabs
+ }
+
+ private class TestBreadcrumbControlHeader : BreadcrumbControlOverlayHeader
+ {
+ protected override ScreenTitle CreateTitle() => new TestTitle();
+
+ public TestBreadcrumbControlHeader()
+ {
+ TabControl.AddItem("tab1");
+ TabControl.AddItem("tab2");
+ TabControl.Current.Value = "tab2";
+ }
+ }
+
+ private class TestTitle : ScreenTitle
+ {
+ public TestTitle()
+ {
+ Title = "title";
+ Section = "section";
+ }
+
+ protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaderBackground.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaderBackground.cs
new file mode 100644
index 0000000000..5a0b28e24a
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeaderBackground.cs
@@ -0,0 +1,42 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics.Containers;
+using osu.Game.Overlays;
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneOverlayHeaderBackground : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(OverlayHeaderBackground)
+ };
+
+ public TestSceneOverlayHeaderBackground()
+ {
+ Add(new BasicScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 20),
+ Children = new[]
+ {
+ new OverlayHeaderBackground(@"Headers/changelog"),
+ new OverlayHeaderBackground(@"Headers/news"),
+ new OverlayHeaderBackground(@"Headers/rankings"),
+ new OverlayHeaderBackground(@"Headers/search"),
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs
new file mode 100644
index 0000000000..8a98127793
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs
@@ -0,0 +1,82 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics;
+using System;
+using System.Collections.Generic;
+using osu.Game.Rulesets.Catch;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Taiko;
+using osu.Framework.Bindables;
+using osu.Game.Overlays;
+using osu.Game.Rulesets;
+using NUnit.Framework;
+using osu.Framework.Graphics.Containers;
+using osuTK;
+using osu.Framework.Allocation;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneOverlayRulesetSelector : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(OverlayRulesetSelector),
+ typeof(OverlayRulesetTabItem),
+ };
+
+ private readonly OverlayRulesetSelector selector;
+ private readonly Bindable ruleset = new Bindable();
+
+ public TestSceneOverlayRulesetSelector()
+ {
+ Add(new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 5),
+ Children = new[]
+ {
+ new ColourProvidedContainer(OverlayColourScheme.Green, selector = new OverlayRulesetSelector { Current = ruleset }),
+ new ColourProvidedContainer(OverlayColourScheme.Blue, new OverlayRulesetSelector { Current = ruleset }),
+ new ColourProvidedContainer(OverlayColourScheme.Orange, new OverlayRulesetSelector { Current = ruleset }),
+ new ColourProvidedContainer(OverlayColourScheme.Pink, new OverlayRulesetSelector { Current = ruleset }),
+ new ColourProvidedContainer(OverlayColourScheme.Purple, new OverlayRulesetSelector { Current = ruleset }),
+ new ColourProvidedContainer(OverlayColourScheme.Red, new OverlayRulesetSelector { Current = ruleset }),
+ }
+ });
+ }
+
+ private class ColourProvidedContainer : Container
+ {
+ [Cached]
+ private readonly OverlayColourProvider colourProvider;
+
+ public ColourProvidedContainer(OverlayColourScheme colourScheme, OverlayRulesetSelector rulesetSelector)
+ {
+ colourProvider = new OverlayColourProvider(colourScheme);
+ AutoSizeAxes = Axes.Both;
+ Add(rulesetSelector);
+ }
+ }
+
+ [Test]
+ public void TestSelection()
+ {
+ AddStep("Select osu!", () => ruleset.Value = new OsuRuleset().RulesetInfo);
+ AddAssert("Check osu! selected", () => selector.Current.Value.Equals(new OsuRuleset().RulesetInfo));
+
+ AddStep("Select mania", () => ruleset.Value = new ManiaRuleset().RulesetInfo);
+ AddAssert("Check mania selected", () => selector.Current.Value.Equals(new ManiaRuleset().RulesetInfo));
+
+ AddStep("Select taiko", () => ruleset.Value = new TaikoRuleset().RulesetInfo);
+ AddAssert("Check taiko selected", () => selector.Current.Value.Equals(new TaikoRuleset().RulesetInfo));
+
+ AddStep("Select catch", () => ruleset.Value = new CatchRuleset().RulesetInfo);
+ AddAssert("Check catch selected", () => selector.Current.Value.Equals(new CatchRuleset().RulesetInfo));
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs
index 0cb8683d72..77a7d819a9 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -26,7 +25,9 @@ namespace osu.Game.Tests.Visual.UserInterface
OsuSpriteText titleText;
IScreen startScreen = new TestScreenOne();
- screenStack = new OsuScreenStack(startScreen) { RelativeSizeAxes = Axes.Both };
+
+ screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
+ screenStack.Push(startScreen);
Children = new Drawable[]
{
@@ -62,7 +63,7 @@ namespace osu.Game.Tests.Visual.UserInterface
waitForCurrent();
pushNext();
waitForCurrent();
- AddAssert(@"only 2 items", () => breadcrumbs.Items.Count() == 2);
+ AddAssert(@"only 2 items", () => breadcrumbs.Items.Count == 2);
AddStep(@"exit current", () => screenStack.CurrentScreen.Exit());
AddAssert(@"current screen is first", () => startScreen == screenStack.CurrentScreen);
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs
index e6589fa823..9738f73548 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("Select random", () =>
{
- selector.Current.Value = selector.Items.ElementAt(RNG.Next(selector.Items.Count()));
+ selector.Current.Value = selector.Items.ElementAt(RNG.Next(selector.Items.Count));
});
AddStep("Toggle disabled state", () => selector.Current.Disabled = !selector.Current.Disabled);
}
diff --git a/osu.Game.Tournament/Resources/countries.json b/osu.Game.Tournament/Resources/countries.json
index ec2ca2bf37..7306a8bec5 100644
--- a/osu.Game.Tournament/Resources/countries.json
+++ b/osu.Game.Tournament/Resources/countries.json
@@ -541,7 +541,7 @@
},
{
"FlagName": "MK",
- "FullName": "Macedonia",
+ "FullName": "North Macedonia",
"Acronym": "MKD"
},
{
@@ -811,7 +811,7 @@
},
{
"FlagName": "CV",
- "FullName": "Cape Verde",
+ "FullName": "Cabo Verde",
"Acronym": "CPV"
},
{
@@ -821,7 +821,7 @@
},
{
"FlagName": "SZ",
- "FullName": "Swaziland",
+ "FullName": "Eswatini",
"Acronym": "SWZ"
},
{
diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs
index dde280ccd8..c4b670f059 100644
--- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs
+++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs
@@ -289,16 +289,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
return true;
}
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
- if (base.OnDrag(e)) return true;
+ base.OnDrag(e);
Selected = true;
this.MoveToOffset(e.Delta);
var pos = Position;
Match.Position.Value = new Point((int)pos.X, (int)pos.Y);
- return true;
}
public void Remove()
diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs
index 84a329085a..cb73985b11 100644
--- a/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs
+++ b/osu.Game.Tournament/Screens/Ladder/Components/ProgressionPath.cs
@@ -10,8 +10,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
{
public class ProgressionPath : Path
{
- public DrawableTournamentMatch Source { get; private set; }
- public DrawableTournamentMatch Destination { get; private set; }
+ public DrawableTournamentMatch Source { get; }
+ public DrawableTournamentMatch Destination { get; }
public ProgressionPath(DrawableTournamentMatch source, DrawableTournamentMatch destination)
{
diff --git a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs
index 0c450a66b4..bdaa1ae7fd 100644
--- a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs
+++ b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs
@@ -22,10 +22,9 @@ namespace osu.Game.Tournament.Screens.Ladder
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
this.MoveTo(target += e.Delta, 1000, Easing.OutQuint);
- return true;
}
private const float min_scale = 0.6f;
diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs
index bd91ad9704..1c94856a4e 100644
--- a/osu.Game.Tournament/TournamentGameBase.cs
+++ b/osu.Game.Tournament/TournamentGameBase.cs
@@ -223,9 +223,12 @@ namespace osu.Game.Tournament
foreach (var r in ladder.Rounds)
{
- foreach (var b in r.Beatmaps)
+ foreach (var b in r.Beatmaps.ToList())
{
- if (b.BeatmapInfo == null && b.ID > 0)
+ if (b.BeatmapInfo != null)
+ continue;
+
+ if (b.ID > 0)
{
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
API.Perform(req);
@@ -233,6 +236,10 @@ namespace osu.Game.Tournament
addedInfo = true;
}
+
+ if (b.BeatmapInfo == null)
+ // if online population couldn't be performed, ensure we don't leave a null value behind
+ r.Beatmaps.Remove(b);
}
}
@@ -296,7 +303,7 @@ namespace osu.Game.Tournament
private class TournamentInputManager : UserInputManager
{
- protected override MouseButtonEventManager CreateButtonManagerFor(MouseButton button)
+ protected override MouseButtonEventManager CreateButtonEventManagerFor(MouseButton button)
{
switch (button)
{
@@ -304,7 +311,7 @@ namespace osu.Game.Tournament
return new RightMouseManager(button);
}
- return base.CreateButtonManagerFor(button);
+ return base.CreateButtonEventManagerFor(button);
}
private class RightMouseManager : MouseButtonEventManager
diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs
index 66c07209f3..2406b0bef2 100644
--- a/osu.Game/Audio/SampleInfo.cs
+++ b/osu.Game/Audio/SampleInfo.cs
@@ -19,6 +19,6 @@ namespace osu.Game.Audio
public IEnumerable LookupNames => new[] { sampleName };
- public int Volume { get; set; } = 100;
+ public int Volume { get; } = 100;
}
}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index a10ad73817..31869f9310 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
@@ -26,6 +27,8 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
+using Decoder = osu.Game.Beatmaps.Formats.Decoder;
+using ZipArchive = SharpCompress.Archives.Zip.ZipArchive;
namespace osu.Game.Beatmaps
{
@@ -56,14 +59,11 @@ namespace osu.Game.Beatmaps
protected override string ImportFromStablePath => "Songs";
private readonly RulesetStore rulesets;
-
private readonly BeatmapStore beatmaps;
-
private readonly AudioManager audioManager;
-
private readonly GameHost host;
-
private readonly BeatmapUpdateQueue updateQueue;
+ private readonly Storage exportStorage;
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null,
WorkingBeatmap defaultBeatmap = null)
@@ -80,6 +80,7 @@ namespace osu.Game.Beatmaps
beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
updateQueue = new BeatmapUpdateQueue(api);
+ exportStorage = storage.GetStorageForDirectory("exports");
}
protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) =>
@@ -174,6 +175,50 @@ namespace osu.Game.Beatmaps
/// The beatmap difficulty to restore.
public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
+ ///
+ /// Saves an file against a given .
+ ///
+ /// The to save the content against. The file referenced by will be replaced.
+ /// The content to write.
+ public void Save(BeatmapInfo info, IBeatmap beatmapContent)
+ {
+ var setInfo = QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == info.ID));
+
+ using (var stream = new MemoryStream())
+ {
+ using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
+ new LegacyBeatmapEncoder(beatmapContent).Encode(sw);
+
+ stream.Seek(0, SeekOrigin.Begin);
+
+ UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream);
+ }
+
+ var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID);
+ if (working != null)
+ workingCache.Remove(working);
+ }
+
+ ///
+ /// Exports a to an .osz package.
+ ///
+ /// The to export.
+ public void Export(BeatmapSetInfo set)
+ {
+ var localSet = QueryBeatmapSet(s => s.ID == set.ID);
+
+ using (var archive = ZipArchive.Create())
+ {
+ foreach (var file in localSet.Files)
+ archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath));
+
+ using (var outputStream = exportStorage.GetStream($"{set}.osz", FileAccess.Write, FileMode.Create))
+ archive.SaveTo(outputStream);
+
+ exportStorage.OpenInNativeExplorer();
+ }
+ }
+
private readonly WeakList workingCache = new WeakList();
///
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 447d52d980..009da0656b 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -239,11 +239,11 @@ namespace osu.Game.Beatmaps.Formats
break;
case @"Source":
- beatmap.BeatmapInfo.Metadata.Source = pair.Value;
+ metadata.Source = pair.Value;
break;
case @"Tags":
- beatmap.BeatmapInfo.Metadata.Tags = pair.Value;
+ metadata.Tags = pair.Value;
break;
case @"BeatmapID":
@@ -300,13 +300,11 @@ namespace osu.Game.Beatmaps.Formats
switch (type)
{
case LegacyEventType.Background:
- string bgFilename = split[2].Trim('"');
- beatmap.BeatmapInfo.Metadata.BackgroundFile = bgFilename.ToStandardisedPath();
+ beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]);
break;
case LegacyEventType.Video:
- string videoFilename = split[2].Trim('"');
- beatmap.BeatmapInfo.Metadata.VideoFile = videoFilename.ToStandardisedPath();
+ beatmap.BeatmapInfo.Metadata.VideoFile = CleanFilename(split[2]);
break;
case LegacyEventType.Break:
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 433becd8cc..09f40ce7b6 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -63,7 +63,7 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}"));
// Todo: Not all countdown types are supported by lazer yet
writer.WriteLine(FormattableString.Invariant($"Countdown: {(beatmap.BeatmapInfo.Countdown ? '1' : '0')}"));
- writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePoints[0].SampleBank)}"));
+ writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePointAt(double.MinValue).SampleBank)}"));
writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}"));
writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}"));
writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}"));
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index f55e24245b..0ec80eee41 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using osu.Framework.Extensions;
using osu.Framework.Logging;
using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
@@ -115,7 +116,7 @@ namespace osu.Game.Beatmaps.Formats
protected KeyValuePair SplitKeyVal(string line, char separator = ':')
{
- var split = line.Trim().Split(new[] { separator }, 2);
+ var split = line.Split(separator, 2);
return new KeyValuePair
(
@@ -124,6 +125,8 @@ namespace osu.Game.Beatmaps.Formats
);
}
+ protected string CleanFilename(string path) => path.Trim('"').ToStandardisedPath();
+
protected enum Section
{
None,
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index c46634e72f..35576e0f33 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -7,7 +7,6 @@ using System.Globalization;
using System.IO;
using osuTK;
using osuTK.Graphics;
-using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Game.IO;
using osu.Game.Storyboards;
@@ -65,13 +64,16 @@ namespace osu.Game.Beatmaps.Formats
private void handleEvents(string line)
{
var depth = 0;
+ var lineSpan = line.AsSpan();
- while (line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal))
+ while (lineSpan.StartsWith(" ", StringComparison.Ordinal) || lineSpan.StartsWith("_", StringComparison.Ordinal))
{
+ lineSpan = lineSpan.Slice(1);
++depth;
- line = line.Substring(1);
}
+ line = lineSpan.ToString();
+
decodeVariables(ref line);
string[] split = line.Split(',');
@@ -89,7 +91,7 @@ namespace osu.Game.Beatmaps.Formats
{
var layer = parseLayer(split[1]);
var origin = parseOrigin(split[2]);
- var path = cleanFilename(split[3]);
+ var path = CleanFilename(split[3]);
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y));
@@ -101,7 +103,7 @@ namespace osu.Game.Beatmaps.Formats
{
var layer = parseLayer(split[1]);
var origin = parseOrigin(split[2]);
- var path = cleanFilename(split[3]);
+ var path = CleanFilename(split[3]);
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
var frameCount = int.Parse(split[6]);
@@ -116,7 +118,7 @@ namespace osu.Game.Beatmaps.Formats
{
var time = double.Parse(split[1], CultureInfo.InvariantCulture);
var layer = parseLayer(split[2]);
- var path = cleanFilename(split[3]);
+ var path = CleanFilename(split[3]);
var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100;
storyboard.GetLayer(layer).Add(new StoryboardSampleInfo(path, time, (int)volume));
break;
@@ -331,7 +333,5 @@ namespace osu.Game.Beatmaps.Formats
break;
}
}
-
- private string cleanFilename(string path) => path.Trim('"').ToStandardisedPath();
}
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 6aba5257f5..05c344b199 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -7,13 +7,11 @@ using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
using osu.Game.Storyboards;
-using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Audio;
using osu.Framework.Statistics;
-using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
@@ -76,21 +74,6 @@ namespace osu.Game.Beatmaps
return AudioManager.Tracks.GetVirtual(length);
}
- ///
- /// Saves the .
- ///
- /// The absolute path of the output file.
- public string Save()
- {
- string directory = Path.Combine(Path.GetTempPath(), @"osu!");
- Directory.CreateDirectory(directory);
-
- var path = Path.Combine(directory, Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json");
- using (var sw = new StreamWriter(path))
- sw.WriteLine(Beatmap.Serialize());
- return path;
- }
-
///
/// Creates a to convert a for a specified .
///
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 947e864a87..4155381790 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.ShowConvertedBeatmaps, true);
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
- Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1);
+ Set(OsuSetting.DisplayStarsMaximum, 10.1, 0, 10.1, 0.1);
Set(OsuSetting.SongSelectGroupingMode, GroupMode.All);
Set(OsuSetting.SongSelectSortingMode, SortMode.Title);
@@ -85,6 +85,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.HitLighting, true);
Set(OsuSetting.ShowInterface, true);
+ Set(OsuSetting.ShowProgressGraph, true);
Set(OsuSetting.ShowHealthDisplayWhenCantFail, true);
Set(OsuSetting.KeyOverlay, false);
Set(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth);
@@ -150,6 +151,7 @@ namespace osu.Game.Configuration
ScoreMeter,
FloatingComments,
ShowInterface,
+ ShowProgressGraph,
ShowHealthDisplayWhenCantFail,
MouseDisableButtons,
MouseDisableWheel,
diff --git a/osu.Game/Configuration/ScoreMeterType.cs b/osu.Game/Configuration/ScoreMeterType.cs
index b85ef9309d..156c4b1377 100644
--- a/osu.Game/Configuration/ScoreMeterType.cs
+++ b/osu.Game/Configuration/ScoreMeterType.cs
@@ -18,5 +18,14 @@ namespace osu.Game.Configuration
[Description("Hit Error (both)")]
HitErrorBoth,
+
+ [Description("Colour (left)")]
+ ColourLeft,
+
+ [Description("Colour (right)")]
+ ColourRight,
+
+ [Description("Colour (both)")]
+ ColourBoth,
}
}
diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs
index 056fa8bcc0..f859dccc80 100644
--- a/osu.Game/Configuration/SettingSourceAttribute.cs
+++ b/osu.Game/Configuration/SettingSourceAttribute.cs
@@ -35,16 +35,11 @@ namespace osu.Game.Configuration
{
public static IEnumerable CreateSettingsControls(this object obj)
{
- foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance))
+ foreach (var (attr, property) in obj.GetSettingsSourceProperties())
{
- var attr = property.GetCustomAttribute(true);
+ object value = property.GetValue(obj);
- if (attr == null)
- continue;
-
- var prop = property.GetValue(obj);
-
- switch (prop)
+ switch (value)
{
case BindableNumber bNumber:
yield return new SettingsSlider
@@ -102,9 +97,22 @@ namespace osu.Game.Configuration
break;
default:
- throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({prop})");
+ throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({value})");
}
}
}
+
+ public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this object obj)
+ {
+ foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance))
+ {
+ var attr = property.GetCustomAttribute(true);
+
+ if (attr == null)
+ continue;
+
+ yield return (attr, property);
+ }
+ }
}
}
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 45fe034a70..5e237d2ecb 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Database
/// The associated file join type.
public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager
where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete
- where TFileModel : INamedFileInfo, new()
+ where TFileModel : class, INamedFileInfo, new()
{
private const int import_queue_request_concurrency = 1;
@@ -222,9 +222,8 @@ namespace osu.Game.Database
{
model = CreateModel(archive);
- if (model == null) return Task.FromResult(null);
-
- model.Hash = computeHash(archive);
+ if (model == null)
+ return Task.FromResult(null);
}
catch (TaskCanceledException)
{
@@ -262,18 +261,24 @@ namespace osu.Game.Database
///
/// In the case of no matching files, a hash will be generated from the passed archive's .
///
- private string computeHash(ArchiveReader reader)
+ private string computeHash(TModel item, ArchiveReader reader = null)
{
// for now, concatenate all .osu files in the set to create a unique hash.
MemoryStream hashable = new MemoryStream();
- foreach (string file in reader.Filenames.Where(f => HashableFileTypes.Any(f.EndsWith)))
+ foreach (TFileModel file in item.Files.Where(f => HashableFileTypes.Any(f.Filename.EndsWith)))
{
- using (Stream s = reader.GetStream(file))
+ using (Stream s = Files.Store.GetStream(file.FileInfo.StoragePath))
s.CopyTo(hashable);
}
- return hashable.Length > 0 ? hashable.ComputeSHA2Hash() : reader.Name.ComputeSHA2Hash();
+ if (hashable.Length > 0)
+ return hashable.ComputeSHA2Hash();
+
+ if (reader != null)
+ return reader.Name.ComputeSHA2Hash();
+
+ return item.Hash;
}
///
@@ -303,6 +308,7 @@ namespace osu.Game.Database
LogForModel(item, "Beginning import...");
item.Files = archive != null ? createFileInfos(archive, Files) : new List();
+ item.Hash = computeHash(item, archive);
await Populate(item, archive, cancellationToken);
@@ -358,12 +364,42 @@ namespace osu.Game.Database
return item;
}, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap();
+ public void UpdateFile(TModel model, TFileModel file, Stream contents)
+ {
+ using (var usage = ContextFactory.GetForWrite())
+ {
+ // Dereference the existing file info, since the file model will be removed.
+ Files.Dereference(file.FileInfo);
+
+ // Remove the file model.
+ usage.Context.Set().Remove(file);
+
+ // Add the new file info and containing file model.
+ model.Files.Remove(file);
+ model.Files.Add(new TFileModel
+ {
+ Filename = file.Filename,
+ FileInfo = Files.Add(contents)
+ });
+
+ Update(model);
+ }
+ }
+
///
/// Perform an update of the specified item.
- /// TODO: Support file changes.
+ /// TODO: Support file additions/removals.
///
/// The item to update.
- public void Update(TModel item) => ModelStore.Update(item);
+ public void Update(TModel item)
+ {
+ using (ContextFactory.GetForWrite())
+ {
+ item.Hash = computeHash(item);
+
+ ModelStore.Update(item);
+ }
+ }
///
/// Delete an item from the manager.
diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs
index 71bf195a8d..4bd9df5f36 100644
--- a/osu.Game/Database/DownloadableArchiveModelManager.cs
+++ b/osu.Game/Database/DownloadableArchiveModelManager.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Database
/// The associated file join type.
public abstract class DownloadableArchiveModelManager : ArchiveModelManager, IModelDownloader
where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable
- where TFileModel : INamedFileInfo, new()
+ where TFileModel : class, INamedFileInfo, new()
{
public event Action> DownloadBegan;
diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs
index b9ef279f5c..be9aefa359 100644
--- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs
+++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs
@@ -38,6 +38,11 @@ namespace osu.Game.Graphics.Containers
///
public int Divisor { get; set; } = 1;
+ ///
+ /// An optional minimum beat length. Any beat length below this will be multiplied by two until valid.
+ ///
+ public double MinimumBeatLength { get; set; }
+
///
/// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing.
///
@@ -89,6 +94,9 @@ namespace osu.Game.Graphics.Containers
double beatLength = timingPoint.BeatLength / Divisor;
+ while (beatLength < MinimumBeatLength)
+ beatLength *= 2;
+
int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength) - (effectPoint.OmitFirstBarLine ? 1 : 0);
// The beats before the start of the first control point are off by 1, this should do the trick
diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
index 9735f6373d..e3a9a5fe9d 100644
--- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs
+++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
@@ -63,7 +63,7 @@ namespace osu.Game.Graphics.Containers
}
public void AddUserLink(User user, Action creationParameters = null)
- => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "View Profile");
+ => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile");
private void createLink(IEnumerable drawables, LinkDetails link, string tooltipText, Action action = null)
{
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index facf70b47a..93ac69bdbf 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -76,12 +76,12 @@ namespace osu.Game.Graphics.Containers
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
if (closeOnMouseUp && !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
Hide();
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
public virtual bool OnPressed(GlobalAction action)
@@ -99,7 +99,9 @@ namespace osu.Game.Graphics.Containers
return false;
}
- public bool OnReleased(GlobalAction action) => false;
+ public void OnReleased(GlobalAction action)
+ {
+ }
protected override void UpdateState(ValueChangedEvent state)
{
diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
index cfd459da5e..6d531887ed 100644
--- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
@@ -50,15 +50,15 @@ namespace osu.Game.Graphics.Containers
return base.OnMouseDown(e);
}
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
if (rightMouseDragging)
{
scrollFromMouseEvent(e);
- return true;
+ return;
}
- return base.OnDrag(e);
+ base.OnDrag(e);
}
protected override bool OnDragStart(DragStartEvent e)
@@ -72,15 +72,15 @@ namespace osu.Game.Graphics.Containers
return base.OnDragStart(e);
}
- protected override bool OnDragEnd(DragEndEvent e)
+ protected override void OnDragEnd(DragEndEvent e)
{
if (rightMouseDragging)
{
rightMouseDragging = false;
- return true;
+ return;
}
- return base.OnDragEnd(e);
+ base.OnDragEnd(e);
}
protected override bool OnScroll(ScrollEvent e)
@@ -162,13 +162,13 @@ namespace osu.Game.Graphics.Containers
return true;
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
- if (e.Button != MouseButton.Left) return false;
+ if (e.Button != MouseButton.Left) return;
box.FadeColour(Color4.White, 100);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
}
}
diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs
index 170ea63059..580177d17a 100644
--- a/osu.Game/Graphics/Cursor/MenuCursor.cs
+++ b/osu.Game/Graphics/Cursor/MenuCursor.cs
@@ -92,7 +92,7 @@ namespace osu.Game.Graphics.Cursor
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
if (!e.IsPressed(MouseButton.Left) && !e.IsPressed(MouseButton.Right))
{
@@ -107,7 +107,7 @@ namespace osu.Game.Graphics.Cursor
dragRotationState = DragRotationState.NotDragging;
}
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override void PopIn()
diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs
index 533f02af7b..0224c77ee8 100644
--- a/osu.Game/Graphics/DrawableDate.cs
+++ b/osu.Game/Graphics/DrawableDate.cs
@@ -29,9 +29,9 @@ namespace osu.Game.Graphics
}
}
- public DrawableDate(DateTimeOffset date)
+ public DrawableDate(DateTimeOffset date, float textSize = OsuFont.DEFAULT_FONT_SIZE, bool italic = true)
{
- Font = OsuFont.GetFont(weight: FontWeight.Regular, italics: true);
+ Font = OsuFont.GetFont(weight: FontWeight.Regular, size: textSize, italics: italic);
Date = date;
}
diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs
index 2dc12b3e67..c8298543a1 100644
--- a/osu.Game/Graphics/OsuColour.cs
+++ b/osu.Game/Graphics/OsuColour.cs
@@ -35,6 +35,20 @@ namespace osu.Game.Graphics
Convert.ToByte(hex.Substring(2, 2), 16),
Convert.ToByte(hex.Substring(4, 2), 16),
255);
+
+ case 4:
+ return new Color4(
+ (byte)(Convert.ToByte(hex.Substring(0, 1), 16) * 17),
+ (byte)(Convert.ToByte(hex.Substring(1, 1), 16) * 17),
+ (byte)(Convert.ToByte(hex.Substring(2, 1), 16) * 17),
+ (byte)(Convert.ToByte(hex.Substring(3, 1), 16) * 17));
+
+ case 8:
+ return new Color4(
+ Convert.ToByte(hex.Substring(0, 2), 16),
+ Convert.ToByte(hex.Substring(2, 2), 16),
+ Convert.ToByte(hex.Substring(4, 2), 16),
+ Convert.ToByte(hex.Substring(6, 2), 16));
}
}
diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs
index b9151b7393..3ad36577b5 100644
--- a/osu.Game/Graphics/ScreenshotManager.cs
+++ b/osu.Game/Graphics/ScreenshotManager.cs
@@ -67,7 +67,9 @@ namespace osu.Game.Graphics
return false;
}
- public bool OnReleased(GlobalAction action) => false;
+ public void OnReleased(GlobalAction action)
+ {
+ }
private volatile int screenShotTasks;
@@ -88,7 +90,7 @@ namespace osu.Game.Graphics
{
ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() =>
{
- if (framesWaited++ < frames_to_wait)
+ if (framesWaited++ >= frames_to_wait)
// ReSharper disable once AccessToDisposedClosure
framesWaitedEvent.Set();
}, 10, true);
diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs
index 23565e8742..88ba7ede6e 100644
--- a/osu.Game/Graphics/UserInterface/BackButton.cs
+++ b/osu.Game/Graphics/UserInterface/BackButton.cs
@@ -67,7 +67,9 @@ namespace osu.Game.Graphics.UserInterface
return false;
}
- public bool OnReleased(GlobalAction action) => action == GlobalAction.Back;
+ public void OnReleased(GlobalAction action)
+ {
+ }
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
index e2438cc4cd..84429bf5bd 100644
--- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
+++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
@@ -34,13 +34,13 @@ namespace osu.Game.Graphics.UserInterface
var tIndex = TabContainer.IndexOf(t);
var tabIndex = TabContainer.IndexOf(TabMap[index.NewValue]);
- t.State = tIndex < tabIndex ? Visibility.Hidden : Visibility.Visible;
- t.Chevron.FadeTo(tIndex <= tabIndex ? 0f : 1f, 500, Easing.OutQuint);
+ t.State = tIndex > tabIndex ? Visibility.Hidden : Visibility.Visible;
+ t.Chevron.FadeTo(tIndex >= tabIndex ? 0f : 1f, 500, Easing.OutQuint);
}
};
}
- protected class BreadcrumbTabItem : OsuTabItem, IStateful
+ public class BreadcrumbTabItem : OsuTabItem, IStateful
{
protected virtual float ChevronSize => 10;
diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs
index aed07e56ee..9b53ee7b2d 100644
--- a/osu.Game/Graphics/UserInterface/DialogButton.cs
+++ b/osu.Game/Graphics/UserInterface/DialogButton.cs
@@ -232,11 +232,11 @@ namespace osu.Game.Graphics.UserInterface
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
if (Selected.Value)
colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override bool OnHover(HoverEvent e)
diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
index 8c00cae08a..7225dbc66f 100644
--- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
+++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
@@ -57,6 +57,6 @@ namespace osu.Game.Graphics.UserInterface
return true;
}
- public string TooltipText => "View in browser";
+ public string TooltipText => "view in browser";
}
}
diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
index 0b183c0ec9..e59353a480 100644
--- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
@@ -80,7 +80,9 @@ namespace osu.Game.Graphics.UserInterface
return false;
}
- public bool OnReleased(GlobalAction action) => false;
+ public void OnReleased(GlobalAction action)
+ {
+ }
public override bool RequestsFocus => HoldFocus;
}
diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs
index fcd8940348..40899e7e95 100644
--- a/osu.Game/Graphics/UserInterface/HoverSounds.cs
+++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Graphics.UserInterface
///
/// Length of debounce for hover sound playback, in milliseconds. Default is 50ms.
///
- public double HoverDebounceTime { get; set; } = 50;
+ public double HoverDebounceTime { get; } = 50;
protected readonly HoverSampleSet SampleSet;
diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
index 660bd7979f..cfcf034d1c 100644
--- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
@@ -107,10 +107,10 @@ namespace osu.Game.Graphics.UserInterface
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
Content.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs
index c6a9aa1c97..9cf8f02024 100644
--- a/osu.Game/Graphics/UserInterface/OsuButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuButton.cs
@@ -129,10 +129,10 @@ namespace osu.Game.Graphics.UserInterface
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
Content.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected virtual SpriteText CreateText() => new OsuSpriteText
diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
index 418ad038f7..e4a4b1c50e 100644
--- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
@@ -104,7 +104,7 @@ namespace osu.Game.Graphics.UserInterface
private class CapsWarning : SpriteIcon, IHasTooltip
{
- public string TooltipText => @"Caps lock is active";
+ public string TooltipText => @"caps lock is active";
public CapsWarning()
{
diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
index 958390d5d2..d0356e77c7 100644
--- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
+++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
@@ -36,6 +36,11 @@ namespace osu.Game.Graphics.UserInterface
public virtual string TooltipText { get; private set; }
+ ///
+ /// Whether to format the tooltip as a percentage or the actual value.
+ ///
+ public bool DisplayAsPercentage { get; set; }
+
private Color4 accentColour;
public Color4 AccentColour
@@ -128,10 +133,10 @@ namespace osu.Game.Graphics.UserInterface
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
Nub.Current.Value = false;
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override void OnUserChange(T value)
@@ -169,11 +174,11 @@ namespace osu.Game.Graphics.UserInterface
else
{
double floatValue = value.ToDouble(NumberFormatInfo.InvariantInfo);
- double floatMinValue = CurrentNumber.MinValue.ToDouble(NumberFormatInfo.InvariantInfo);
- double floatMaxValue = CurrentNumber.MaxValue.ToDouble(NumberFormatInfo.InvariantInfo);
- if (floatMaxValue == 1 && floatMinValue >= -1)
- TooltipText = floatValue.ToString("P0");
+ if (DisplayAsPercentage)
+ {
+ TooltipText = floatValue.ToString("0%");
+ }
else
{
var decimalPrecision = normalise(CurrentNumber.Precision.ToDecimal(NumberFormatInfo.InvariantInfo), max_decimal_digits);
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
index 6a7998d5fb..9fa6085035 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
@@ -10,7 +10,6 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
@@ -22,6 +21,22 @@ namespace osu.Game.Graphics.UserInterface
{
public class OsuTabControl : TabControl
{
+ private Color4 accentColour;
+
+ public virtual Color4 AccentColour
+ {
+ get => accentColour;
+ set
+ {
+ accentColour = value;
+
+ if (Dropdown is IHasAccentColour dropdown)
+ dropdown.AccentColour = value;
+ foreach (var i in TabContainer.Children.OfType())
+ i.AccentColour = value;
+ }
+ }
+
private readonly Box strip;
protected override Dropdown CreateDropdown() => new OsuTabDropdown();
@@ -63,35 +78,12 @@ namespace osu.Game.Graphics.UserInterface
AccentColour = colours.Blue;
}
- private Color4 accentColour;
-
- public Color4 AccentColour
- {
- get => accentColour;
- set
- {
- accentColour = value;
- if (Dropdown is IHasAccentColour dropdown)
- dropdown.AccentColour = value;
- foreach (var i in TabContainer.Children.OfType())
- i.AccentColour = value;
- }
- }
-
public Color4 StripColour
{
get => strip.Colour;
set => strip.Colour = value;
}
- protected override TabFillFlowContainer CreateTabFlow() => new OsuTabFillFlowContainer
- {
- Direction = FillDirection.Full,
- RelativeSizeAxes = Axes.Both,
- Depth = -1,
- Masking = true
- };
-
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
@@ -283,10 +275,5 @@ namespace osu.Game.Graphics.UserInterface
}
}
}
-
- private class OsuTabFillFlowContainer : TabFillFlowContainer
- {
- protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y);
- }
}
}
diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs
index 24d8009f40..01d8edaecf 100644
--- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs
+++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs
@@ -16,11 +16,7 @@ namespace osu.Game.Graphics.UserInterface
///
/// How many leading zeroes the counter has.
///
- public uint LeadingZeroes
- {
- get;
- protected set;
- }
+ public uint LeadingZeroes { get; }
///
/// Displays score.
diff --git a/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs
index 3e0a6c3265..e85525b2f8 100644
--- a/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs
+++ b/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs
@@ -17,7 +17,8 @@ namespace osu.Game.Graphics.UserInterface
stack.ScreenPushed += onPushed;
stack.ScreenExited += onExited;
- onPushed(null, stack.CurrentScreen);
+ if (stack.CurrentScreen != null)
+ onPushed(null, stack.CurrentScreen);
Current.ValueChanged += current => current.NewValue.MakeCurrent();
}
diff --git a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs
index 4931a6aed6..c9cd9f1158 100644
--- a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs
+++ b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs
@@ -40,13 +40,14 @@ namespace osu.Game.Graphics.UserInterface
public ShowMoreButton()
{
- AutoSizeAxes = Axes.Both;
+ Height = 30;
+ Width = 140;
}
protected override Drawable CreateContent() => new CircularContainer
{
Masking = true,
- Size = new Vector2(140, 30),
+ RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
background = new Box
diff --git a/osu.Game/IO/Legacy/SerializationWriter.cs b/osu.Game/IO/Legacy/SerializationWriter.cs
index c75de93bc8..bb8014fe54 100644
--- a/osu.Game/IO/Legacy/SerializationWriter.cs
+++ b/osu.Game/IO/Legacy/SerializationWriter.cs
@@ -130,91 +130,91 @@ namespace osu.Game.IO.Legacy
}
else
{
- switch (obj.GetType().Name)
+ switch (obj)
{
- case "Boolean":
+ case bool boolObj:
Write((byte)ObjType.boolType);
- Write((bool)obj);
+ Write(boolObj);
break;
- case "Byte":
+ case byte byteObj:
Write((byte)ObjType.byteType);
- Write((byte)obj);
+ Write(byteObj);
break;
- case "UInt16":
+ case ushort ushortObj:
Write((byte)ObjType.uint16Type);
- Write((ushort)obj);
+ Write(ushortObj);
break;
- case "UInt32":
+ case uint uintObj:
Write((byte)ObjType.uint32Type);
- Write((uint)obj);
+ Write(uintObj);
break;
- case "UInt64":
+ case ulong ulongObj:
Write((byte)ObjType.uint64Type);
- Write((ulong)obj);
+ Write(ulongObj);
break;
- case "SByte":
+ case sbyte sbyteObj:
Write((byte)ObjType.sbyteType);
- Write((sbyte)obj);
+ Write(sbyteObj);
break;
- case "Int16":
+ case short shortObj:
Write((byte)ObjType.int16Type);
- Write((short)obj);
+ Write(shortObj);
break;
- case "Int32":
+ case int intObj:
Write((byte)ObjType.int32Type);
- Write((int)obj);
+ Write(intObj);
break;
- case "Int64":
+ case long longObj:
Write((byte)ObjType.int64Type);
- Write((long)obj);
+ Write(longObj);
break;
- case "Char":
+ case char charObj:
Write((byte)ObjType.charType);
- base.Write((char)obj);
+ base.Write(charObj);
break;
- case "String":
+ case string stringObj:
Write((byte)ObjType.stringType);
- base.Write((string)obj);
+ base.Write(stringObj);
break;
- case "Single":
+ case float floatObj:
Write((byte)ObjType.singleType);
- Write((float)obj);
+ Write(floatObj);
break;
- case "Double":
+ case double doubleObj:
Write((byte)ObjType.doubleType);
- Write((double)obj);
+ Write(doubleObj);
break;
- case "Decimal":
+ case decimal decimalObj:
Write((byte)ObjType.decimalType);
- Write((decimal)obj);
+ Write(decimalObj);
break;
- case "DateTime":
+ case DateTime dateTimeObj:
Write((byte)ObjType.dateTimeType);
- Write((DateTime)obj);
+ Write(dateTimeObj);
break;
- case "Byte[]":
+ case byte[] byteArray:
Write((byte)ObjType.byteArrayType);
- base.Write((byte[])obj);
+ base.Write(byteArray);
break;
- case "Char[]":
+ case char[] charArray:
Write((byte)ObjType.charArrayType);
- base.Write((char[])obj);
+ base.Write(charArray);
break;
default:
diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs
index 39ccf9fe1c..63a6348b57 100644
--- a/osu.Game/Input/IdleTracker.cs
+++ b/osu.Game/Input/IdleTracker.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Input
public bool OnPressed(PlatformAction action) => updateLastInteractionTime();
- public bool OnReleased(PlatformAction action) => updateLastInteractionTime();
+ public void OnReleased(PlatformAction action) => updateLastInteractionTime();
protected override bool Handle(UIEvent e)
{
diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs
new file mode 100644
index 0000000000..46a8db31b7
--- /dev/null
+++ b/osu.Game/Online/API/APIMod.cs
@@ -0,0 +1,57 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Humanizer;
+using Newtonsoft.Json;
+using osu.Framework.Bindables;
+using osu.Game.Configuration;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+
+namespace osu.Game.Online.API
+{
+ public class APIMod : IMod
+ {
+ [JsonProperty("acronym")]
+ public string Acronym { get; set; }
+
+ [JsonProperty("settings")]
+ public Dictionary Settings { get; set; } = new Dictionary();
+
+ [JsonConstructor]
+ private APIMod()
+ {
+ }
+
+ public APIMod(Mod mod)
+ {
+ Acronym = mod.Acronym;
+
+ foreach (var (_, property) in mod.GetSettingsSourceProperties())
+ Settings.Add(property.Name.Underscore(), property.GetValue(mod));
+ }
+
+ public Mod ToMod(Ruleset ruleset)
+ {
+ Mod resultMod = ruleset.GetAllMods().FirstOrDefault(m => m.Acronym == Acronym);
+
+ if (resultMod == null)
+ throw new InvalidOperationException($"There is no mod in the ruleset ({ruleset.ShortName}) matching the acronym {Acronym}.");
+
+ foreach (var (_, property) in resultMod.GetSettingsSourceProperties())
+ {
+ if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue))
+ continue;
+
+ ((IBindable)property.GetValue(resultMod)).Parse(settingValue);
+ }
+
+ return resultMod;
+ }
+
+ public bool Equals(IMod other) => Acronym == other?.Acronym;
+ }
+}
diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs
index 0956c749a2..30c1018c1e 100644
--- a/osu.Game/Online/API/APIRequest.cs
+++ b/osu.Game/Online/API/APIRequest.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Online.API
{
protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest(Uri);
- public T Result => ((JsonWebRequest)WebRequest).ResponseObject;
+ public T Result => ((OsuJsonWebRequest)WebRequest).ResponseObject;
protected APIRequest()
{
@@ -30,16 +30,6 @@ namespace osu.Game.Online.API
/// This will be scheduled to the API's internal scheduler (run on update thread automatically).
///
public new event APISuccessHandler Success;
-
- private class OsuJsonWebRequest : JsonWebRequest
- {
- public OsuJsonWebRequest(string uri)
- : base(uri)
- {
- }
-
- protected override string UserAgent => "osu!";
- }
}
///
@@ -162,16 +152,6 @@ namespace osu.Game.Online.API
[JsonProperty("error")]
public string ErrorMessage { get; set; }
}
-
- private class OsuWebRequest : WebRequest
- {
- public OsuWebRequest(string uri)
- : base(uri)
- {
- }
-
- protected override string UserAgent => "osu!";
- }
}
public class APIException : InvalidOperationException
diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs
index baf494ebf9..bdc47aab8d 100644
--- a/osu.Game/Online/API/OAuth.cs
+++ b/osu.Game/Online/API/OAuth.cs
@@ -4,7 +4,6 @@
using System.Diagnostics;
using System.Net.Http;
using osu.Framework.Bindables;
-using osu.Framework.IO.Network;
namespace osu.Game.Online.API
{
@@ -166,7 +165,7 @@ namespace osu.Game.Online.API
}
}
- private class AccessTokenRequest : JsonWebRequest
+ private class AccessTokenRequest : OsuJsonWebRequest
{
protected string GrantType;
diff --git a/osu.Game/Online/API/OsuJsonWebRequest.cs b/osu.Game/Online/API/OsuJsonWebRequest.cs
new file mode 100644
index 0000000000..4a45a8b261
--- /dev/null
+++ b/osu.Game/Online/API/OsuJsonWebRequest.cs
@@ -0,0 +1,21 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.IO.Network;
+
+namespace osu.Game.Online.API
+{
+ public class OsuJsonWebRequest : JsonWebRequest
+ {
+ public OsuJsonWebRequest(string uri)
+ : base(uri)
+ {
+ }
+
+ public OsuJsonWebRequest()
+ {
+ }
+
+ protected override string UserAgent => "osu!";
+ }
+}
diff --git a/osu.Game/Online/API/OsuWebRequest.cs b/osu.Game/Online/API/OsuWebRequest.cs
new file mode 100644
index 0000000000..1d27899473
--- /dev/null
+++ b/osu.Game/Online/API/OsuWebRequest.cs
@@ -0,0 +1,21 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.IO.Network;
+
+namespace osu.Game.Online.API
+{
+ public class OsuWebRequest : WebRequest
+ {
+ public OsuWebRequest(string uri)
+ : base(uri)
+ {
+ }
+
+ public OsuWebRequest()
+ {
+ }
+
+ protected override string UserAgent => "osu!";
+ }
+}
diff --git a/osu.Game/Online/API/RegistrationRequest.cs b/osu.Game/Online/API/RegistrationRequest.cs
index 349cd4de0c..f650e5c93b 100644
--- a/osu.Game/Online/API/RegistrationRequest.cs
+++ b/osu.Game/Online/API/RegistrationRequest.cs
@@ -2,11 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using Newtonsoft.Json;
-using osu.Framework.IO.Network;
namespace osu.Game.Online.API
{
- public class RegistrationRequest : WebRequest
+ public class RegistrationRequest : OsuWebRequest
{
internal string Username;
internal string Email;
diff --git a/osu.Game/Online/API/Requests/Responses/APIMod.cs b/osu.Game/Online/API/Requests/Responses/APIMod.cs
deleted file mode 100644
index b9da4f49ee..0000000000
--- a/osu.Game/Online/API/Requests/Responses/APIMod.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using osu.Game.Rulesets.Mods;
-
-namespace osu.Game.Online.API.Requests.Responses
-{
- public class APIMod : IMod
- {
- public string Acronym { get; set; }
-
- public bool Equals(IMod other) => Acronym == other?.Acronym;
- }
-}
diff --git a/osu.Game/Online/API/Requests/Responses/Comment.cs b/osu.Game/Online/API/Requests/Responses/Comment.cs
index 5510e9afff..3e38c3067b 100644
--- a/osu.Game/Online/API/Requests/Responses/Comment.cs
+++ b/osu.Game/Online/API/Requests/Responses/Comment.cs
@@ -6,8 +6,6 @@ using osu.Game.Users;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Net;
-using System.Text.RegularExpressions;
namespace osu.Game.Online.API.Requests.Responses
{
@@ -70,12 +68,10 @@ namespace osu.Game.Online.API.Requests.Responses
public bool IsDeleted => DeletedAt.HasValue;
- public bool HasMessage => !string.IsNullOrEmpty(MessageHtml);
+ public bool HasMessage => !string.IsNullOrEmpty(Message);
public bool IsVoted { get; set; }
- public string GetMessage => HasMessage ? WebUtility.HtmlDecode(Regex.Replace(MessageHtml, @"<(.|\n)*?>", string.Empty)) : string.Empty;
-
public int DeletedChildrenCount => ChildComments.Count(c => c.IsDeleted);
}
}
diff --git a/osu.Game/Online/API/Requests/Responses/CommentBundle.cs b/osu.Game/Online/API/Requests/Responses/CommentBundle.cs
index 8db5d8d6ad..9b3ef8b6e5 100644
--- a/osu.Game/Online/API/Requests/Responses/CommentBundle.cs
+++ b/osu.Game/Online/API/Requests/Responses/CommentBundle.cs
@@ -47,17 +47,18 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"included_comments")]
public List IncludedComments { get; set; }
+ private List userVotes;
+
[JsonProperty(@"user_votes")]
- private List userVotes
+ public List UserVotes
{
- set => value.ForEach(v =>
+ get => userVotes;
+ set
{
- Comments.ForEach(c =>
- {
- if (v == c.Id)
- c.IsVoted = true;
- });
- });
+ userVotes = value;
+
+ Comments.ForEach(c => c.IsVoted = value.Contains(c.Id));
+ }
}
private List users;
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index 9c7324d913..c8b2f2327b 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -277,7 +277,7 @@ namespace osu.Game.Online.Leaderboards
protected virtual IEnumerable GetStatistics(ScoreInfo model) => new[]
{
new LeaderboardScoreStatistic(FontAwesome.Solid.Link, "Max Combo", model.MaxCombo.ToString()),
- new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy))
+ new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:0%}" : @"{0:0.00%}", model.Accuracy))
};
protected override bool OnHover(HoverEvent e)
diff --git a/osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs b/osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs
index 15d7dabe65..d109f28e72 100644
--- a/osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs
+++ b/osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs
@@ -55,10 +55,10 @@ namespace osu.Game.Online.Leaderboards
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
icon.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
}
}
diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs
index d13e8b31e6..5f8edc607b 100644
--- a/osu.Game/Online/Multiplayer/PlaylistItem.cs
+++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using osu.Game.Beatmaps;
+using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
@@ -50,7 +51,7 @@ namespace osu.Game.Online.Multiplayer
[JsonProperty("allowed_mods")]
private APIMod[] allowedMods
{
- get => AllowedMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
+ get => AllowedMods.Select(m => new APIMod(m)).ToArray();
set => allowedModsBacking = value;
}
@@ -59,7 +60,7 @@ namespace osu.Game.Online.Multiplayer
[JsonProperty("required_mods")]
private APIMod[] requiredMods
{
- get => RequiredMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
+ get => RequiredMods.Select(m => new APIMod(m)).ToArray();
set => requiredModsBacking = value;
}
@@ -72,10 +73,12 @@ namespace osu.Game.Online.Multiplayer
Beatmap = apiBeatmap == null ? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == BeatmapID) : apiBeatmap.ToBeatmap(rulesets);
Ruleset = rulesets.GetRuleset(RulesetID);
+ Ruleset rulesetInstance = Ruleset.CreateInstance();
+
if (allowedModsBacking != null)
{
AllowedMods.Clear();
- AllowedMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => allowedModsBacking.Any(m => m.Acronym == mod.Acronym)));
+ AllowedMods.AddRange(allowedModsBacking.Select(m => m.ToMod(rulesetInstance)));
allowedModsBacking = null;
}
@@ -83,7 +86,7 @@ namespace osu.Game.Online.Multiplayer
if (requiredModsBacking != null)
{
RequiredMods.Clear();
- RequiredMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => requiredModsBacking.Any(m => m.Acronym == mod.Acronym)));
+ RequiredMods.AddRange(requiredModsBacking.Select(m => m.ToMod(rulesetInstance)));
requiredModsBacking = null;
}
diff --git a/osu.Game/Online/Placeholders/LoginPlaceholder.cs b/osu.Game/Online/Placeholders/LoginPlaceholder.cs
index ffc6623229..591eb976e2 100644
--- a/osu.Game/Online/Placeholders/LoginPlaceholder.cs
+++ b/osu.Game/Online/Placeholders/LoginPlaceholder.cs
@@ -31,10 +31,10 @@ namespace osu.Game.Online.Placeholders
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
this.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override bool OnClick(ClickEvent e)
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 9df854d178..ff3dee55af 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -327,10 +327,10 @@ namespace osu.Game
return;
}
- performFromMainMenu(() =>
+ PerformFromScreen(screen =>
{
// we might already be at song select, so a check is required before performing the load to solo.
- if (menuScreen.IsCurrentScreen())
+ if (screen is MainMenu)
menuScreen.LoadToSolo();
// we might even already be at the song
@@ -344,7 +344,7 @@ namespace osu.Game
Ruleset.Value = first.Ruleset;
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
- }, $"load {beatmap}", bypassScreenAllowChecks: true, targetScreen: typeof(PlaySongSelect));
+ }, validScreens: new[] { typeof(PlaySongSelect) });
}
///
@@ -381,12 +381,12 @@ namespace osu.Game
return;
}
- performFromMainMenu(() =>
+ PerformFromScreen(screen =>
{
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
- menuScreen.Push(new ReplayPlayerLoader(databasedScore));
- }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);
+ screen.Push(new ReplayPlayerLoader(databasedScore));
+ }, validScreens: new[] { typeof(PlaySongSelect) });
}
protected virtual Loader CreateLoader() => new Loader();
@@ -441,53 +441,42 @@ namespace osu.Game
private ScheduledDelegate performFromMainMenuTask;
///
- /// Perform an action only after returning to the main menu.
+ /// Perform an action only after returning to a specific screen as indicated by .
/// Eagerly tries to exit the current screen until it succeeds.
///
/// The action to perform once we are in the correct state.
- /// The task name to display in a notification (if we can't immediately reach the main menu state).
- /// An optional target screen type. If this screen is already current we can immediately perform the action without returning to the menu.
- /// Whether checking should be bypassed.
- private void performFromMainMenu(Action action, string taskName, Type targetScreen = null, bool bypassScreenAllowChecks = false)
+ /// An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. is used if not specified.
+ protected void PerformFromScreen(Action action, IEnumerable validScreens = null)
{
performFromMainMenuTask?.Cancel();
- // if the current screen does not allow screen changing, give the user an option to try again later.
- if (!bypassScreenAllowChecks && (ScreenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false)
- {
- notifications.Post(new SimpleNotification
- {
- Text = $"Click here to {taskName}",
- Activated = () =>
- {
- performFromMainMenu(action, taskName, targetScreen, true);
- return true;
- }
- });
-
- return;
- }
+ validScreens ??= Enumerable.Empty();
+ validScreens = validScreens.Append(typeof(MainMenu));
CloseAllOverlays(false);
// we may already be at the target screen type.
- if (targetScreen != null && ScreenStack.CurrentScreen?.GetType() == targetScreen)
+ if (validScreens.Contains(ScreenStack.CurrentScreen?.GetType()) && !Beatmap.Disabled)
{
- action();
+ action(ScreenStack.CurrentScreen);
return;
}
- // all conditions have been met to continue with the action.
- if (menuScreen?.IsCurrentScreen() == true && !Beatmap.Disabled)
+ // find closest valid target
+ IScreen screen = ScreenStack.CurrentScreen;
+
+ while (screen != null)
{
- action();
- return;
+ if (validScreens.Contains(screen.GetType()))
+ {
+ screen.MakeCurrent();
+ break;
+ }
+
+ screen = screen.GetParentScreen();
}
- // menuScreen may not be initialised yet (null check required).
- menuScreen?.MakeCurrent();
-
- performFromMainMenuTask = Schedule(() => performFromMainMenu(action, taskName));
+ performFromMainMenuTask = Schedule(() => PerformFromScreen(action, validScreens));
}
///
@@ -881,7 +870,9 @@ namespace osu.Game
#endregion
- public bool OnReleased(GlobalAction action) => false;
+ public void OnReleased(GlobalAction action)
+ {
+ }
private Container overlayContent;
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 0819642d2d..07c9d37a86 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -330,7 +330,7 @@ namespace osu.Game
private class OsuUserInputManager : UserInputManager
{
- protected override MouseButtonEventManager CreateButtonManagerFor(MouseButton button)
+ protected override MouseButtonEventManager CreateButtonEventManagerFor(MouseButton button)
{
switch (button)
{
@@ -338,7 +338,7 @@ namespace osu.Game
return new RightMouseManager(button);
}
- return base.CreateButtonManagerFor(button);
+ return base.CreateButtonEventManagerFor(button);
}
private class RightMouseManager : MouseButtonEventManager
diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs
index fe10287491..e0360c6312 100644
--- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
{
private readonly bool noVideo;
- public string TooltipText => button.Enabled.Value ? "Download this beatmap" : "Login to download";
+ public string TooltipText => button.Enabled.Value ? "download this beatmap" : "login to download";
private readonly IBindable localUser = new Bindable();
diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
index 0258a0301a..cd81013c30 100644
--- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
+++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Overlays.BeatmapSet
int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0;
var rate = playCount != 0 ? (float)passCount / playCount : 0;
- successPercent.Text = rate.ToString("P0");
+ successPercent.Text = rate.ToString("0%");
successRate.Length = rate;
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs
index 50fb2782d4..e4e928df18 100644
--- a/osu.Game/Overlays/BeatmapSetOverlay.cs
+++ b/osu.Game/Overlays/BeatmapSetOverlay.cs
@@ -34,6 +34,7 @@ namespace osu.Game.Overlays
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
public BeatmapSetOverlay()
+ : base(OverlayColourScheme.Blue)
{
OsuScrollContainer scroll;
Info info;
diff --git a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs
index 8a82b1f0c0..1d8411dfcc 100644
--- a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs
+++ b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs
@@ -7,11 +7,9 @@ using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays
{
- public abstract class BreadcrumbControlOverlayHeader : OverlayHeader
+ public abstract class BreadcrumbControlOverlayHeader : TabControlOverlayHeader
{
- protected OverlayHeaderBreadcrumbControl BreadcrumbControl;
-
- protected override TabControl CreateTabControl() => BreadcrumbControl = new OverlayHeaderBreadcrumbControl();
+ protected override OsuTabControl CreateTabControl() => new OverlayHeaderBreadcrumbControl();
public class OverlayHeaderBreadcrumbControl : BreadcrumbControl
{
diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
index 7e47a3e29f..4165a180da 100644
--- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs
+++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
@@ -2,14 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
-using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
@@ -27,8 +24,8 @@ namespace osu.Game.Overlays.Changelog
public ChangelogHeader()
{
- BreadcrumbControl.AddItem(listing_string);
- BreadcrumbControl.Current.ValueChanged += e =>
+ TabControl.AddItem(listing_string);
+ TabControl.Current.ValueChanged += e =>
{
if (e.NewValue == listing_string)
ListingSelected?.Invoke();
@@ -38,44 +35,36 @@ namespace osu.Game.Overlays.Changelog
Streams.Current.ValueChanged += e =>
{
- if (e.NewValue?.LatestBuild != null && e.NewValue != Current.Value?.UpdateStream)
+ if (e.NewValue?.LatestBuild != null && !e.NewValue.Equals(Current.Value?.UpdateStream))
Current.Value = e.NewValue.LatestBuild;
};
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- BreadcrumbControl.AccentColour = colours.Violet;
- TitleBackgroundColour = colours.GreyVioletDarker;
- ControlBackgroundColour = colours.GreyVioletDark;
- }
-
private ChangelogHeaderTitle title;
private void showBuild(ValueChangedEvent e)
{
if (e.OldValue != null)
- BreadcrumbControl.RemoveItem(e.OldValue.ToString());
+ TabControl.RemoveItem(e.OldValue.ToString());
if (e.NewValue != null)
{
- BreadcrumbControl.AddItem(e.NewValue.ToString());
- BreadcrumbControl.Current.Value = e.NewValue.ToString();
+ TabControl.AddItem(e.NewValue.ToString());
+ TabControl.Current.Value = e.NewValue.ToString();
- Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == e.NewValue.UpdateStream.Name);
+ updateCurrentStream();
title.Version = e.NewValue.UpdateStream.DisplayName;
}
else
{
- BreadcrumbControl.Current.Value = listing_string;
+ TabControl.Current.Value = listing_string;
Streams.Current.Value = null;
title.Version = null;
}
}
- protected override Drawable CreateBackground() => new HeaderBackground();
+ protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/changelog");
protected override Drawable CreateContent() => new Container
{
@@ -89,19 +78,18 @@ namespace osu.Game.Overlays.Changelog
protected override ScreenTitle CreateTitle() => title = new ChangelogHeaderTitle();
- public class HeaderBackground : Sprite
+ public void Populate(List streams)
{
- public HeaderBackground()
- {
- RelativeSizeAxes = Axes.Both;
- FillMode = FillMode.Fill;
- }
+ Streams.Populate(streams);
+ updateCurrentStream();
+ }
- [BackgroundDependencyLoader]
- private void load(TextureStore textures)
- {
- Texture = textures.Get(@"Headers/changelog");
- }
+ private void updateCurrentStream()
+ {
+ if (Current.Value == null)
+ return;
+
+ Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == Current.Value.UpdateStream.Name);
}
private class ChangelogHeaderTitle : ScreenTitle
@@ -117,12 +105,6 @@ namespace osu.Game.Overlays.Changelog
Version = null;
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- AccentColour = colours.Violet;
- }
-
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
}
}
diff --git a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs b/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs
index 2b48811bd6..ca57ba24e2 100644
--- a/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs
+++ b/osu.Game/Overlays/Changelog/UpdateStreamBadgeArea.cs
@@ -29,8 +29,6 @@ namespace osu.Game.Overlays.Changelog
public void Populate(List streams)
{
- Current.Value = null;
-
foreach (APIUpdateStream updateStream in streams)
AddItem(updateStream);
}
diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs
index 15b0079277..90ba206077 100644
--- a/osu.Game/Overlays/ChangelogOverlay.cs
+++ b/osu.Game/Overlays/ChangelogOverlay.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Overlays
{
public readonly Bindable Current = new Bindable();
- private ChangelogHeader header;
+ protected ChangelogHeader Header;
private Container content;
@@ -34,16 +34,16 @@ namespace osu.Game.Overlays
private List builds;
- private List streams;
+ protected List Streams;
+
+ public ChangelogOverlay()
+ : base(OverlayColourScheme.Purple)
+ {
+ }
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colour)
{
- Waves.FirstWaveColour = colour.GreyVioletLight;
- Waves.SecondWaveColour = colour.GreyViolet;
- Waves.ThirdWaveColour = colour.GreyVioletDark;
- Waves.FourthWaveColour = colour.GreyVioletDarker;
-
Children = new Drawable[]
{
new Box
@@ -62,7 +62,7 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- header = new ChangelogHeader
+ Header = new ChangelogHeader
{
ListingSelected = ShowListing,
},
@@ -78,7 +78,7 @@ namespace osu.Game.Overlays
sampleBack = audio.Samples.Get(@"UI/generic-select-soft");
- header.Current.BindTo(Current);
+ Header.Current.BindTo(Current);
Current.BindValueChanged(e =>
{
@@ -117,7 +117,7 @@ namespace osu.Game.Overlays
performAfterFetch(() =>
{
var build = builds.Find(b => b.Version == version && b.UpdateStream.Name == updateStream)
- ?? streams.Find(s => s.Name == updateStream)?.LatestBuild;
+ ?? Streams.Find(s => s.Name == updateStream)?.LatestBuild;
if (build != null)
ShowBuild(build);
@@ -179,9 +179,9 @@ namespace osu.Game.Overlays
res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id));
builds = res.Builds;
- streams = res.Streams;
+ Streams = res.Streams;
- header.Streams.Populate(res.Streams);
+ Header.Populate(res.Streams);
tcs.SetResult(true);
});
diff --git a/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs
index 31c48deee0..1e58e8b640 100644
--- a/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs
+++ b/osu.Game/Overlays/Chat/Selection/ChannelListItem.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Chat.Selection
private const float text_size = 15;
private const float transition_duration = 100;
- private readonly Channel channel;
+ public readonly Channel Channel;
private readonly Bindable joinedBind = new Bindable();
private readonly OsuSpriteText name;
@@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Chat.Selection
private Color4 topicColour;
private Color4 hoverColour;
- public IEnumerable FilterTerms => new[] { channel.Name, channel.Topic };
+ public IEnumerable FilterTerms => new[] { Channel.Name, Channel.Topic ?? string.Empty };
public bool MatchingFilter
{
@@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Chat.Selection
public ChannelListItem(Channel channel)
{
- this.channel = channel;
+ Channel = channel;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
@@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Chat.Selection
hoverColour = colours.Yellow;
joinedBind.ValueChanged += joined => updateColour(joined.NewValue);
- joinedBind.BindTo(channel.Joined);
+ joinedBind.BindTo(Channel.Joined);
joinedBind.TriggerChange();
FinishTransforms(true);
@@ -156,7 +156,7 @@ namespace osu.Game.Overlays.Chat.Selection
protected override bool OnHover(HoverEvent e)
{
- if (!channel.Joined.Value)
+ if (!Channel.Joined.Value)
name.FadeColour(hoverColour, 50, Easing.OutQuint);
return base.OnHover(e);
@@ -164,7 +164,7 @@ namespace osu.Game.Overlays.Chat.Selection
protected override void OnHoverLost(HoverLostEvent e)
{
- if (!channel.Joined.Value)
+ if (!Channel.Joined.Value)
name.FadeColour(Color4.White, transition_duration);
}
diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
index 4b1d595b44..104495ae01 100644
--- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
+++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs
@@ -9,6 +9,7 @@ using osuTK;
using System;
using System.Linq;
using osu.Framework.Bindables;
+using osu.Framework.Graphics.Containers;
namespace osu.Game.Overlays.Chat.Tabs
{
@@ -81,7 +82,10 @@ namespace osu.Game.Overlays.Chat.Tabs
RemoveItem(channel);
if (Current.Value == channel)
- Current.Value = Items.FirstOrDefault();
+ {
+ // Prefer non-selector channels first
+ Current.Value = Items.FirstOrDefault(c => !(c is ChannelSelectorTabItem.ChannelSelectorTabChannel)) ?? Items.FirstOrDefault();
+ }
}
protected override void SelectTab(TabItem tab)
@@ -110,5 +114,18 @@ namespace osu.Game.Overlays.Chat.Tabs
OnRequestLeave?.Invoke(tab.Value);
}
+
+ protected override TabFillFlowContainer CreateTabFlow() => new ChannelTabFillFlowContainer
+ {
+ Direction = FillDirection.Full,
+ RelativeSizeAxes = Axes.Both,
+ Depth = -1,
+ Masking = true
+ };
+
+ private class ChannelTabFillFlowContainer : TabFillFlowContainer
+ {
+ protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y);
+ }
}
}
diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs
index 266e68f17e..09dc06b95f 100644
--- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs
+++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs
@@ -141,16 +141,13 @@ namespace osu.Game.Overlays.Chat.Tabs
updateState();
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
switch (e.Button)
{
case MouseButton.Middle:
CloseButton.Click();
- return true;
-
- default:
- return false;
+ break;
}
}
diff --git a/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs b/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs
index bde930d4fb..178afda5ac 100644
--- a/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs
+++ b/osu.Game/Overlays/Chat/Tabs/TabCloseButton.cs
@@ -34,10 +34,10 @@ namespace osu.Game.Overlays.Chat.Tabs
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
icon.ScaleTo(0.75f, 1000, Easing.OutElastic);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override bool OnHover(HoverEvent e)
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index 45d311df28..f2aef0995f 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -299,7 +299,7 @@ namespace osu.Game.Overlays
return true;
}
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
if (isDragging)
{
@@ -311,20 +311,20 @@ namespace osu.Game.Overlays
ChatHeight.Value = targetChatHeight;
}
-
- return true;
}
- protected override bool OnDragEnd(DragEndEvent e)
+ protected override void OnDragEnd(DragEndEvent e)
{
isDragging = false;
- return base.OnDragEnd(e);
+ base.OnDragEnd(e);
}
private void selectTab(int index)
{
- var channel = ChannelTabControl.Items.Skip(index).FirstOrDefault();
- if (channel != null && !(channel is ChannelSelectorTabItem.ChannelSelectorTabChannel))
+ var channel = ChannelTabControl.Items
+ .Where(tab => !(tab is ChannelSelectorTabItem.ChannelSelectorTabChannel))
+ .ElementAtOrDefault(index);
+ if (channel != null)
ChannelTabControl.Current.Value = channel;
}
diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs
index 560123eb55..8761b88b1e 100644
--- a/osu.Game/Overlays/Comments/CommentsContainer.cs
+++ b/osu.Game/Overlays/Comments/CommentsContainer.cs
@@ -8,7 +8,6 @@ using osu.Game.Online.API.Requests;
using osu.Framework.Graphics;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Graphics;
using osu.Game.Online.API.Requests.Responses;
using System.Threading;
using System.Linq;
@@ -18,8 +17,8 @@ namespace osu.Game.Overlays.Comments
{
public class CommentsContainer : CompositeDrawable
{
- private readonly CommentableType type;
- private readonly long id;
+ private CommentableType type;
+ private long? id;
public readonly Bindable Sort = new Bindable();
public readonly BindableBool ShowDeleted = new BindableBool();
@@ -27,30 +26,26 @@ namespace osu.Game.Overlays.Comments
[Resolved]
private IAPIProvider api { get; set; }
- [Resolved]
- private OsuColour colours { get; set; }
-
private GetCommentsRequest request;
private CancellationTokenSource loadCancellation;
private int currentPage;
- private readonly Box background;
- private readonly FillFlowContainer content;
- private readonly DeletedChildrenPlaceholder deletedChildrenPlaceholder;
- private readonly CommentsShowMoreButton moreButton;
+ private FillFlowContainer content;
+ private DeletedCommentsCounter deletedCommentsCounter;
+ private CommentsShowMoreButton moreButton;
+ private TotalCommentsCounter commentCounter;
- public CommentsContainer(CommentableType type, long id)
+ [BackgroundDependencyLoader]
+ private void load(OverlayColourProvider colourProvider)
{
- this.type = type;
- this.id = id;
-
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
AddRangeInternal(new Drawable[]
{
- background = new Box
+ new Box
{
RelativeSizeAxes = Axes.Both,
+ Colour = colourProvider.Background5
},
new FillFlowContainer
{
@@ -59,6 +54,7 @@ namespace osu.Game.Overlays.Comments
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
+ commentCounter = new TotalCommentsCounter(),
new CommentsHeader
{
Sort = { BindTarget = Sort },
@@ -72,6 +68,7 @@ namespace osu.Game.Overlays.Comments
},
new Container
{
+ Name = @"Footer",
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
@@ -79,7 +76,7 @@ namespace osu.Game.Overlays.Comments
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = OsuColour.Gray(0.2f)
+ Colour = colourProvider.Background4
},
new FillFlowContainer
{
@@ -88,7 +85,7 @@ namespace osu.Game.Overlays.Comments
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- deletedChildrenPlaceholder = new DeletedChildrenPlaceholder
+ deletedCommentsCounter = new DeletedCommentsCounter
{
ShowDeleted = { BindTarget = ShowDeleted }
},
@@ -101,7 +98,8 @@ namespace osu.Game.Overlays.Comments
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding(5),
- Action = getComments
+ Action = getComments,
+ IsLoading = true,
}
}
}
@@ -113,19 +111,29 @@ namespace osu.Game.Overlays.Comments
});
}
- [BackgroundDependencyLoader]
- private void load()
- {
- background.Colour = colours.Gray2;
- }
-
protected override void LoadComplete()
{
- Sort.BindValueChanged(onSortChanged, true);
+ Sort.BindValueChanged(_ => refetchComments(), true);
base.LoadComplete();
}
- private void onSortChanged(ValueChangedEvent sort)
+ /// The type of resource to get comments for.
+ /// The id of the resource to get comments for.
+ public void ShowComments(CommentableType type, long id)
+ {
+ this.type = type;
+ this.id = id;
+
+ if (!IsLoaded)
+ return;
+
+ // only reset when changing ID/type. other refetch ops are generally just changing sort order.
+ commentCounter.Current.Value = 0;
+
+ refetchComments();
+ }
+
+ private void refetchComments()
{
clearComments();
getComments();
@@ -133,9 +141,12 @@ namespace osu.Game.Overlays.Comments
private void getComments()
{
+ if (!id.HasValue)
+ return;
+
request?.Cancel();
loadCancellation?.Cancel();
- request = new GetCommentsRequest(type, id, Sort.Value, currentPage++);
+ request = new GetCommentsRequest(type, id.Value, Sort.Value, currentPage++);
request.Success += onSuccess;
api.Queue(request);
}
@@ -143,7 +154,8 @@ namespace osu.Game.Overlays.Comments
private void clearComments()
{
currentPage = 1;
- deletedChildrenPlaceholder.DeletedCount.Value = 0;
+ deletedCommentsCounter.Count.Value = 0;
+ moreButton.Show();
moreButton.IsLoading = true;
content.Clear();
}
@@ -152,29 +164,14 @@ namespace osu.Game.Overlays.Comments
{
loadCancellation = new CancellationTokenSource();
- FillFlowContainer page = new FillFlowContainer
+ LoadComponentAsync(new CommentsPage(response)
{
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- };
-
- foreach (var c in response.Comments)
- {
- if (c.IsTopLevel)
- {
- page.Add(new DrawableComment(c)
- {
- ShowDeleted = { BindTarget = ShowDeleted }
- });
- }
- }
-
- LoadComponentAsync(page, loaded =>
+ ShowDeleted = { BindTarget = ShowDeleted }
+ }, loaded =>
{
content.Add(loaded);
- deletedChildrenPlaceholder.DeletedCount.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel);
+ deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel);
if (response.HasMore)
{
@@ -184,8 +181,12 @@ namespace osu.Game.Overlays.Comments
moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments;
moreButton.IsLoading = false;
}
+ else
+ {
+ moreButton.Hide();
+ }
- moreButton.FadeTo(response.HasMore ? 1 : 0);
+ commentCounter.Current.Value = response.Total;
}, loadCancellation.Token);
}
diff --git a/osu.Game/Overlays/Comments/CommentsHeader.cs b/osu.Game/Overlays/Comments/CommentsHeader.cs
index 6a7a678cc7..ad80e67330 100644
--- a/osu.Game/Overlays/Comments/CommentsHeader.cs
+++ b/osu.Game/Overlays/Comments/CommentsHeader.cs
@@ -76,9 +76,9 @@ namespace osu.Game.Overlays.Comments
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OverlayColourProvider colourProvider)
{
- background.Colour = colours.Gray3;
+ background.Colour = colourProvider.Background4;
}
private class ShowDeletedButton : HeaderButton
diff --git a/osu.Game/Overlays/Comments/CommentsPage.cs b/osu.Game/Overlays/Comments/CommentsPage.cs
new file mode 100644
index 0000000000..153ce676eb
--- /dev/null
+++ b/osu.Game/Overlays/Comments/CommentsPage.cs
@@ -0,0 +1,92 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics;
+using osu.Framework.Bindables;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics.Sprites;
+using System.Linq;
+
+namespace osu.Game.Overlays.Comments
+{
+ public class CommentsPage : CompositeDrawable
+ {
+ public readonly BindableBool ShowDeleted = new BindableBool();
+
+ private readonly CommentBundle commentBundle;
+
+ public CommentsPage(CommentBundle commentBundle)
+ {
+ this.commentBundle = commentBundle;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OverlayColourProvider colourProvider)
+ {
+ FillFlowContainer flow;
+
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ AddRangeInternal(new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colourProvider.Background5
+ },
+ flow = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ }
+ });
+
+ if (!commentBundle.Comments.Any())
+ {
+ flow.Add(new NoCommentsPlaceholder());
+ return;
+ }
+
+ foreach (var c in commentBundle.Comments)
+ {
+ if (c.IsTopLevel)
+ {
+ flow.Add(new DrawableComment(c)
+ {
+ ShowDeleted = { BindTarget = ShowDeleted }
+ });
+ }
+ }
+ }
+
+ private class NoCommentsPlaceholder : CompositeDrawable
+ {
+ [BackgroundDependencyLoader]
+ private void load(OverlayColourProvider colourProvider)
+ {
+ Height = 80;
+ RelativeSizeAxes = Axes.X;
+ AddRangeInternal(new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colourProvider.Background4
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Margin = new MarginPadding { Left = 50 },
+ Text = @"No comments yet."
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs b/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs
index b0174e7b1a..d2ff7ecb1f 100644
--- a/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs
+++ b/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs
@@ -1,8 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Allocation;
using osu.Framework.Bindables;
-using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Comments
@@ -11,11 +11,14 @@ namespace osu.Game.Overlays.Comments
{
public readonly BindableInt Current = new BindableInt();
- public CommentsShowMoreButton()
+ [BackgroundDependencyLoader]
+ private void load(OverlayColourProvider colourProvider)
{
- IdleColour = OsuColour.Gray(0.3f);
- HoverColour = OsuColour.Gray(0.4f);
- ChevronIconColour = OsuColour.Gray(0.5f);
+ Height = 20;
+
+ IdleColour = colourProvider.Background2;
+ HoverColour = colourProvider.Background1;
+ ChevronIconColour = colourProvider.Foreground1;
}
protected override void LoadComplete()
diff --git a/osu.Game/Overlays/Comments/DeletedChildrenPlaceholder.cs b/osu.Game/Overlays/Comments/DeletedCommentsCounter.cs
similarity index 51%
rename from osu.Game/Overlays/Comments/DeletedChildrenPlaceholder.cs
rename to osu.Game/Overlays/Comments/DeletedCommentsCounter.cs
index 6b41453b91..f22086bf23 100644
--- a/osu.Game/Overlays/Comments/DeletedChildrenPlaceholder.cs
+++ b/osu.Game/Overlays/Comments/DeletedCommentsCounter.cs
@@ -12,51 +12,56 @@ using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays.Comments
{
- public class DeletedChildrenPlaceholder : FillFlowContainer
+ public class DeletedCommentsCounter : CompositeDrawable
{
public readonly BindableBool ShowDeleted = new BindableBool();
- public readonly BindableInt DeletedCount = new BindableInt();
+
+ public readonly BindableInt Count = new BindableInt();
private readonly SpriteText countText;
- public DeletedChildrenPlaceholder()
+ public DeletedCommentsCounter()
{
AutoSizeAxes = Axes.Both;
- Direction = FillDirection.Horizontal;
- Spacing = new Vector2(3, 0);
Margin = new MarginPadding { Vertical = 10, Left = 80 };
- Children = new Drawable[]
+
+ InternalChild = new FillFlowContainer
{
- new SpriteIcon
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(3, 0),
+ Children = new Drawable[]
{
- Icon = FontAwesome.Solid.Trash,
- Size = new Vector2(14),
- },
- countText = new OsuSpriteText
- {
- Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
+ new SpriteIcon
+ {
+ Icon = FontAwesome.Solid.Trash,
+ Size = new Vector2(14),
+ },
+ countText = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
+ }
}
};
}
protected override void LoadComplete()
{
- DeletedCount.BindValueChanged(_ => updateDisplay(), true);
- ShowDeleted.BindValueChanged(_ => updateDisplay(), true);
base.LoadComplete();
+
+ Count.BindValueChanged(_ => updateDisplay(), true);
+ ShowDeleted.BindValueChanged(_ => updateDisplay(), true);
}
private void updateDisplay()
{
- if (DeletedCount.Value != 0)
+ if (!ShowDeleted.Value && Count.Value != 0)
{
- countText.Text = @"deleted comment".ToQuantity(DeletedCount.Value);
- this.FadeTo(ShowDeleted.Value ? 0 : 1);
+ countText.Text = @"deleted comment".ToQuantity(Count.Value);
+ Show();
}
else
- {
Hide();
- }
}
}
}
diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs
index bdae9da226..70ea478814 100644
--- a/osu.Game/Overlays/Comments/DrawableComment.cs
+++ b/osu.Game/Overlays/Comments/DrawableComment.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Comments
{
LinkFlowContainer username;
FillFlowContainer childCommentsContainer;
- DeletedChildrenPlaceholder deletedChildrenPlaceholder;
+ DeletedCommentsCounter deletedCommentsCounter;
FillFlowContainer info;
LinkFlowContainer message;
GridContainer content;
@@ -184,7 +184,7 @@ namespace osu.Game.Overlays.Comments
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical
},
- deletedChildrenPlaceholder = new DeletedChildrenPlaceholder
+ deletedCommentsCounter = new DeletedCommentsCounter
{
ShowDeleted = { BindTarget = ShowDeleted }
}
@@ -193,7 +193,7 @@ namespace osu.Game.Overlays.Comments
}
};
- deletedChildrenPlaceholder.DeletedCount.Value = comment.DeletedChildrenCount;
+ deletedCommentsCounter.Count.Value = comment.DeletedChildrenCount;
if (comment.UserId.HasValue)
username.AddUserLink(comment.User);
@@ -213,7 +213,7 @@ namespace osu.Game.Overlays.Comments
if (comment.HasMessage)
{
- var formattedSource = MessageFormatter.FormatText(comment.GetMessage);
+ var formattedSource = MessageFormatter.FormatText(comment.Message);
message.AddLinks(formattedSource.Text, formattedSource.Links);
}
@@ -343,7 +343,7 @@ namespace osu.Game.Overlays.Comments
if (parentComment == null)
return string.Empty;
- return parentComment.HasMessage ? parentComment.GetMessage : parentComment.IsDeleted ? @"deleted" : string.Empty;
+ return parentComment.HasMessage ? parentComment.Message : parentComment.IsDeleted ? @"deleted" : string.Empty;
}
}
}
diff --git a/osu.Game/Overlays/Comments/HeaderButton.cs b/osu.Game/Overlays/Comments/HeaderButton.cs
index 8789cf5830..fdc8db35ab 100644
--- a/osu.Game/Overlays/Comments/HeaderButton.cs
+++ b/osu.Game/Overlays/Comments/HeaderButton.cs
@@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
@@ -45,9 +44,9 @@ namespace osu.Game.Overlays.Comments
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OverlayColourProvider colourProvider)
{
- background.Colour = colours.Gray4;
+ background.Colour = colourProvider.Background3;
}
protected override bool OnHover(HoverEvent e)
diff --git a/osu.Game/Overlays/Comments/SortTabControl.cs b/osu.Game/Overlays/Comments/SortTabControl.cs
index a114197b8d..700d63351f 100644
--- a/osu.Game/Overlays/Comments/SortTabControl.cs
+++ b/osu.Game/Overlays/Comments/SortTabControl.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Comments
public readonly BindableBool Active = new BindableBool();
[Resolved]
- private OsuColour colours { get; set; }
+ private OverlayColourProvider colourProvider { get; set; }
private readonly SpriteText text;
@@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Comments
updateBackgroundState();
text.Font = text.Font.With(weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium);
- text.Colour = active.NewValue ? colours.BlueLighter : Color4.White;
+ text.Colour = active.NewValue ? colourProvider.Light1 : Color4.White;
}, true);
}
diff --git a/osu.Game/Overlays/Comments/TotalCommentsCounter.cs b/osu.Game/Overlays/Comments/TotalCommentsCounter.cs
index 376853c1de..1bb9b52689 100644
--- a/osu.Game/Overlays/Comments/TotalCommentsCounter.cs
+++ b/osu.Game/Overlays/Comments/TotalCommentsCounter.cs
@@ -5,7 +5,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
-using osu.Framework.Graphics.Sprites;
using osuTK;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -17,9 +16,10 @@ namespace osu.Game.Overlays.Comments
{
public readonly BindableInt Current = new BindableInt();
- private readonly SpriteText counter;
+ private OsuSpriteText counter;
- public TotalCommentsCounter()
+ [BackgroundDependencyLoader]
+ private void load(OverlayColourProvider colourProvider)
{
RelativeSizeAxes = Axes.X;
Height = 50;
@@ -38,6 +38,7 @@ namespace osu.Game.Overlays.Comments
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 20, italics: true),
+ Colour = colourProvider.Light1,
Text = @"Comments"
},
new CircularContainer
@@ -51,14 +52,15 @@ namespace osu.Game.Overlays.Comments
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = OsuColour.Gray(0.05f)
+ Colour = colourProvider.Background6
},
counter = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Horizontal = 10, Vertical = 5 },
- Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold)
+ Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
+ Colour = colourProvider.Foreground1
}
},
}
@@ -66,12 +68,6 @@ namespace osu.Game.Overlays.Comments
});
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- counter.Colour = colours.BlueLighter;
- }
-
protected override void LoadComplete()
{
Current.BindValueChanged(value => counter.Text = value.NewValue.ToString("N0"), true);
diff --git a/osu.Game/Overlays/Direct/PanelDownloadButton.cs b/osu.Game/Overlays/Direct/PanelDownloadButton.cs
index 4037cd46f3..ed44f1e960 100644
--- a/osu.Game/Overlays/Direct/PanelDownloadButton.cs
+++ b/osu.Game/Overlays/Direct/PanelDownloadButton.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Direct
if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false)
{
button.Enabled.Value = false;
- button.TooltipText = "This beatmap is currently not available for download.";
+ button.TooltipText = "this beatmap is currently not available for download.";
return;
}
diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs
index 9daf55c796..e4cef319fe 100644
--- a/osu.Game/Overlays/DirectOverlay.cs
+++ b/osu.Game/Overlays/DirectOverlay.cs
@@ -84,14 +84,8 @@ namespace osu.Game.Overlays
}
public DirectOverlay()
+ : base(OverlayColourScheme.Blue)
{
- // osu!direct colours are not part of the standard palette
-
- Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2");
- Waves.SecondWaveColour = OsuColour.FromHex(@"2280a2");
- Waves.ThirdWaveColour = OsuColour.FromHex(@"005774");
- Waves.FourthWaveColour = OsuColour.FromHex(@"003a4e");
-
ScrollFlow.Children = new Drawable[]
{
resultCountsContainer = new FillFlowContainer
diff --git a/osu.Game/Overlays/FullscreenOverlay.cs b/osu.Game/Overlays/FullscreenOverlay.cs
index 0911ee84de..3464ce6086 100644
--- a/osu.Game/Overlays/FullscreenOverlay.cs
+++ b/osu.Game/Overlays/FullscreenOverlay.cs
@@ -6,7 +6,6 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
-using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osuTK.Graphics;
@@ -18,12 +17,12 @@ namespace osu.Game.Overlays
[Resolved]
protected IAPIProvider API { get; private set; }
- protected FullscreenOverlay()
+ [Cached]
+ protected readonly OverlayColourProvider ColourProvider;
+
+ protected FullscreenOverlay(OverlayColourScheme colourScheme)
{
- Waves.FirstWaveColour = OsuColour.Gray(0.4f);
- Waves.SecondWaveColour = OsuColour.Gray(0.3f);
- Waves.ThirdWaveColour = OsuColour.Gray(0.2f);
- Waves.FourthWaveColour = OsuColour.Gray(0.1f);
+ ColourProvider = new OverlayColourProvider(colourScheme);
RelativeSizeAxes = Axes.Both;
RelativePositionAxes = Axes.Both;
@@ -41,6 +40,15 @@ namespace osu.Game.Overlays
};
}
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Waves.FirstWaveColour = ColourProvider.Light4;
+ Waves.SecondWaveColour = ColourProvider.Light3;
+ Waves.ThirdWaveColour = ColourProvider.Dark4;
+ Waves.FourthWaveColour = ColourProvider.Dark3;
+ }
+
public override void Show()
{
if (State.Value == Visibility.Visible)
diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
index 8317951c8a..d2fcc2652a 100644
--- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
+++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
@@ -177,17 +177,19 @@ namespace osu.Game.Overlays.KeyBinding
return true;
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
// don't do anything until the last button is released.
if (!HasFocus || e.HasAnyButtonPressed)
- return base.OnMouseUp(e);
+ {
+ base.OnMouseUp(e);
+ return;
+ }
if (bindTarget.IsHovered)
finalise();
else
updateBindTarget();
- return true;
}
protected override bool OnScroll(ScrollEvent e)
@@ -216,12 +218,15 @@ namespace osu.Game.Overlays.KeyBinding
return true;
}
- protected override bool OnKeyUp(KeyUpEvent e)
+ protected override void OnKeyUp(KeyUpEvent e)
{
- if (!HasFocus) return base.OnKeyUp(e);
+ if (!HasFocus)
+ {
+ base.OnKeyUp(e);
+ return;
+ }
finalise();
- return true;
}
protected override bool OnJoystickPress(JoystickPressEvent e)
@@ -235,13 +240,15 @@ namespace osu.Game.Overlays.KeyBinding
return true;
}
- protected override bool OnJoystickRelease(JoystickReleaseEvent e)
+ protected override void OnJoystickRelease(JoystickReleaseEvent e)
{
if (!HasFocus)
- return base.OnJoystickRelease(e);
+ {
+ base.OnJoystickRelease(e);
+ return;
+ }
finalise();
- return true;
}
private void clear()
@@ -313,14 +320,6 @@ namespace osu.Game.Overlays.KeyBinding
Size = new Vector2(80, 20);
}
- protected override bool OnMouseUp(MouseUpEvent e)
- {
- base.OnMouseUp(e);
-
- // without this, the mouse up triggers a finalise (and deselection) of the current binding target.
- return true;
- }
-
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs
index 69a4a4181a..e574828cd2 100644
--- a/osu.Game/Overlays/Mods/ModButton.cs
+++ b/osu.Game/Overlays/Mods/ModButton.cs
@@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Mods
foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing);
backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing);
- backgroundIcon.Icon = modAfter.Icon;
+ backgroundIcon.Mod = modAfter;
using (BeginDelayedSequence(mod_switch_duration, true))
{
@@ -158,7 +158,7 @@ namespace osu.Game.Overlays.Mods
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
scaleContainer.ScaleTo(1, 500, Easing.OutElastic);
@@ -172,8 +172,6 @@ namespace osu.Game.Overlays.Mods
break;
}
}
-
- return true;
}
protected override bool OnClick(ClickEvent e)
@@ -218,8 +216,8 @@ namespace osu.Game.Overlays.Mods
private void displayMod(Mod mod)
{
if (backgroundIcon != null)
- backgroundIcon.Icon = foregroundIcon.Icon;
- foregroundIcon.Icon = mod.Icon;
+ backgroundIcon.Mod = foregroundIcon.Mod;
+ foregroundIcon.Mod = mod;
text.Text = mod.Name;
Colour = mod.HasImplementation ? Color4.White : Color4.Gray;
}
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index 7f07ce620c..6afe398172 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Mods
protected readonly Container ModSettingsContainer;
- protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty());
+ public readonly Bindable> SelectedMods = new Bindable>(Array.Empty());
private Bindable>> availableMods;
@@ -321,14 +321,13 @@ namespace osu.Game.Overlays.Mods
}
[BackgroundDependencyLoader(true)]
- private void load(OsuColour colours, AudioManager audio, Bindable> selectedMods, OsuGameBase osu)
+ private void load(OsuColour colours, AudioManager audio, OsuGameBase osu)
{
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
UnrankedLabel.Colour = colours.Blue;
availableMods = osu.AvailableMods.GetBoundCopy();
- SelectedMods.BindTo(selectedMods);
sampleOn = audio.Samples.Get(@"UI/check-on");
sampleOff = audio.Samples.Get(@"UI/check-off");
@@ -473,7 +472,10 @@ namespace osu.Game.Overlays.Mods
if (selectedMod != null)
{
if (State.Value == Visibility.Visible) sampleOn?.Play();
+
DeselectTypes(selectedMod.IncompatibleMods, true);
+
+ if (selectedMod.RequiresConfiguration) ModSettingsContainer.Alpha = 1;
}
else
{
diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs
index 29b6ae00f3..d40f391982 100644
--- a/osu.Game/Overlays/Music/PlaylistItem.cs
+++ b/osu.Game/Overlays/Music/PlaylistItem.cs
@@ -43,10 +43,10 @@ namespace osu.Game.Overlays.Music
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
IsDraggable = false;
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
private bool selected;
diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs
index 3cd04ac809..7bdcab6dff 100644
--- a/osu.Game/Overlays/Music/PlaylistList.cs
+++ b/osu.Game/Overlays/Music/PlaylistList.cs
@@ -136,29 +136,29 @@ namespace osu.Game.Overlays.Music
return draggedItem != null || base.OnDragStart(e);
}
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
nativeDragPosition = e.ScreenSpaceMousePosition;
- if (draggedItem == null)
- return base.OnDrag(e);
- return true;
+ if (draggedItem == null)
+ base.OnDrag(e);
}
- protected override bool OnDragEnd(DragEndEvent e)
+ protected override void OnDragEnd(DragEndEvent e)
{
nativeDragPosition = e.ScreenSpaceMousePosition;
if (draggedItem == null)
- return base.OnDragEnd(e);
+ {
+ base.OnDragEnd(e);
+ return;
+ }
if (dragDestination != null)
musicController.ChangeBeatmapSetPosition(draggedItem.BeatmapSetInfo, dragDestination.Value);
draggedItem = null;
dragDestination = null;
-
- return true;
}
protected override void Update()
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index 3c0f6468bc..7c7daf6eb9 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -196,7 +196,7 @@ namespace osu.Game.Overlays
if (!instant)
queuedDirection = TrackChangeDirection.Next;
- var playable = BeatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? BeatmapSets.FirstOrDefault();
+ var playable = BeatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).ElementAtOrDefault(1) ?? BeatmapSets.FirstOrDefault();
if (playable != null)
{
@@ -326,7 +326,9 @@ namespace osu.Game.Overlays
return false;
}
- public bool OnReleased(GlobalAction action) => false;
+ public void OnReleased(GlobalAction action)
+ {
+ }
public class MusicControllerToast : Toast
{
diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs
index fc88c86df2..b525ba7a82 100644
--- a/osu.Game/Overlays/News/NewsHeader.cs
+++ b/osu.Game/Overlays/News/NewsHeader.cs
@@ -1,12 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
-using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using System;
@@ -24,9 +20,9 @@ namespace osu.Game.Overlays.News
public NewsHeader()
{
- BreadcrumbControl.AddItem(front_page_string);
+ TabControl.AddItem(front_page_string);
- BreadcrumbControl.Current.ValueChanged += e =>
+ TabControl.Current.ValueChanged += e =>
{
if (e.NewValue == front_page_string)
ShowFrontPage?.Invoke();
@@ -35,52 +31,29 @@ namespace osu.Game.Overlays.News
Current.ValueChanged += showPost;
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- BreadcrumbControl.AccentColour = colours.Violet;
- TitleBackgroundColour = colours.GreyVioletDarker;
- ControlBackgroundColour = colours.GreyVioletDark;
- }
-
private void showPost(ValueChangedEvent e)
{
if (e.OldValue != null)
- BreadcrumbControl.RemoveItem(e.OldValue);
+ TabControl.RemoveItem(e.OldValue);
if (e.NewValue != null)
{
- BreadcrumbControl.AddItem(e.NewValue);
- BreadcrumbControl.Current.Value = e.NewValue;
+ TabControl.AddItem(e.NewValue);
+ TabControl.Current.Value = e.NewValue;
title.IsReadingPost = true;
}
else
{
- BreadcrumbControl.Current.Value = front_page_string;
+ TabControl.Current.Value = front_page_string;
title.IsReadingPost = false;
}
}
- protected override Drawable CreateBackground() => new NewsHeaderBackground();
+ protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news");
protected override ScreenTitle CreateTitle() => title = new NewsHeaderTitle();
- private class NewsHeaderBackground : Sprite
- {
- public NewsHeaderBackground()
- {
- RelativeSizeAxes = Axes.Both;
- FillMode = FillMode.Fill;
- }
-
- [BackgroundDependencyLoader]
- private void load(TextureStore textures)
- {
- Texture = textures.Get(@"Headers/news");
- }
- }
-
private class NewsHeaderTitle : ScreenTitle
{
private const string post_string = "post";
@@ -97,12 +70,6 @@ namespace osu.Game.Overlays.News
}
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/news");
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- AccentColour = colours.Violet;
- }
}
}
}
diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs
index e7471cb21d..6dde300556 100644
--- a/osu.Game/Overlays/NewsOverlay.cs
+++ b/osu.Game/Overlays/NewsOverlay.cs
@@ -21,6 +21,11 @@ namespace osu.Game.Overlays
public readonly Bindable Current = new Bindable(null);
+ public NewsOverlay()
+ : base(OverlayColourScheme.Purple)
+ {
+ }
+
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs
index a8ba7fa427..042e95c6d7 100644
--- a/osu.Game/Overlays/NowPlayingOverlay.cs
+++ b/osu.Game/Overlays/NowPlayingOverlay.cs
@@ -385,7 +385,7 @@ namespace osu.Game.Overlays
return true;
}
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
Vector2 change = e.MousePosition - e.MouseDownPosition;
@@ -393,13 +393,12 @@ namespace osu.Game.Overlays
change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.7f) / change.Length;
this.MoveTo(change);
- return true;
}
- protected override bool OnDragEnd(DragEndEvent e)
+ protected override void OnDragEnd(DragEndEvent e)
{
this.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
- return base.OnDragEnd(e);
+ base.OnDragEnd(e);
}
}
diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs
new file mode 100644
index 0000000000..9816f313ad
--- /dev/null
+++ b/osu.Game/Overlays/OverlayColourProvider.cs
@@ -0,0 +1,80 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays
+{
+ public class OverlayColourProvider
+ {
+ private readonly OverlayColourScheme colourScheme;
+
+ public OverlayColourProvider(OverlayColourScheme colourScheme)
+ {
+ this.colourScheme = colourScheme;
+ }
+
+ public Color4 Highlight1 => getColour(1, 0.7f);
+ public Color4 Content1 => getColour(0.4f, 1);
+ public Color4 Content2 => getColour(0.4f, 0.9f);
+ public Color4 Light1 => getColour(0.4f, 0.8f);
+ public Color4 Light2 => getColour(0.4f, 0.75f);
+ public Color4 Light3 => getColour(0.4f, 0.7f);
+ public Color4 Light4 => getColour(0.4f, 0.5f);
+ public Color4 Dark1 => getColour(0.2f, 0.35f);
+ public Color4 Dark2 => getColour(0.2f, 0.3f);
+ public Color4 Dark3 => getColour(0.2f, 0.25f);
+ public Color4 Dark4 => getColour(0.2f, 0.2f);
+ public Color4 Dark5 => getColour(0.2f, 0.15f);
+ public Color4 Dark6 => getColour(0.2f, 0.1f);
+ public Color4 Foreground1 => getColour(0.1f, 0.6f);
+ public Color4 Background1 => getColour(0.1f, 0.4f);
+ public Color4 Background2 => getColour(0.1f, 0.3f);
+ public Color4 Background3 => getColour(0.1f, 0.25f);
+ public Color4 Background4 => getColour(0.1f, 0.2f);
+ public Color4 Background5 => getColour(0.1f, 0.15f);
+ public Color4 Background6 => getColour(0.1f, 0.1f);
+
+ private Color4 getColour(float saturation, float lightness) => Color4.FromHsl(new Vector4(getBaseHue(colourScheme), saturation, lightness, 1));
+
+ // See https://github.com/ppy/osu-web/blob/4218c288292d7c810b619075471eaea8bbb8f9d8/app/helpers.php#L1463
+ private static float getBaseHue(OverlayColourScheme colourScheme)
+ {
+ switch (colourScheme)
+ {
+ default:
+ throw new ArgumentException($@"{colourScheme} colour scheme does not provide a hue value in {nameof(getBaseHue)}.");
+
+ case OverlayColourScheme.Red:
+ return 0;
+
+ case OverlayColourScheme.Pink:
+ return 333 / 360f;
+
+ case OverlayColourScheme.Orange:
+ return 46 / 360f;
+
+ case OverlayColourScheme.Green:
+ return 115 / 360f;
+
+ case OverlayColourScheme.Purple:
+ return 255 / 360f;
+
+ case OverlayColourScheme.Blue:
+ return 200 / 360f;
+ }
+ }
+ }
+
+ public enum OverlayColourScheme
+ {
+ Red,
+ Pink,
+ Orange,
+ Green,
+ Purple,
+ Blue
+ }
+}
diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs
index 53da2da634..bedf8e5435 100644
--- a/osu.Game/Overlays/OverlayHeader.cs
+++ b/osu.Game/Overlays/OverlayHeader.cs
@@ -2,10 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using JetBrains.Annotations;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
@@ -14,23 +14,9 @@ namespace osu.Game.Overlays
public abstract class OverlayHeader : Container
{
private readonly Box titleBackground;
- private readonly Box controlBackground;
- private readonly Container background;
+ private readonly ScreenTitle title;
- protected Color4 TitleBackgroundColour
- {
- set => titleBackground.Colour = value;
- }
-
- protected Color4 ControlBackgroundColour
- {
- set => controlBackground.Colour = value;
- }
-
- protected float BackgroundHeight
- {
- set => background.Height = value;
- }
+ protected readonly FillFlowContainer HeaderInfo;
protected OverlayHeader()
{
@@ -44,47 +30,51 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical,
Children = new[]
{
- background = new Container
- {
- RelativeSizeAxes = Axes.X,
- Height = 80,
- Masking = true,
- Child = CreateBackground()
- },
- new Container
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Children = new Drawable[]
- {
- titleBackground = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Gray,
- },
- CreateTitle().With(title =>
- {
- title.Margin = new MarginPadding
- {
- Vertical = 10,
- Left = UserProfileOverlay.CONTENT_X_MARGIN
- };
- })
- }
- },
- new Container
+ HeaderInfo = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
Depth = -float.MaxValue,
- Children = new Drawable[]
+ Children = new[]
{
- controlBackground = new Box
+ CreateBackground(),
+ new Container
{
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Gray,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ titleBackground = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Gray,
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Padding = new MarginPadding
+ {
+ Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
+ Vertical = 10,
+ },
+ Children = new[]
+ {
+ title = CreateTitle().With(title =>
+ {
+ title.Anchor = Anchor.CentreLeft;
+ title.Origin = Anchor.CentreLeft;
+ }),
+ CreateTitleContent().With(content =>
+ {
+ content.Anchor = Anchor.CentreRight;
+ content.Origin = Anchor.CentreRight;
+ })
+ }
+ }
+ }
},
- CreateTabControl().With(control => control.Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN })
}
},
CreateContent()
@@ -92,13 +82,25 @@ namespace osu.Game.Overlays
});
}
- protected abstract Drawable CreateBackground();
+ [BackgroundDependencyLoader]
+ private void load(OverlayColourProvider colourProvider)
+ {
+ titleBackground.Colour = colourProvider.Dark5;
+ title.AccentColour = colourProvider.Highlight1;
+ }
[NotNull]
- protected virtual Drawable CreateContent() => new Container();
+ protected virtual Drawable CreateContent() => Empty();
+
+ [NotNull]
+ protected virtual Drawable CreateBackground() => Empty();
+
+ ///
+ /// Creates a on the opposite side of the . Used mostly to create .
+ ///
+ [NotNull]
+ protected virtual Drawable CreateTitleContent() => Empty();
protected abstract ScreenTitle CreateTitle();
-
- protected abstract TabControl CreateTabControl();
}
}
diff --git a/osu.Game/Overlays/OverlayHeaderBackground.cs b/osu.Game/Overlays/OverlayHeaderBackground.cs
new file mode 100644
index 0000000000..2fef593285
--- /dev/null
+++ b/osu.Game/Overlays/OverlayHeaderBackground.cs
@@ -0,0 +1,43 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+
+namespace osu.Game.Overlays
+{
+ public class OverlayHeaderBackground : CompositeDrawable
+ {
+ public OverlayHeaderBackground(string textureName)
+ {
+ Height = 80;
+ RelativeSizeAxes = Axes.X;
+ Masking = true;
+ InternalChild = new Background(textureName);
+ }
+
+ private class Background : Sprite
+ {
+ private readonly string textureName;
+
+ public Background(string textureName)
+ {
+ this.textureName = textureName;
+
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ RelativeSizeAxes = Axes.Both;
+ FillMode = FillMode.Fill;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ Texture = textures.Get(textureName);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/OverlayRulesetSelector.cs b/osu.Game/Overlays/OverlayRulesetSelector.cs
new file mode 100644
index 0000000000..b73d38eeb3
--- /dev/null
+++ b/osu.Game/Overlays/OverlayRulesetSelector.cs
@@ -0,0 +1,28 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Rulesets;
+using osuTK;
+
+namespace osu.Game.Overlays
+{
+ public class OverlayRulesetSelector : RulesetSelector
+ {
+ public OverlayRulesetSelector()
+ {
+ AutoSizeAxes = Axes.Both;
+ }
+
+ protected override TabItem