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..5497a82a7a 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.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/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/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/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/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index f74f2d7bc5..3162f4b379 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -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..20b31c68f2 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,16 +34,7 @@ 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();
@@ -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/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/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
index 6433ced624..a463542e64 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
@@ -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..ca3030db3e 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
@@ -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..e825df5a3f 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(() =>
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/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/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/TestSceneEditorComposeTimeline.cs b/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs
similarity index 92%
rename from osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs
rename to osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs
index 29575cb42e..e7b2508ac7 100644
--- a/osu.Game.Tests/Visual/Editor/TestSceneEditorComposeTimeline.cs
+++ b/osu.Game.Tests/Visual/Editor/TestSceneTimelineBlueprintContainer.cs
@@ -13,6 +13,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;
@@ -22,12 +23,11 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
- public class TestSceneEditorComposeTimeline : EditorClockTestScene
+ public class TestSceneTimelineBlueprintContainer : EditorClockTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
typeof(TimelineArea),
- typeof(TimelineHitObjectDisplay),
typeof(Timeline),
typeof(TimelineButton),
typeof(CentreMarker)
@@ -38,7 +38,10 @@ namespace osu.Game.Tests.Visual.Editor
{
Beatmap.Value = new WaveformTestBeatmap(audio);
- var editorBeatmap = new EditorBeatmap((Beatmap)Beatmap.Value.Beatmap);
+ var editorBeatmap = new EditorBeatmap((Beatmap)Beatmap.Value.Beatmap, BeatDivisor);
+
+ Dependencies.Cache(editorBeatmap);
+ Dependencies.CacheAs(editorBeatmap);
Children = new Drawable[]
{
@@ -55,7 +58,7 @@ namespace osu.Game.Tests.Visual.Editor
},
new TimelineArea
{
- Child = new TimelineHitObjectDisplay(editorBeatmap),
+ Child = new TimelineBlueprintContainer(),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
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/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/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/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/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
index 86bd0ddd11..8134c10750 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs
@@ -30,29 +30,23 @@ namespace osu.Game.Tests.Visual.Online
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/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/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/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/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
index 8dcb7dcbf8..e2ce2341e5 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
@@ -25,6 +25,8 @@ namespace osu.Game.Tests.Visual.UserInterface
private readonly Mod testCustomisableMod = new TestModCustomisable1();
+ private readonly Mod testCustomisableAutoOpenMod = new TestModCustomisable2();
+
[Test]
public void TestButtonShowsOnCustomisableMod()
{
@@ -53,6 +55,17 @@ 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", () =>
@@ -128,6 +141,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/TestSceneScreenBreadcrumbControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenBreadcrumbControl.cs
index 0cb8683d72..7386e0fa1f 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;
@@ -62,7 +61,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/TournamentFont.cs b/osu.Game.Tournament/TournamentFont.cs
index f9e60ff2bc..32f0264562 100644
--- a/osu.Game.Tournament/TournamentFont.cs
+++ b/osu.Game.Tournament/TournamentFont.cs
@@ -61,7 +61,7 @@ namespace osu.Game.Tournament
string weightString = weight.ToString();
// Only exo has an explicit "regular" weight, other fonts do not
- if (weight == FontWeight.Regular && family != GetFamilyString(TournamentTypeface.Aquatico) && family != GetFamilyString(TournamentTypeface.Aquatico))
+ if (weight == FontWeight.Regular && family != GetFamilyString(TournamentTypeface.Aquatico))
weightString = string.Empty;
return weightString;
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/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/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/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs
index 2dc12b3e67..53a40f5613 100644
--- a/osu.Game/Graphics/OsuColour.cs
+++ b/osu.Game/Graphics/OsuColour.cs
@@ -3,6 +3,7 @@
using System;
using osu.Game.Beatmaps;
+using osuTK;
using osuTK.Graphics;
namespace osu.Game.Graphics
@@ -35,6 +36,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));
}
}
@@ -63,6 +78,46 @@ namespace osu.Game.Graphics
}
}
+ public Color4 ForOverlayElement(OverlayColourScheme colourScheme, float saturation, float lightness, float opacity = 1) => Color4.FromHsl(new Vector4(getBaseHue(colourScheme), saturation, lightness, opacity));
+
+ // See https://github.com/ppy/osu-web/blob/4218c288292d7c810b619075471eaea8bbb8f9d8/app/helpers.php#L1463
+ private static float getBaseHue(OverlayColourScheme colourScheme)
+ {
+ float hue;
+
+ switch (colourScheme)
+ {
+ default:
+ throw new ArgumentException($@"{colourScheme} colour scheme does not provide a hue value in {nameof(getBaseHue)}.");
+
+ case OverlayColourScheme.Red:
+ hue = 0;
+ break;
+
+ case OverlayColourScheme.Pink:
+ hue = 333;
+ break;
+
+ case OverlayColourScheme.Orange:
+ hue = 46;
+ break;
+
+ case OverlayColourScheme.Green:
+ hue = 115;
+ break;
+
+ case OverlayColourScheme.Purple:
+ hue = 255;
+ break;
+
+ case OverlayColourScheme.Blue:
+ hue = 200;
+ break;
+ }
+
+ return hue / 360f;
+ }
+
// See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less
public readonly Color4 PurpleLighter = FromHex(@"eeeeff");
public readonly Color4 PurpleLight = FromHex(@"aa88ff");
@@ -165,4 +220,14 @@ namespace osu.Game.Graphics
public readonly Color4 ContextMenuGray = FromHex(@"223034");
}
+
+ public enum OverlayColourScheme
+ {
+ Red,
+ Pink,
+ Orange,
+ Green,
+ Purple,
+ Blue
+ }
}
diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs
index b9151b7393..7f20c30048 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;
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/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..2112aac6a3 100644
--- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
+++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
@@ -128,10 +128,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)
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
index 6a7998d5fb..1bfbee4a60 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;
@@ -84,14 +83,6 @@ namespace osu.Game.Graphics.UserInterface
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 +274,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/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/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..ff46c6d849 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -881,7 +881,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/BreadcrumbControlOverlayHeader.cs b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs
index 8a82b1f0c0..2e50c19729 100644
--- a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs
+++ b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs
@@ -1,8 +1,10 @@
// 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.UserInterface;
+using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays
@@ -13,6 +15,17 @@ namespace osu.Game.Overlays
protected override TabControl CreateTabControl() => BreadcrumbControl = new OverlayHeaderBreadcrumbControl();
+ protected BreadcrumbControlOverlayHeader(OverlayColourScheme colourScheme)
+ : base(colourScheme)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ BreadcrumbControl.AccentColour = colours.ForOverlayElement(ColourScheme, 1, 0.75f);
+ }
+
public class OverlayHeaderBreadcrumbControl : BreadcrumbControl
{
public OverlayHeaderBreadcrumbControl()
diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
index 7e47a3e29f..d5e0890b4d 100644
--- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs
+++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
@@ -26,6 +26,7 @@ namespace osu.Game.Overlays.Changelog
private const string listing_string = "listing";
public ChangelogHeader()
+ : base(OverlayColourScheme.Purple)
{
BreadcrumbControl.AddItem(listing_string);
BreadcrumbControl.Current.ValueChanged += e =>
@@ -43,14 +44,6 @@ namespace osu.Game.Overlays.Changelog
};
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- BreadcrumbControl.AccentColour = colours.Violet;
- TitleBackgroundColour = colours.GreyVioletDarker;
- ControlBackgroundColour = colours.GreyVioletDark;
- }
-
private ChangelogHeaderTitle title;
private void showBuild(ValueChangedEvent e)
@@ -117,12 +110,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/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..2c0fa49b5d 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,14 +311,12 @@ 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)
diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs
index 560123eb55..d252083411 100644
--- a/osu.Game/Overlays/Comments/CommentsContainer.cs
+++ b/osu.Game/Overlays/Comments/CommentsContainer.cs
@@ -18,8 +18,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();
@@ -38,12 +38,10 @@ namespace osu.Game.Overlays.Comments
private readonly FillFlowContainer content;
private readonly DeletedChildrenPlaceholder deletedChildrenPlaceholder;
private readonly CommentsShowMoreButton moreButton;
+ private readonly TotalCommentsCounter commentCounter;
- public CommentsContainer(CommentableType type, long id)
+ public CommentsContainer()
{
- this.type = type;
- this.id = id;
-
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
AddRangeInternal(new Drawable[]
@@ -59,6 +57,7 @@ namespace osu.Game.Overlays.Comments
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
+ commentCounter = new TotalCommentsCounter(),
new CommentsHeader
{
Sort = { BindTarget = Sort },
@@ -101,7 +100,8 @@ namespace osu.Game.Overlays.Comments
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding(5),
- Action = getComments
+ Action = getComments,
+ IsLoading = true,
}
}
}
@@ -121,11 +121,27 @@ namespace osu.Game.Overlays.Comments
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 +149,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);
}
@@ -152,7 +171,7 @@ namespace osu.Game.Overlays.Comments
{
loadCancellation = new CancellationTokenSource();
- FillFlowContainer page = new FillFlowContainer
+ var page = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
@@ -185,6 +204,8 @@ namespace osu.Game.Overlays.Comments
moreButton.IsLoading = false;
}
+ commentCounter.Current.Value = response.Total;
+
moreButton.FadeTo(response.HasMore ? 1 : 0);
}, loadCancellation.Token);
}
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/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..38f5d54714 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -473,7 +473,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..19f06e99f1 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -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..03dc64b3bd 100644
--- a/osu.Game/Overlays/News/NewsHeader.cs
+++ b/osu.Game/Overlays/News/NewsHeader.cs
@@ -23,6 +23,7 @@ namespace osu.Game.Overlays.News
public Action ShowFrontPage;
public NewsHeader()
+ : base(OverlayColourScheme.Purple)
{
BreadcrumbControl.AddItem(front_page_string);
@@ -35,14 +36,6 @@ 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)
@@ -97,12 +90,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/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/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs
index 53da2da634..c9547bb5b8 100644
--- a/osu.Game/Overlays/OverlayHeader.cs
+++ b/osu.Game/Overlays/OverlayHeader.cs
@@ -2,10 +2,12 @@
// 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;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
@@ -16,24 +18,19 @@ namespace osu.Game.Overlays
private readonly Box titleBackground;
private readonly Box controlBackground;
private readonly Container background;
-
- protected Color4 TitleBackgroundColour
- {
- set => titleBackground.Colour = value;
- }
-
- protected Color4 ControlBackgroundColour
- {
- set => controlBackground.Colour = value;
- }
+ private readonly ScreenTitle title;
protected float BackgroundHeight
{
set => background.Height = value;
}
- protected OverlayHeader()
+ protected OverlayColourScheme ColourScheme { get; }
+
+ protected OverlayHeader(OverlayColourScheme colourScheme)
{
+ ColourScheme = colourScheme;
+
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
@@ -60,9 +57,8 @@ namespace osu.Game.Overlays
titleBackground = new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = Color4.Gray,
},
- CreateTitle().With(title =>
+ title = CreateTitle().With(title =>
{
title.Margin = new MarginPadding
{
@@ -92,6 +88,14 @@ namespace osu.Game.Overlays
});
}
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ titleBackground.Colour = colours.ForOverlayElement(ColourScheme, 0.2f, 0.15f);
+ title.AccentColour = colours.ForOverlayElement(ColourScheme, 1, 0.7f);
+ controlBackground.Colour = colours.ForOverlayElement(ColourScheme, 0.2f, 0.2f);
+ }
+
protected abstract Drawable CreateBackground();
[NotNull]
diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs
index 8069937810..29471375b5 100644
--- a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs
+++ b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
public LevelBadge()
{
- TooltipText = "Level";
+ TooltipText = "level";
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs
index 6a6532764f..a73ce56a2b 100644
--- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs
+++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
public LevelProgressBar()
{
- TooltipText = "Progress to next level";
+ TooltipText = "progress to next level";
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index 59e64dfc26..b550d7d823 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -25,6 +24,7 @@ namespace osu.Game.Overlays.Profile
private DetailHeaderContainer detailHeaderContainer;
public ProfileHeader()
+ : base(OverlayColourScheme.Green)
{
BackgroundHeight = 150;
@@ -36,14 +36,6 @@ namespace osu.Game.Overlays.Profile
centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true);
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- TabControl.AccentColour = colours.Seafoam;
- TitleBackgroundColour = colours.GreySeafoamDarker;
- ControlBackgroundColour = colours.GreySeafoam;
- }
-
protected override Drawable CreateBackground() =>
new Container
{
@@ -109,12 +101,6 @@ namespace osu.Game.Overlays.Profile
Section = "info";
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- AccentColour = colours.Seafoam;
- }
-
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/profile");
}
}
diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs
new file mode 100644
index 0000000000..2b12457ccc
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/CountryFilter.cs
@@ -0,0 +1,105 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class CountryFilter : CompositeDrawable, IHasCurrentValue
+ {
+ private const int duration = 200;
+ private const int height = 50;
+
+ private readonly BindableWithCurrent current = new BindableWithCurrent();
+
+ public Bindable Current
+ {
+ get => current.Current;
+ set => current.Current = value;
+ }
+
+ private readonly Box background;
+ private readonly CountryPill countryPill;
+ private readonly Container content;
+
+ public CountryFilter()
+ {
+ RelativeSizeAxes = Axes.X;
+
+ InternalChild = content = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = height,
+ Alpha = 0,
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ new FillFlowContainer
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(10, 0),
+ Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN },
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Text = @"filtered by country:",
+ Font = OsuFont.GetFont(size: 14)
+ },
+ countryPill = new CountryPill
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Alpha = 0,
+ Current = Current
+ }
+ }
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ background.Colour = colours.GreySeafoam;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ Current.BindValueChanged(onCountryChanged, true);
+ }
+
+ private void onCountryChanged(ValueChangedEvent country)
+ {
+ if (country.NewValue == null)
+ {
+ countryPill.Collapse();
+ this.ResizeHeightTo(0, duration, Easing.OutQuint);
+ content.FadeOut(duration, Easing.OutQuint);
+ return;
+ }
+
+ this.ResizeHeightTo(height, duration, Easing.OutQuint);
+ content.FadeIn(duration, Easing.OutQuint);
+ countryPill.Expand();
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Rankings/CountryPill.cs b/osu.Game/Overlays/Rankings/CountryPill.cs
new file mode 100644
index 0000000000..410d316006
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/CountryPill.cs
@@ -0,0 +1,164 @@
+// 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.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Users;
+using osu.Game.Users.Drawables;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class CountryPill : CompositeDrawable, IHasCurrentValue
+ {
+ private const int duration = 200;
+
+ private readonly BindableWithCurrent current = new BindableWithCurrent();
+
+ public Bindable Current
+ {
+ get => current.Current;
+ set => current.Current = value;
+ }
+
+ private readonly Container content;
+ private readonly Box background;
+ private readonly UpdateableFlag flag;
+ private readonly OsuSpriteText countryName;
+
+ public CountryPill()
+ {
+ AutoSizeAxes = Axes.Both;
+
+ InternalChild = content = new CircularContainer
+ {
+ Height = 25,
+ AutoSizeDuration = duration,
+ AutoSizeEasing = Easing.OutQuint,
+ Masking = true,
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Y,
+ AutoSizeAxes = Axes.X,
+ Margin = new MarginPadding { Horizontal = 10 },
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(8, 0),
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.Y,
+ AutoSizeAxes = Axes.X,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(3, 0),
+ Children = new Drawable[]
+ {
+ flag = new UpdateableFlag
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(22, 15)
+ },
+ countryName = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Font = OsuFont.GetFont(size: 14)
+ }
+ }
+ },
+ new CloseButton
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Action = () => Current.Value = null
+ }
+ }
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ background.Colour = colours.GreySeafoamDarker;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ Current.BindValueChanged(onCountryChanged, true);
+ }
+
+ public void Expand()
+ {
+ content.ClearTransforms();
+ content.AutoSizeAxes = Axes.X;
+
+ this.FadeIn(duration, Easing.OutQuint);
+ }
+
+ public void Collapse()
+ {
+ content.ClearTransforms();
+ content.AutoSizeAxes = Axes.None;
+ content.ResizeWidthTo(0, duration, Easing.OutQuint);
+
+ this.FadeOut(duration, Easing.OutQuint);
+ }
+
+ private void onCountryChanged(ValueChangedEvent country)
+ {
+ if (country.NewValue == null)
+ return;
+
+ flag.Country = country.NewValue;
+ countryName.Text = country.NewValue.FullName;
+ }
+
+ private class CloseButton : OsuHoverContainer
+ {
+ private readonly SpriteIcon icon;
+
+ protected override IEnumerable EffectTargets => new[] { icon };
+
+ public CloseButton()
+ {
+ AutoSizeAxes = Axes.Both;
+ Add(icon = new SpriteIcon
+ {
+ Size = new Vector2(8),
+ Icon = FontAwesome.Solid.Times
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ IdleColour = colours.GreySeafoamLighter;
+ HoverColour = Color4.White;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
index 3f8bc2b0c7..08bc67e43e 100644
--- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
@@ -40,6 +40,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
Bindable = config.GetBindable(OsuSetting.ShowInterface)
},
new SettingsCheckbox
+ {
+ LabelText = "Show difficulty graph on progress bar",
+ Bindable = config.GetBindable(OsuSetting.ShowProgressGraph)
+ },
+ new SettingsCheckbox
{
LabelText = "Show health display even when you can't fail",
Bindable = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail),
diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
index a5f56ae76e..0c42247993 100644
--- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
@@ -1,7 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
@@ -10,11 +12,20 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{
public class SongSelectSettings : SettingsSubsection
{
+ private Bindable minStars;
+ private Bindable maxStars;
+
protected override string Header => "Song Select";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
+ minStars = config.GetBindable(OsuSetting.DisplayStarsMinimum);
+ maxStars = config.GetBindable(OsuSetting.DisplayStarsMaximum);
+
+ minStars.ValueChanged += min => maxStars.Value = Math.Max(min.NewValue, maxStars.Value);
+ maxStars.ValueChanged += max => minStars.Value = Math.Min(max.NewValue, minStars.Value);
+
Children = new Drawable[]
{
new SettingsCheckbox
@@ -27,19 +38,19 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
LabelText = "Show converted beatmaps",
Bindable = config.GetBindable(OsuSetting.ShowConvertedBeatmaps),
},
- new SettingsSlider
+ new SettingsSlider
{
LabelText = "Display beatmaps from",
Bindable = config.GetBindable(OsuSetting.DisplayStarsMinimum),
KeyboardStep = 0.1f,
- Keywords = new[] { "star", "difficulty" }
+ Keywords = new[] { "minimum", "maximum", "star", "difficulty" }
},
- new SettingsSlider
+ new SettingsSlider
{
LabelText = "up to",
Bindable = config.GetBindable(OsuSetting.DisplayStarsMaximum),
KeyboardStep = 0.1f,
- Keywords = new[] { "star", "difficulty" }
+ Keywords = new[] { "minimum", "maximum", "star", "difficulty" }
},
new SettingsEnumDropdown
{
@@ -49,7 +60,12 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
};
}
- private class StarSlider : OsuSliderBar
+ private class MaximumStarsSlider : StarsSlider
+ {
+ public override string TooltipText => Current.IsDefault ? "no limit" : base.TooltipText;
+ }
+
+ private class StarsSlider : OsuSliderBar
{
public override string TooltipText => Current.Value.ToString(@"0.## stars");
}
diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
index 55c7210d6c..db6f24a954 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
new SettingsButton
{
Text = "Key configuration",
- TooltipText = "Change global shortcut keys and gameplay bindings",
+ TooltipText = "change global shortcut keys and gameplay bindings",
Action = keyConfig.ToggleVisibility
},
};
diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
index 4f2f3dfd1a..59d39a1c3c 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
@@ -87,7 +87,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
private class SensitivitySlider : OsuSliderBar
{
- public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : $"{base.TooltipText}x";
+ public override string TooltipText => Current.Disabled ? "enable raw input to adjust sensitivity" : $"{base.TooltipText}x";
}
}
}
diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs
index 55e1937d83..e89f2adf0b 100644
--- a/osu.Game/Overlays/Settings/SettingsItem.cs
+++ b/osu.Game/Overlays/Settings/SettingsItem.cs
@@ -161,7 +161,7 @@ namespace osu.Game.Overlays.Settings
UpdateState();
}
- public string TooltipText => "Revert to default";
+ public string TooltipText => "revert to default";
protected override bool OnClick(ClickEvent e)
{
diff --git a/osu.Game/Overlays/TabControlOverlayHeader.cs b/osu.Game/Overlays/TabControlOverlayHeader.cs
index f3521b66c8..8f3aa896ee 100644
--- a/osu.Game/Overlays/TabControlOverlayHeader.cs
+++ b/osu.Game/Overlays/TabControlOverlayHeader.cs
@@ -1,6 +1,7 @@
// 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.UserInterface;
@@ -15,6 +16,17 @@ namespace osu.Game.Overlays
protected override TabControl CreateTabControl() => TabControl = new OverlayHeaderTabControl();
+ protected TabControlOverlayHeader(OverlayColourScheme colourScheme)
+ : base(colourScheme)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ TabControl.AccentColour = colours.ForOverlayElement(ColourScheme, 1, 0.75f);
+ }
+
public class OverlayHeaderTabControl : OverlayTabControl
{
public OverlayHeaderTabControl()
diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs
index 9cd3aac2cb..76fad945cc 100644
--- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs
+++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs
@@ -16,6 +16,9 @@ namespace osu.Game.Overlays.Volume
public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false;
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
- public bool OnReleased(GlobalAction action) => false;
+
+ public void OnReleased(GlobalAction action)
+ {
+ }
}
}
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index bfaa7e8872..9ee3bacf9b 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -46,12 +46,12 @@ namespace osu.Game.Rulesets.Edit
private IAdjustableClock adjustableClock { get; set; }
[Resolved]
- private BindableBeatDivisor beatDivisor { get; set; }
+ private IBeatSnapProvider beatSnapProvider { get; set; }
private IBeatmapProcessor beatmapProcessor;
private DrawableEditRulesetWrapper drawableRulesetWrapper;
- private BlueprintContainer blueprintContainer;
+ private ComposeBlueprintContainer blueprintContainer;
private Container distanceSnapGridContainer;
private DistanceSnapGrid distanceSnapGrid;
private readonly List layerContainers = new List();
@@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Edit
new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both }
});
- var layerAboveRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChild(blueprintContainer = new BlueprintContainer());
+ var layerAboveRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChild(blueprintContainer = CreateBlueprintContainer());
layerContainers.Add(layerBelowRuleset);
layerContainers.Add(layerAboveRuleset);
@@ -233,6 +233,8 @@ namespace osu.Game.Rulesets.Edit
protected abstract IReadOnlyList CompositionTools { get; }
+ protected abstract ComposeBlueprintContainer CreateBlueprintContainer();
+
protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null);
public void BeginPlacement(HitObject hitObject)
@@ -257,40 +259,26 @@ namespace osu.Game.Rulesets.Edit
public override float GetBeatSnapDistanceAt(double referenceTime)
{
DifficultyControlPoint difficultyPoint = EditorBeatmap.ControlPointInfo.DifficultyPointAt(referenceTime);
- return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / beatDivisor.Value);
+ return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / beatSnapProvider.BeatDivisor);
}
public override float DurationToDistance(double referenceTime, double duration)
{
- double beatLength = EditorBeatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value;
+ double beatLength = beatSnapProvider.GetBeatLengthAtTime(referenceTime);
return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceTime));
}
public override double DistanceToDuration(double referenceTime, float distance)
{
- double beatLength = EditorBeatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value;
+ double beatLength = beatSnapProvider.GetBeatLengthAtTime(referenceTime);
return distance / GetBeatSnapDistanceAt(referenceTime) * beatLength;
}
public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
- => beatSnap(referenceTime, DistanceToDuration(referenceTime, distance));
+ => beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance));
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
- => DurationToDistance(referenceTime, beatSnap(referenceTime, DistanceToDuration(referenceTime, distance)));
-
- ///
- /// Snaps a duration to the closest beat of a timing point applicable at the reference time.
- ///
- /// The time of the timing point which resides in.
- /// The duration to snap.
- /// A value that represents snapped to the closest beat of the timing point.
- private double beatSnap(double referenceTime, double duration)
- {
- double beatLength = EditorBeatmap.ControlPointInfo.TimingPointAt(referenceTime).BeatLength / beatDivisor.Value;
-
- // A 1ms offset prevents rounding errors due to minute variations in duration
- return (int)((duration + 1) / beatLength) * beatLength;
- }
+ => DurationToDistance(referenceTime, beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance)));
protected override void Dispose(bool isDisposing)
{
@@ -323,17 +311,6 @@ namespace osu.Game.Rulesets.Edit
///
public abstract bool CursorInPlacementArea { get; }
- ///
- /// Creates a for a specific .
- ///
- /// The to create the overlay for.
- public virtual SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null;
-
- ///
- /// Creates a which outlines s and handles movement of selections.
- ///
- public virtual SelectionHandler CreateSelectionHandler() => new SelectionHandler();
-
///
/// Creates the applicable for a selection.
///
diff --git a/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs b/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs
new file mode 100644
index 0000000000..e1daafaebe
--- /dev/null
+++ b/osu.Game/Rulesets/Edit/IBeatSnapProvider.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.
+
+namespace osu.Game.Rulesets.Edit
+{
+ public interface IBeatSnapProvider
+ {
+ ///
+ /// Snaps a duration to the closest beat of a timing point applicable at the reference time.
+ ///
+ /// The time of the timing point which resides in.
+ /// The duration to snap.
+ /// A value that represents snapped to the closest beat of the timing point.
+ double SnapTime(double referenceTime, double duration);
+
+ ///
+ /// Get the most appropriate beat length at a given time.
+ ///
+ /// A reference time used for lookup.
+ /// The most appropriate beat length.
+ double GetBeatLengthAtTime(double referenceTime);
+
+ ///
+ /// Returns the current beat divisor.
+ ///
+ int BeatDivisor { get; }
+ }
+}
diff --git a/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs b/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs
new file mode 100644
index 0000000000..b4ae3f3fba
--- /dev/null
+++ b/osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.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 osu.Framework.Graphics.Primitives;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Rulesets.Objects.Drawables;
+using osuTK;
+
+namespace osu.Game.Rulesets.Edit
+{
+ public abstract class OverlaySelectionBlueprint : SelectionBlueprint
+ {
+ ///
+ /// The which this applies to.
+ ///
+ public readonly DrawableHitObject DrawableObject;
+
+ protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || State == SelectionState.Selected;
+
+ protected OverlaySelectionBlueprint(DrawableHitObject drawableObject)
+ : base(drawableObject.HitObject)
+ {
+ DrawableObject = drawableObject;
+ }
+
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.ReceivePositionalInputAt(screenSpacePos);
+
+ public override Vector2 SelectionPoint => DrawableObject.ScreenSpaceDrawQuad.Centre;
+
+ public override Quad SelectionQuad => DrawableObject.ScreenSpaceDrawQuad;
+
+ public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => DrawableObject.Parent.ToLocalSpace(screenSpacePosition) - DrawableObject.Position;
+ }
+}
diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs
index bf99f83e0b..a972d28480 100644
--- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs
+++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs
@@ -1,4 +1,4 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
@@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Edit
///
public abstract class SelectionBlueprint : CompositeDrawable, IStateful
{
+ public readonly HitObject HitObject;
+
///
/// Invoked when this has been selected.
///
@@ -30,26 +32,24 @@ namespace osu.Game.Rulesets.Edit
///
public event Action Deselected;
- ///
- /// The which this applies to.
- ///
- public readonly DrawableHitObject DrawableObject;
-
- protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || State == SelectionState.Selected;
public override bool HandlePositionalInput => ShouldBeAlive;
public override bool RemoveWhenNotAlive => false;
[Resolved(CanBeNull = true)]
private HitObjectComposer composer { get; set; }
- protected SelectionBlueprint(DrawableHitObject drawableObject)
+ protected SelectionBlueprint(HitObject hitObject)
{
- DrawableObject = drawableObject;
+ HitObject = hitObject;
RelativeSizeAxes = Axes.Both;
-
AlwaysPresent = true;
- Alpha = 0;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ updateState();
}
private SelectionState state;
@@ -66,58 +66,68 @@ namespace osu.Game.Rulesets.Edit
state = value;
- switch (state)
- {
- case SelectionState.Selected:
- Show();
- Selected?.Invoke(this);
- break;
-
- case SelectionState.NotSelected:
- Hide();
- Deselected?.Invoke(this);
- break;
- }
+ if (IsLoaded)
+ updateState();
StateChanged?.Invoke(state);
}
}
+ private void updateState()
+ {
+ switch (state)
+ {
+ case SelectionState.Selected:
+ OnSelected();
+ Selected?.Invoke(this);
+ break;
+
+ case SelectionState.NotSelected:
+ OnDeselected();
+ Deselected?.Invoke(this);
+ break;
+ }
+ }
+
+ protected virtual void OnDeselected() => Hide();
+
+ protected virtual void OnSelected() => Show();
+
// When not selected, input is only required for the blueprint itself to receive IsHovering
protected override bool ShouldBeConsideredForInput(Drawable child) => State == SelectionState.Selected;
///
- /// Selects this , causing it to become visible.
+ /// Selects this , causing it to become visible.
///
public void Select() => State = SelectionState.Selected;
///
- /// Deselects this , causing it to become invisible.
+ /// Deselects this , causing it to become invisible.
///
public void Deselect() => State = SelectionState.NotSelected;
public bool IsSelected => State == SelectionState.Selected;
///
- /// Updates the , invoking and re-processing the beatmap.
+ /// Updates the , invoking and re-processing the beatmap.
///
- protected void UpdateHitObject() => composer?.UpdateHitObject(DrawableObject.HitObject);
-
- public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.ReceivePositionalInputAt(screenSpacePos);
+ protected void UpdateHitObject() => composer?.UpdateHitObject(HitObject);
///
- /// The s to be displayed in the context menu for this .
+ /// The s to be displayed in the context menu for this .
///
public virtual MenuItem[] ContextMenuItems => Array.Empty
[JsonIgnore]
- public virtual IconUsage Icon => FontAwesome.Solid.Question;
+ public virtual IconUsage? Icon => null;
///
/// The type of this mod.
@@ -60,6 +60,12 @@ namespace osu.Game.Rulesets.Mods
[JsonIgnore]
public virtual bool Ranked => false;
+ ///
+ /// Whether this mod requires configuration to apply changes to the game.
+ ///
+ [JsonIgnore]
+ public virtual bool RequiresConfiguration => false;
+
///
/// The mods this mod cannot be enabled with.
///
diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs
index 070a10b1c8..e51b8b6457 100644
--- a/osu.Game/Rulesets/Mods/ModAutoplay.cs
+++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Autoplay";
public override string Acronym => "AT";
- public override IconUsage Icon => OsuIcon.ModAuto;
+ public override IconUsage? Icon => OsuIcon.ModAuto;
public override ModType Type => ModType.Automation;
public override string Description => "Watch a perfect automated play through the song.";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs
index 3487d49e08..cd08aee453 100644
--- a/osu.Game/Rulesets/Mods/ModCinema.cs
+++ b/osu.Game/Rulesets/Mods/ModCinema.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Cinema";
public override string Acronym => "CN";
- public override IconUsage Icon => OsuIcon.ModCinema;
+ public override IconUsage? Icon => OsuIcon.ModCinema;
public override string Description => "Watch the video without visual distractions.";
public void ApplyToHUD(HUDOverlay overlay)
diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs
index 71a666414f..bd98e735e5 100644
--- a/osu.Game/Rulesets/Mods/ModDaycore.cs
+++ b/osu.Game/Rulesets/Mods/ModDaycore.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Daycore";
public override string Acronym => "DC";
- public override IconUsage Icon => FontAwesome.Solid.Question;
+ public override IconUsage? Icon => null;
public override string Description => "Whoaaaaa...";
private readonly BindableNumber tempoAdjust = new BindableDouble(1);
diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs
index c5b8a1bc73..d74e2ce2bc 100644
--- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs
+++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs
@@ -20,10 +20,12 @@ namespace osu.Game.Rulesets.Mods
public override ModType Type => ModType.Conversion;
- public override IconUsage Icon => FontAwesome.Solid.Hammer;
+ public override IconUsage? Icon => FontAwesome.Solid.Hammer;
public override double ScoreMultiplier => 1.0;
+ public override bool RequiresConfiguration => true;
+
public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModHardRock) };
[SettingSource("Drain Rate", "Override a beatmap's set HP.")]
diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs
index 7015460c51..152657da33 100644
--- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs
+++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Double Time";
public override string Acronym => "DT";
- public override IconUsage Icon => OsuIcon.ModDoubletime;
+ public override IconUsage? Icon => OsuIcon.ModDoubletime;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Zoooooooooom...";
public override bool Ranked => true;
diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs
index ec0f50c0be..b56be95dfe 100644
--- a/osu.Game/Rulesets/Mods/ModEasy.cs
+++ b/osu.Game/Rulesets/Mods/ModEasy.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Easy";
public override string Acronym => "EZ";
- public override IconUsage Icon => OsuIcon.ModEasy;
+ public override IconUsage? Icon => OsuIcon.ModEasy;
public override ModType Type => ModType.DifficultyReduction;
public override double ScoreMultiplier => 0.5;
public override bool Ranked => true;
diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs
index 4f939362bb..35a8334237 100644
--- a/osu.Game/Rulesets/Mods/ModFlashlight.cs
+++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Flashlight";
public override string Acronym => "FL";
- public override IconUsage Icon => OsuIcon.ModFlashlight;
+ public override IconUsage? Icon => OsuIcon.ModFlashlight;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Restricted view area.";
public override bool Ranked => true;
diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs
index 15f7afa312..203b88951c 100644
--- a/osu.Game/Rulesets/Mods/ModHalfTime.cs
+++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Half Time";
public override string Acronym => "HT";
- public override IconUsage Icon => OsuIcon.ModHalftime;
+ public override IconUsage? Icon => OsuIcon.ModHalftime;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "Less zoom...";
public override bool Ranked => true;
diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs
index a613d41cf4..58c9a58408 100644
--- a/osu.Game/Rulesets/Mods/ModHardRock.cs
+++ b/osu.Game/Rulesets/Mods/ModHardRock.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Hard Rock";
public override string Acronym => "HR";
- public override IconUsage Icon => OsuIcon.ModHardrock;
+ public override IconUsage? Icon => OsuIcon.ModHardrock;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Everything just got a bit harder...";
public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust) };
diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs
index 0934992f55..4e4a75db82 100644
--- a/osu.Game/Rulesets/Mods/ModHidden.cs
+++ b/osu.Game/Rulesets/Mods/ModHidden.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Hidden";
public override string Acronym => "HD";
- public override IconUsage Icon => OsuIcon.ModHidden;
+ public override IconUsage? Icon => OsuIcon.ModHidden;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => true;
diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs
index a8c79bb896..1df2aeb348 100644
--- a/osu.Game/Rulesets/Mods/ModNightcore.cs
+++ b/osu.Game/Rulesets/Mods/ModNightcore.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Nightcore";
public override string Acronym => "NC";
- public override IconUsage Icon => OsuIcon.ModNightcore;
+ public override IconUsage? Icon => OsuIcon.ModNightcore;
public override string Description => "Uguuuuuuuu...";
private readonly BindableNumber tempoAdjust = new BindableDouble(1);
diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs
index 49ee3354c3..b95ec7490e 100644
--- a/osu.Game/Rulesets/Mods/ModNoFail.cs
+++ b/osu.Game/Rulesets/Mods/ModNoFail.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "No Fail";
public override string Acronym => "NF";
- public override IconUsage Icon => OsuIcon.ModNofail;
+ public override IconUsage? Icon => OsuIcon.ModNofail;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "You can't fail, no matter what.";
public override double ScoreMultiplier => 0.5;
diff --git a/osu.Game/Rulesets/Mods/ModNoMod.cs b/osu.Game/Rulesets/Mods/ModNoMod.cs
index 487985b2b3..379a2122f2 100644
--- a/osu.Game/Rulesets/Mods/ModNoMod.cs
+++ b/osu.Game/Rulesets/Mods/ModNoMod.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "No Mod";
public override string Acronym => "NM";
public override double ScoreMultiplier => 1;
- public override IconUsage Icon => FontAwesome.Solid.Ban;
+ public override IconUsage? Icon => FontAwesome.Solid.Ban;
public override ModType Type => ModType.System;
}
}
diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs
index afa263f1c9..882d3ebd6a 100644
--- a/osu.Game/Rulesets/Mods/ModPerfect.cs
+++ b/osu.Game/Rulesets/Mods/ModPerfect.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Perfect";
public override string Acronym => "PF";
- public override IconUsage Icon => OsuIcon.ModPerfect;
+ public override IconUsage? Icon => OsuIcon.ModPerfect;
public override string Description => "SS or quit.";
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => result.Type != result.Judgement.MaxResult;
diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs
index 7c355577d4..b6fec42f43 100644
--- a/osu.Game/Rulesets/Mods/ModRelax.cs
+++ b/osu.Game/Rulesets/Mods/ModRelax.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Relax";
public override string Acronym => "RX";
- public override IconUsage Icon => OsuIcon.ModRelax;
+ public override IconUsage? Icon => OsuIcon.ModRelax;
public override ModType Type => ModType.Automation;
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) };
diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
index a4d0631d8c..8799431f1d 100644
--- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
+++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Sudden Death";
public override string Acronym => "SD";
- public override IconUsage Icon => OsuIcon.ModSuddendeath;
+ public override IconUsage? Icon => OsuIcon.ModSuddendeath;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Miss and fail.";
public override double ScoreMultiplier => 1;
diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs
index 5416f1ac22..da3bd75b44 100644
--- a/osu.Game/Rulesets/Mods/ModWindDown.cs
+++ b/osu.Game/Rulesets/Mods/ModWindDown.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Wind Down";
public override string Acronym => "WD";
public override string Description => "Sloooow doooown...";
- public override IconUsage Icon => FontAwesome.Solid.ChevronCircleDown;
+ public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleDown;
public override double ScoreMultiplier => 1.0;
[SettingSource("Final rate", "The speed increase to ramp towards")]
diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs
index 3cf584f3dd..3f456a42a5 100644
--- a/osu.Game/Rulesets/Mods/ModWindUp.cs
+++ b/osu.Game/Rulesets/Mods/ModWindUp.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Wind Up";
public override string Acronym => "WU";
public override string Description => "Can you keep up?";
- public override IconUsage Icon => FontAwesome.Solid.ChevronCircleUp;
+ public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleUp;
public override double ScoreMultiplier => 1.0;
[SettingSource("Final rate", "The speed increase to ramp towards")]
diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs
index 945dbe4cc9..3edab0745d 100644
--- a/osu.Game/Rulesets/UI/ModIcon.cs
+++ b/osu.Game/Rulesets/UI/ModIcon.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osuTK;
using osu.Framework.Bindables;
@@ -20,25 +21,30 @@ namespace osu.Game.Rulesets.UI
public readonly BindableBool Selected = new BindableBool();
private readonly SpriteIcon modIcon;
+ private readonly SpriteText modAcronym;
private readonly SpriteIcon background;
private const float size = 80;
- public IconUsage Icon
- {
- get => modIcon.Icon;
- set => modIcon.Icon = value;
- }
-
private readonly ModType type;
public virtual string TooltipText { get; }
- protected Mod Mod { get; private set; }
+ private Mod mod;
+
+ public Mod Mod
+ {
+ get => mod;
+ set
+ {
+ mod = value;
+ updateMod(value);
+ }
+ }
public ModIcon(Mod mod)
{
- Mod = mod ?? throw new ArgumentNullException(nameof(mod));
+ this.mod = mod ?? throw new ArgumentNullException(nameof(mod));
type = mod.Type;
@@ -56,15 +62,43 @@ namespace osu.Game.Rulesets.UI
Icon = OsuIcon.ModBg,
Shadow = true,
},
+ modAcronym = new OsuSpriteText
+ {
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Colour = OsuColour.Gray(84),
+ Alpha = 0,
+ Font = OsuFont.Numeric.With(null, 22f),
+ UseFullGlyphHeight = false,
+ Text = mod.Acronym
+ },
modIcon = new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Colour = OsuColour.Gray(84),
- Size = new Vector2(size - 35),
- Icon = mod.Icon
+ Size = new Vector2(45),
+ Icon = FontAwesome.Solid.Question
},
};
+
+ updateMod(mod);
+ }
+
+ private void updateMod(Mod value)
+ {
+ modAcronym.Text = value.Acronym;
+ modIcon.Icon = value.Icon ?? FontAwesome.Solid.Question;
+
+ if (value.Icon is null)
+ {
+ modIcon.FadeOut();
+ modAcronym.FadeIn();
+ return;
+ }
+
+ modIcon.FadeIn();
+ modAcronym.FadeOut();
}
private Color4 backgroundColour;
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index 5cc213be41..41b2739fc5 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -139,7 +139,11 @@ namespace osu.Game.Rulesets.UI
public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action, Clock.Rate >= 0));
- public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action, Clock.Rate >= 0));
+ public void OnReleased(T action)
+ {
+ foreach (var c in Target.Children.OfType>())
+ c.OnReleased(action, Clock.Rate >= 0);
+ }
}
#endregion
diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
index fda1d7c723..8bcdfff2fd 100644
--- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
@@ -201,7 +201,9 @@ namespace osu.Game.Rulesets.UI.Scrolling
throw new ArgumentException($"{nameof(Playfield)} must be a {nameof(ScrollingPlayfield)} when using {nameof(DrawableScrollingRuleset)}.");
}
- public bool OnReleased(GlobalAction action) => false;
+ public void OnReleased(GlobalAction action)
+ {
+ }
private class LocalScrollingInfo : IScrollingInfo
{
diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs
index 62d6c4648b..66df68418a 100644
--- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs
+++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs
@@ -5,6 +5,8 @@ using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -25,13 +27,13 @@ namespace osu.Game.Screens.Edit.Components
private IAdjustableClock adjustableClock;
+ private readonly BindableNumber tempo = new BindableDouble(1);
+
[BackgroundDependencyLoader]
private void load(IAdjustableClock adjustableClock)
{
this.adjustableClock = adjustableClock;
- PlaybackTabControl tabs;
-
Children = new Drawable[]
{
playButton = new IconButton
@@ -58,11 +60,18 @@ namespace osu.Game.Screens.Edit.Components
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Padding = new MarginPadding { Left = 45 },
- Child = tabs = new PlaybackTabControl(),
+ Child = new PlaybackTabControl { Current = tempo },
}
};
- tabs.Current.ValueChanged += tempo => Beatmap.Value.Track.Tempo.Value = tempo.NewValue;
+ Track?.AddAdjustment(AdjustableProperty.Tempo, tempo);
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ Track?.RemoveAdjustment(AdjustableProperty.Tempo, tempo);
+
+ base.Dispose(isDisposing);
}
protected override bool OnKeyDown(KeyDownEvent e)
diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
index 79ada40a89..5d638d7919 100644
--- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
+++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
@@ -32,12 +32,10 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
}
protected override bool OnDragStart(DragStartEvent e) => true;
- protected override bool OnDragEnd(DragEndEvent e) => true;
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
seekToPosition(e.ScreenSpaceMousePosition);
- return true;
}
protected override bool OnMouseDown(MouseDownEvent e)
diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs
index 7706e33179..4a7c3f26bc 100644
--- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs
+++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs
@@ -11,20 +11,24 @@ using osu.Game.Beatmaps;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
+ public class TimelinePart : TimelinePart
+ {
+ }
+
///
/// Represents a part of the summary timeline..
///
- public abstract class TimelinePart : Container
+ public class TimelinePart : Container where T : Drawable
{
protected readonly IBindable Beatmap = new Bindable();
- private readonly Container timeline;
+ private readonly Container content;
- protected override Container Content => timeline;
+ protected override Container Content => content;
- protected TimelinePart()
+ public TimelinePart(Container content = null)
{
- AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both });
+ AddInternal(this.content = content ?? new Container { RelativeSizeAxes = Axes.Both });
Beatmap.ValueChanged += b =>
{
@@ -44,17 +48,17 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
// the track may not be loaded completely (only has a length once it is).
if (!Beatmap.Value.Track.IsLoaded)
{
- timeline.RelativeChildSize = Vector2.One;
+ content.RelativeChildSize = Vector2.One;
Schedule(updateRelativeChildSize);
return;
}
- timeline.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
+ content.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
}
protected virtual void LoadBeatmap(WorkingBeatmap beatmap)
{
- timeline.Clear();
+ content.Clear();
}
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs
index 42773ef687..8201ec2710 100644
--- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs
@@ -262,10 +262,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
marker.Active = false;
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override bool OnClick(ClickEvent e)
@@ -274,10 +274,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
return true;
}
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
handleMouseInput(e.ScreenSpaceMousePosition);
- return true;
}
private void handleMouseInput(Vector2 screenSpaceMousePosition)
diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
index cafaddc39e..6b21f56567 100644
--- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
@@ -14,7 +15,6 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Timing;
using osu.Game.Rulesets.Edit;
-using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osuTK;
@@ -22,27 +22,32 @@ using osuTK.Input;
namespace osu.Game.Screens.Edit.Compose.Components
{
- public class BlueprintContainer : CompositeDrawable, IKeyBindingHandler
+ ///
+ /// A container which provides a "blueprint" display of hitobjects.
+ /// Includes selection and manipulation support via a .
+ ///
+ public abstract class BlueprintContainer : CompositeDrawable, IKeyBindingHandler
{
public event Action> SelectionChanged;
- private DragBox dragBox;
- private SelectionBlueprintContainer selectionBlueprints;
- private Container placementBlueprintContainer;
- private PlacementBlueprint currentPlacement;
+ protected DragBox DragBox { get; private set; }
+
+ private Container selectionBlueprints;
+
private SelectionHandler selectionHandler;
- private InputManager inputManager;
[Resolved]
private IAdjustableClock adjustableClock { get; set; }
- [Resolved]
- private HitObjectComposer composer { get; set; }
-
[Resolved]
private EditorBeatmap beatmap { get; set; }
- public BlueprintContainer()
+ private readonly BindableList selectedHitObjects = new BindableList();
+
+ [Resolved(canBeNull: true)]
+ private IDistanceSnapProvider snapProvider { get; set; }
+
+ protected BlueprintContainer()
{
RelativeSizeAxes = Axes.Both;
}
@@ -50,54 +55,63 @@ namespace osu.Game.Screens.Edit.Compose.Components
[BackgroundDependencyLoader]
private void load()
{
- selectionHandler = composer.CreateSelectionHandler();
+ selectionHandler = CreateSelectionHandler();
selectionHandler.DeselectAll = deselectAll;
- InternalChildren = new[]
+ AddRangeInternal(new[]
{
- dragBox = new DragBox(select),
+ DragBox = CreateDragBox(select),
selectionHandler,
- selectionBlueprints = new SelectionBlueprintContainer { RelativeSizeAxes = Axes.Both },
- placementBlueprintContainer = new Container { RelativeSizeAxes = Axes.Both },
- dragBox.CreateProxy()
+ selectionBlueprints = CreateSelectionBlueprintContainer(),
+ DragBox.CreateProxy().With(p => p.Depth = float.MinValue)
+ });
+
+ foreach (var obj in beatmap.HitObjects)
+ AddBlueprintFor(obj);
+
+ selectedHitObjects.BindTo(beatmap.SelectedHitObjects);
+ selectedHitObjects.ItemsAdded += objects =>
+ {
+ foreach (var o in objects)
+ selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Select();
};
- foreach (var obj in composer.HitObjects)
- addBlueprintFor(obj);
+ selectedHitObjects.ItemsRemoved += objects =>
+ {
+ foreach (var o in objects)
+ selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect();
+ };
}
protected override void LoadComplete()
{
base.LoadComplete();
- beatmap.HitObjectAdded += addBlueprintFor;
+ beatmap.HitObjectAdded += AddBlueprintFor;
beatmap.HitObjectRemoved += removeBlueprintFor;
-
- inputManager = GetContainingInputManager();
}
- private HitObjectCompositionTool currentTool;
+ protected virtual Container CreateSelectionBlueprintContainer() =>
+ new Container { RelativeSizeAxes = Axes.Both };
///
- /// The current placement tool.
+ /// Creates a which outlines s and handles movement of selections.
///
- public HitObjectCompositionTool CurrentTool
- {
- get => currentTool;
- set
- {
- if (currentTool == value)
- return;
+ protected virtual SelectionHandler CreateSelectionHandler() => new SelectionHandler();
- currentTool = value;
+ ///
+ /// Creates a for a specific .
+ ///
+ /// The to create the overlay for.
+ protected virtual SelectionBlueprint CreateBlueprintFor(HitObject hitObject) => null;
- refreshTool();
- }
- }
+ protected virtual DragBox CreateDragBox(Action performSelect) => new DragBox(performSelect);
protected override bool OnMouseDown(MouseDownEvent e)
{
beginClickSelection(e);
+ prepareSelectionMovement();
+
return e.Button == MouseButton.Left;
}
@@ -125,26 +139,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (clickedBlueprint == null)
return false;
- adjustableClock?.Seek(clickedBlueprint.DrawableObject.HitObject.StartTime);
+ adjustableClock?.Seek(clickedBlueprint.HitObject.StartTime);
return true;
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
// Special case for when a drag happened instead of a click
Schedule(() => endClickSelection());
- return e.Button == MouseButton.Left;
- }
- protected override bool OnMouseMove(MouseMoveEvent e)
- {
- if (currentPlacement != null)
- {
- updatePlacementPosition(e.ScreenSpaceMousePosition);
- return true;
- }
-
- return base.OnMouseMove(e);
+ finishSelectionMovement();
}
protected override bool OnDragStart(DragStartEvent e)
@@ -152,38 +156,39 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (e.Button == MouseButton.Right)
return false;
- if (!beginSelectionMovement())
+ if (movementBlueprint != null)
+ return true;
+
+ if (DragBox.HandleDrag(e))
{
- dragBox.UpdateDrag(e);
- dragBox.FadeIn(250, Easing.OutQuint);
+ DragBox.Show();
+ return true;
}
- return true;
+ return false;
}
- protected override bool OnDrag(DragEvent e)
+ protected override void OnDrag(DragEvent e)
{
if (e.Button == MouseButton.Right)
- return false;
+ return;
- if (!moveCurrentSelection(e))
- dragBox.UpdateDrag(e);
+ if (DragBox.State == Visibility.Visible)
+ DragBox.HandleDrag(e);
- return true;
+ moveCurrentSelection(e);
}
- protected override bool OnDragEnd(DragEndEvent e)
+ protected override void OnDragEnd(DragEndEvent e)
{
if (e.Button == MouseButton.Right)
- return false;
+ return;
- if (!finishSelectionMovement())
+ if (DragBox.State == Visibility.Visible)
{
- dragBox.FadeOut(250, Easing.OutQuint);
+ DragBox.Hide();
selectionHandler.UpdateVisibility();
}
-
- return true;
}
protected override bool OnKeyDown(KeyDownEvent e)
@@ -201,8 +206,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
return false;
}
- protected override bool OnKeyUp(KeyUpEvent e) => false;
-
public bool OnPressed(PlatformAction action)
{
switch (action.ActionType)
@@ -215,35 +218,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
return false;
}
- public bool OnReleased(PlatformAction action) => false;
-
- protected override void Update()
+ public void OnReleased(PlatformAction action)
{
- base.Update();
-
- if (currentPlacement != null)
- {
- if (composer.CursorInPlacementArea)
- currentPlacement.State = PlacementState.Shown;
- else if (currentPlacement?.PlacementBegun == false)
- currentPlacement.State = PlacementState.Hidden;
- }
}
#region Blueprint Addition/Removal
- private void addBlueprintFor(HitObject hitObject)
- {
- var drawable = composer.HitObjects.FirstOrDefault(d => d.HitObject == hitObject);
- if (drawable == null)
- return;
-
- addBlueprintFor(drawable);
- }
-
private void removeBlueprintFor(HitObject hitObject)
{
- var blueprint = selectionBlueprints.Single(m => m.DrawableObject.HitObject == hitObject);
+ var blueprint = selectionBlueprints.SingleOrDefault(m => m.HitObject == hitObject);
if (blueprint == null)
return;
@@ -255,11 +238,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
selectionBlueprints.Remove(blueprint);
}
- private void addBlueprintFor(DrawableHitObject hitObject)
+ protected virtual void AddBlueprintFor(HitObject hitObject)
{
- refreshTool();
-
- var blueprint = composer.CreateBlueprintFor(hitObject);
+ var blueprint = CreateBlueprintFor(hitObject);
if (blueprint == null)
return;
@@ -271,37 +252,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
#endregion
- #region Placement
-
- ///
- /// Refreshes the current placement tool.
- ///
- private void refreshTool()
- {
- placementBlueprintContainer.Clear();
- currentPlacement = null;
-
- var blueprint = CurrentTool?.CreatePlacementBlueprint();
-
- if (blueprint != null)
- {
- placementBlueprintContainer.Child = currentPlacement = blueprint;
-
- // Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame
- updatePlacementPosition(inputManager.CurrentState.Mouse.Position);
- }
- }
-
- private void updatePlacementPosition(Vector2 screenSpacePosition)
- {
- Vector2 snappedGridPosition = composer.GetSnappedPosition(ToLocalSpace(screenSpacePosition), 0).position;
- Vector2 snappedScreenSpacePosition = ToScreenSpace(snappedGridPosition);
-
- currentPlacement.UpdatePosition(snappedScreenSpacePosition);
- }
-
- #endregion
-
#region Selection
///
@@ -324,7 +274,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (!allowDeselection && selectionHandler.SelectedBlueprints.Any(s => s.IsHovered))
return;
- foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveBlueprints)
+ foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveChildren)
{
if (blueprint.IsHovered)
{
@@ -381,6 +331,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
selectionHandler.HandleSelected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 1);
+ beatmap.SelectedHitObjects.Add(blueprint.HitObject);
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
}
@@ -389,6 +340,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
selectionHandler.HandleDeselected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 0);
+ beatmap.SelectedHitObjects.Remove(blueprint.HitObject);
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
}
@@ -397,27 +349,25 @@ namespace osu.Game.Screens.Edit.Compose.Components
#region Selection Movement
- private Vector2? screenSpaceMovementStartPosition;
+ private Vector2? movementBlueprintOriginalPosition;
private SelectionBlueprint movementBlueprint;
///
/// Attempts to begin the movement of any selected blueprints.
///
- /// Whether movement began.
- private bool beginSelectionMovement()
+ private void prepareSelectionMovement()
{
- Debug.Assert(movementBlueprint == null);
+ if (!selectionHandler.SelectedBlueprints.Any())
+ return;
// Any selected blueprint that is hovered can begin the movement of the group, however only the earliest hitobject is used for movement
// A special case is added for when a click selection occurred before the drag
if (!clickSelectionBegan && !selectionHandler.SelectedBlueprints.Any(b => b.IsHovered))
- return false;
+ return;
// Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject
- movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.DrawableObject.HitObject.StartTime).First();
- screenSpaceMovementStartPosition = movementBlueprint.DrawableObject.ToScreenSpace(movementBlueprint.DrawableObject.OriginPosition);
-
- return true;
+ movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime).First();
+ movementBlueprintOriginalPosition = movementBlueprint.SelectionPoint; // todo: unsure if correct
}
///
@@ -430,17 +380,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (movementBlueprint == null)
return false;
- Debug.Assert(screenSpaceMovementStartPosition != null);
+ Debug.Assert(movementBlueprintOriginalPosition != null);
- Vector2 startPosition = screenSpaceMovementStartPosition.Value;
- HitObject draggedObject = movementBlueprint.DrawableObject.HitObject;
+ HitObject draggedObject = movementBlueprint.HitObject;
// The final movement position, relative to screenSpaceMovementStartPosition
- Vector2 movePosition = startPosition + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
- (Vector2 snappedPosition, double snappedTime) = composer.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime);
+ Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
+
+ (Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime);
// Move the hitobjects
- if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, startPosition, ToScreenSpace(snappedPosition))))
+ if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, ToScreenSpace(snappedPosition))))
return true;
// Apply the start time at the newly snapped-to position
@@ -460,7 +410,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (movementBlueprint == null)
return false;
- screenSpaceMovementStartPosition = null;
+ movementBlueprintOriginalPosition = null;
movementBlueprint = null;
return true;
@@ -474,34 +424,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (beatmap != null)
{
- beatmap.HitObjectAdded -= addBlueprintFor;
+ beatmap.HitObjectAdded -= AddBlueprintFor;
beatmap.HitObjectRemoved -= removeBlueprintFor;
}
}
-
- private class SelectionBlueprintContainer : Container
- {
- public IEnumerable AliveBlueprints => AliveInternalChildren.Cast();
-
- protected override int Compare(Drawable x, Drawable y)
- {
- if (!(x is SelectionBlueprint xBlueprint) || !(y is SelectionBlueprint yBlueprint))
- return base.Compare(x, y);
-
- return Compare(xBlueprint, yBlueprint);
- }
-
- public int Compare(SelectionBlueprint x, SelectionBlueprint y)
- {
- // dpeth is used to denote selected status (we always want selected blueprints to handle input first).
- int d = x.Depth.CompareTo(y.Depth);
- if (d != 0)
- return d;
-
- // Put earlier hitobjects towards the end of the list, so they handle input first
- int i = y.DrawableObject.HitObject.StartTime.CompareTo(x.DrawableObject.HitObject.StartTime);
- return i == 0 ? CompareReverseChildID(x, y) : i;
- }
- }
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
new file mode 100644
index 0000000000..3c41dead5d
--- /dev/null
+++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
@@ -0,0 +1,149 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Input;
+using osu.Framework.Input.Events;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Edit.Tools;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
+using osuTK;
+
+namespace osu.Game.Screens.Edit.Compose.Components
+{
+ ///
+ /// A blueprint container generally displayed as an overlay to a ruleset's playfield.
+ ///
+ public class ComposeBlueprintContainer : BlueprintContainer
+ {
+ [Resolved]
+ private HitObjectComposer composer { get; set; }
+
+ private PlacementBlueprint currentPlacement;
+
+ private readonly Container placementBlueprintContainer;
+
+ private InputManager inputManager;
+
+ private readonly IEnumerable drawableHitObjects;
+
+ public ComposeBlueprintContainer(IEnumerable drawableHitObjects)
+ {
+ this.drawableHitObjects = drawableHitObjects;
+
+ placementBlueprintContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ AddInternal(placementBlueprintContainer);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ inputManager = GetContainingInputManager();
+ }
+
+ #region Placement
+
+ ///
+ /// Refreshes the current placement tool.
+ ///
+ private void refreshTool()
+ {
+ placementBlueprintContainer.Clear();
+ currentPlacement = null;
+
+ var blueprint = CurrentTool?.CreatePlacementBlueprint();
+
+ if (blueprint != null)
+ {
+ placementBlueprintContainer.Child = currentPlacement = blueprint;
+
+ // Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame
+ updatePlacementPosition(inputManager.CurrentState.Mouse.Position);
+ }
+ }
+
+ private void updatePlacementPosition(Vector2 screenSpacePosition)
+ {
+ Vector2 snappedGridPosition = composer.GetSnappedPosition(ToLocalSpace(screenSpacePosition), 0).position;
+ Vector2 snappedScreenSpacePosition = ToScreenSpace(snappedGridPosition);
+
+ currentPlacement.UpdatePosition(snappedScreenSpacePosition);
+ }
+
+ #endregion
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ if (currentPlacement != null)
+ {
+ updatePlacementPosition(e.ScreenSpaceMousePosition);
+ return true;
+ }
+
+ return base.OnMouseMove(e);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (currentPlacement != null)
+ {
+ if (composer.CursorInPlacementArea)
+ currentPlacement.State = PlacementState.Shown;
+ else if (currentPlacement?.PlacementBegun == false)
+ currentPlacement.State = PlacementState.Hidden;
+ }
+ }
+
+ protected sealed override SelectionBlueprint CreateBlueprintFor(HitObject hitObject)
+ {
+ var drawable = drawableHitObjects.FirstOrDefault(d => d.HitObject == hitObject);
+ if (drawable == null)
+ return null;
+
+ return CreateBlueprintFor(drawable);
+ }
+
+ public virtual OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null;
+
+ protected override void AddBlueprintFor(HitObject hitObject)
+ {
+ refreshTool();
+ base.AddBlueprintFor(hitObject);
+ }
+
+ private HitObjectCompositionTool currentTool;
+
+ ///
+ /// The current placement tool.
+ ///
+ public HitObjectCompositionTool CurrentTool
+ {
+ get => currentTool;
+ set
+ {
+ if (currentTool == value)
+ return;
+
+ currentTool = value;
+
+ refreshTool();
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs
index 2a510e74fd..c5f1bd1575 100644
--- a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -15,11 +16,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
/// A box that displays the drag selection and provides selection events for users to handle.
///
- public class DragBox : CompositeDrawable
+ public class DragBox : CompositeDrawable, IStateful
{
- private readonly Action performSelection;
+ protected readonly Action PerformSelection;
- private Drawable box;
+ protected Drawable Box;
///
/// Creates a new .
@@ -27,7 +28,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// A delegate that performs drag selection.
public DragBox(Action performSelection)
{
- this.performSelection = performSelection;
+ PerformSelection = performSelection;
RelativeSizeAxes = Axes.Both;
AlwaysPresent = true;
@@ -37,20 +38,27 @@ namespace osu.Game.Screens.Edit.Compose.Components
[BackgroundDependencyLoader]
private void load()
{
- InternalChild = box = new Container
- {
- Masking = true,
- BorderColour = Color4.White,
- BorderThickness = SelectionHandler.BORDER_RADIUS,
- Child = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Alpha = 0.1f
- }
- };
+ InternalChild = Box = CreateBox();
}
- public void UpdateDrag(MouseButtonEvent e)
+ protected virtual Drawable CreateBox() => new Container
+ {
+ Masking = true,
+ BorderColour = Color4.White,
+ BorderThickness = SelectionHandler.BORDER_RADIUS,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.1f
+ }
+ };
+
+ ///
+ /// Handle a forwarded mouse event.
+ ///
+ /// The mouse event.
+ /// Whether the event should be handled and blocking.
+ public virtual bool HandleDrag(MouseButtonEvent e)
{
var dragPosition = e.ScreenSpaceMousePosition;
var dragStartPosition = e.ScreenSpaceMouseDownPosition;
@@ -63,10 +71,32 @@ namespace osu.Game.Screens.Edit.Compose.Components
var topLeft = ToLocalSpace(dragRectangle.TopLeft);
var bottomRight = ToLocalSpace(dragRectangle.BottomRight);
- box.Position = topLeft;
- box.Size = bottomRight - topLeft;
+ Box.Position = topLeft;
+ Box.Size = bottomRight - topLeft;
- performSelection?.Invoke(dragRectangle);
+ PerformSelection?.Invoke(dragRectangle);
+ return true;
}
+
+ private Visibility state;
+
+ public Visibility State
+ {
+ get => state;
+ set
+ {
+ if (value == state) return;
+
+ state = value;
+ this.FadeTo(state == Visibility.Hidden ? 0 : 1, 250, Easing.OutQuint);
+ StateChanged?.Invoke(state);
+ }
+ }
+
+ public override void Hide() => State = Visibility.Hidden;
+
+ public override void Show() => State = Visibility.Visible;
+
+ public event Action StateChanged;
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs b/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs
index fe0a47aec8..0792d0f80e 100644
--- a/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs
@@ -7,7 +7,7 @@ using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
{
///
- /// An event which occurs when a is moved.
+ /// An event which occurs when a is moved.
///
public class MoveSelectionEvent
{
@@ -16,11 +16,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
public readonly SelectionBlueprint Blueprint;
- ///
- /// The starting screen-space position of the hitobject.
- ///
- public readonly Vector2 ScreenSpaceStartPosition;
-
///
/// The expected screen-space position of the hitobject at the current cursor position.
///
@@ -29,18 +24,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
/// The distance between and the hitobject's current position, in the coordinate-space of the hitobject's parent.
///
- ///
- /// This does not use and does not represent the cumulative movement distance.
- ///
public readonly Vector2 InstantDelta;
- public MoveSelectionEvent(SelectionBlueprint blueprint, Vector2 screenSpaceStartPosition, Vector2 screenSpacePosition)
+ public MoveSelectionEvent(SelectionBlueprint blueprint, Vector2 screenSpacePosition)
{
Blueprint = blueprint;
- ScreenSpaceStartPosition = screenSpaceStartPosition;
ScreenSpacePosition = screenSpacePosition;
- InstantDelta = Blueprint.DrawableObject.Parent.ToLocalSpace(ScreenSpacePosition) - Blueprint.DrawableObject.Position;
+ InstantDelta = Blueprint.GetInstantDelta(ScreenSpacePosition);
}
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
index e2d7855eb5..fc46bf3fed 100644
--- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
@@ -33,12 +33,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
public IEnumerable SelectedBlueprints => selectedBlueprints;
private readonly List selectedBlueprints;
- public IEnumerable SelectedHitObjects => selectedBlueprints.Select(b => b.DrawableObject.HitObject);
+ public IEnumerable SelectedHitObjects => selectedBlueprints.Select(b => b.HitObject);
private Drawable outline;
- [Resolved]
- private IPlacementHandler placementHandler { get; set; }
+ [Resolved(CanBeNull = true)]
+ private EditorBeatmap editorBeatmap { get; set; }
public SelectionHandler()
{
@@ -87,7 +87,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
return false;
}
- public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete;
+ public void OnReleased(PlatformAction action)
+ {
+ }
#endregion
@@ -102,7 +104,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// Handle a blueprint becoming selected.
///
/// The blueprint.
- internal void HandleSelected(SelectionBlueprint blueprint) => selectedBlueprints.Add(blueprint);
+ internal void HandleSelected(SelectionBlueprint blueprint)
+ {
+ selectedBlueprints.Add(blueprint);
+ editorBeatmap.SelectedHitObjects.Add(blueprint.HitObject);
+
+ UpdateVisibility();
+ }
///
/// Handle a blueprint becoming deselected.
@@ -111,6 +119,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
internal void HandleDeselected(SelectionBlueprint blueprint)
{
selectedBlueprints.Remove(blueprint);
+ editorBeatmap.SelectedHitObjects.Remove(blueprint.HitObject);
// We don't want to update visibility if > 0, since we may be deselecting blueprints during drag-selection
if (selectedBlueprints.Count == 0)
@@ -139,14 +148,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
DeselectAll?.Invoke();
blueprint.Select();
}
-
- UpdateVisibility();
}
private void deleteSelected()
{
foreach (var h in selectedBlueprints.ToList())
- placementHandler.Delete(h.DrawableObject.HitObject);
+ editorBeatmap.Remove(h.HitObject);
}
#endregion
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
index b4f3b1f610..96395696c3 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
@@ -11,10 +12,14 @@ using osu.Framework.Input.Events;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
+using osu.Game.Rulesets.Edit;
+using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
- public class Timeline : ZoomableScrollContainer
+ [Cached(typeof(IDistanceSnapProvider))]
+ [Cached]
+ public class Timeline : ZoomableScrollContainer, IDistanceSnapProvider
{
public readonly Bindable WaveformVisible = new Bindable();
public readonly IBindable Beatmap = new Bindable();
@@ -143,10 +148,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
return false;
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
endUserDrag();
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
private void beginUserDrag()
@@ -162,5 +167,27 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
if (trackWasPlaying)
adjustableClock.Start();
}
+
+ [Resolved]
+ private EditorBeatmap beatmap { get; set; }
+
+ [Resolved]
+ private IBeatSnapProvider beatSnapProvider { get; set; }
+
+ public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time)
+ {
+ var targetTime = (position.X / Content.DrawWidth) * track.Length;
+ return (position, beatSnapProvider.SnapTime(targetTime, targetTime));
+ }
+
+ public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException();
+
+ public float DurationToDistance(double referenceTime, double duration) => throw new NotImplementedException();
+
+ public double DistanceToDuration(double referenceTime, float distance) => throw new NotImplementedException();
+
+ public double GetSnappedDurationFromDistance(double referenceTime, float distance) => throw new NotImplementedException();
+
+ public float GetSnappedDistanceFromDistance(double referenceTime, float distance) => throw new NotImplementedException();
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs
new file mode 100644
index 0000000000..3b9cb1df24
--- /dev/null
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs
@@ -0,0 +1,141 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Primitives;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Events;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Screens.Edit.Compose.Components.Timeline
+{
+ internal class TimelineBlueprintContainer : BlueprintContainer
+ {
+ [Resolved(CanBeNull = true)]
+ private Timeline timeline { get; set; }
+
+ private DragEvent lastDragEvent;
+
+ public TimelineBlueprintContainer()
+ {
+ RelativeSizeAxes = Axes.Both;
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+
+ Height = 0.4f;
+
+ AddInternal(new Box
+ {
+ Colour = Color4.Black,
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.1f,
+ });
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ DragBox.Alpha = 0;
+ }
+
+ protected override Container CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both };
+
+ protected override void OnDrag(DragEvent e)
+ {
+ if (timeline != null)
+ {
+ var timelineQuad = timeline.ScreenSpaceDrawQuad;
+ var mouseX = e.ScreenSpaceMousePosition.X;
+
+ // scroll if in a drag and dragging outside visible extents
+ if (mouseX > timelineQuad.TopRight.X)
+ timeline.ScrollBy((float)((mouseX - timelineQuad.TopRight.X) / 10 * Clock.ElapsedFrameTime));
+ else if (mouseX < timelineQuad.TopLeft.X)
+ timeline.ScrollBy((float)((mouseX - timelineQuad.TopLeft.X) / 10 * Clock.ElapsedFrameTime));
+ }
+
+ base.OnDrag(e);
+ lastDragEvent = e;
+ }
+
+ protected override void OnDragEnd(DragEndEvent e)
+ {
+ base.OnDragEnd(e);
+ lastDragEvent = null;
+ }
+
+ protected override void Update()
+ {
+ // trigger every frame so drags continue to update selection while playback is scrolling the timeline.
+ if (IsDragged)
+ OnDrag(lastDragEvent);
+
+ base.Update();
+ }
+
+ protected override SelectionHandler CreateSelectionHandler() => new TimelineSelectionHandler();
+
+ protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) => new TimelineHitObjectBlueprint(hitObject);
+
+ protected override DragBox CreateDragBox(Action performSelect) => new TimelineDragBox(performSelect);
+
+ internal class TimelineSelectionHandler : SelectionHandler
+ {
+ // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation
+ public override bool HandleMovement(MoveSelectionEvent moveEvent) => true;
+ }
+
+ private class TimelineDragBox : DragBox
+ {
+ private Vector2 lastMouseDown;
+ private float localMouseDown;
+
+ public TimelineDragBox(Action performSelect)
+ : base(performSelect)
+ {
+ }
+
+ protected override Drawable CreateBox() => new Box
+ {
+ RelativeSizeAxes = Axes.Y,
+ Alpha = 0.3f
+ };
+
+ public override bool HandleDrag(MouseButtonEvent e)
+ {
+ // store the original position of the mouse down, as we may be scrolled during selection.
+ if (lastMouseDown != e.ScreenSpaceMouseDownPosition)
+ {
+ lastMouseDown = e.ScreenSpaceMouseDownPosition;
+ localMouseDown = e.MouseDownPosition.X;
+ }
+
+ float selection1 = localMouseDown;
+ float selection2 = e.MousePosition.X;
+
+ Box.X = Math.Min(selection1, selection2);
+ Box.Width = Math.Abs(selection1 - selection2);
+
+ PerformSelection?.Invoke(Box.ScreenSpaceDrawQuad.AABBFloat);
+ return true;
+ }
+ }
+
+ protected class TimelineSelectionBlueprintContainer : Container
+ {
+ protected override Container Content { get; }
+
+ public TimelineSelectionBlueprintContainer()
+ {
+ AddInternal(new TimelinePart(Content = new Container { RelativeSizeAxes = Axes.Both }) { RelativeSizeAxes = Axes.Both });
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
new file mode 100644
index 0000000000..2ed5471444
--- /dev/null
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
@@ -0,0 +1,116 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using JetBrains.Annotations;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Primitives;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Screens.Edit.Compose.Components.Timeline
+{
+ public class TimelineHitObjectBlueprint : SelectionBlueprint
+ {
+ private readonly Circle circle;
+
+ private readonly Container extensionBar;
+
+ [UsedImplicitly]
+ private readonly Bindable startTime;
+
+ public const float THICKNESS = 3;
+
+ private const float circle_size = 16;
+
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || circle.ReceivePositionalInputAt(screenSpacePos);
+
+ public TimelineHitObjectBlueprint(HitObject hitObject)
+ : base(hitObject)
+ {
+ Anchor = Anchor.CentreLeft;
+ Origin = Anchor.CentreLeft;
+
+ startTime = hitObject.StartTimeBindable.GetBoundCopy();
+ startTime.BindValueChanged(time => X = (float)time.NewValue, true);
+
+ RelativePositionAxes = Axes.X;
+
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ if (hitObject is IHasEndTime)
+ {
+ AddInternal(extensionBar = new Container
+ {
+ CornerRadius = 2,
+ Masking = true,
+ Size = new Vector2(1, THICKNESS),
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ RelativePositionAxes = Axes.X,
+ RelativeSizeAxes = Axes.X,
+ Colour = Color4.Black,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ }
+ });
+ }
+
+ AddInternal(circle = new Circle
+ {
+ Size = new Vector2(circle_size),
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.Centre,
+ RelativePositionAxes = Axes.X,
+ AlwaysPresent = true,
+ Colour = Color4.White,
+ BorderColour = Color4.Black,
+ BorderThickness = THICKNESS,
+ });
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ // no bindable so we perform this every update
+ Width = (float)(HitObject.GetEndTime() - HitObject.StartTime);
+ }
+
+ protected override void OnSelected()
+ {
+ circle.BorderColour = Color4.Orange;
+ if (extensionBar != null)
+ extensionBar.Colour = Color4.Orange;
+ }
+
+ protected override void OnDeselected()
+ {
+ circle.BorderColour = Color4.Black;
+ if (extensionBar != null)
+ extensionBar.Colour = Color4.Black;
+ }
+
+ public override Quad SelectionQuad
+ {
+ get
+ {
+ // correctly include the circle in the selection quad region, as it is usually outside the blueprint itself.
+ var circleQuad = circle.ScreenSpaceDrawQuad;
+ var actualQuad = ScreenSpaceDrawQuad;
+
+ return new Quad(circleQuad.TopLeft, Vector2.ComponentMax(actualQuad.TopRight, circleQuad.TopRight),
+ circleQuad.BottomLeft, Vector2.ComponentMax(actualQuad.BottomRight, circleQuad.BottomRight));
+ }
+ }
+
+ public override Vector2 SelectionPoint => ScreenSpaceDrawQuad.TopLeft;
+ }
+}
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs
deleted file mode 100644
index b20f2fa11d..0000000000
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectDisplay.cs
+++ /dev/null
@@ -1,108 +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 System.Linq;
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Objects.Types;
-using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
-using osuTK;
-using osuTK.Graphics;
-
-namespace osu.Game.Screens.Edit.Compose.Components.Timeline
-{
- internal class TimelineHitObjectDisplay : TimelinePart
- {
- private EditorBeatmap beatmap { get; }
-
- public TimelineHitObjectDisplay(EditorBeatmap beatmap)
- {
- RelativeSizeAxes = Axes.Both;
-
- this.beatmap = beatmap;
- }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- foreach (var h in beatmap.HitObjects)
- add(h);
-
- beatmap.HitObjectAdded += add;
- beatmap.HitObjectRemoved += remove;
- beatmap.StartTimeChanged += h =>
- {
- remove(h);
- add(h);
- };
- }
-
- private void remove(HitObject h)
- {
- foreach (var d in Children.OfType().Where(c => c.HitObject == h))
- d.Expire();
- }
-
- private void add(HitObject h)
- {
- var yOffset = Children.Count(d => d.X == h.StartTime);
-
- Add(new TimelineHitObjectRepresentation(h) { Y = -yOffset * TimelineHitObjectRepresentation.THICKNESS });
- }
-
- private class TimelineHitObjectRepresentation : CompositeDrawable
- {
- public const float THICKNESS = 3;
-
- public readonly HitObject HitObject;
-
- public TimelineHitObjectRepresentation(HitObject hitObject)
- {
- HitObject = hitObject;
- Anchor = Anchor.CentreLeft;
- Origin = Anchor.CentreLeft;
-
- Width = (float)(hitObject.GetEndTime() - hitObject.StartTime);
-
- X = (float)hitObject.StartTime;
-
- RelativePositionAxes = Axes.X;
- RelativeSizeAxes = Axes.X;
-
- if (hitObject is IHasEndTime)
- {
- AddInternal(new Container
- {
- CornerRadius = 2,
- Masking = true,
- Size = new Vector2(1, THICKNESS),
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- RelativePositionAxes = Axes.X,
- RelativeSizeAxes = Axes.X,
- Colour = Color4.Black,
- Child = new Box
- {
- RelativeSizeAxes = Axes.Both,
- }
- });
- }
-
- AddInternal(new Circle
- {
- Size = new Vector2(16),
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.Centre,
- RelativePositionAxes = Axes.X,
- AlwaysPresent = true,
- Colour = Color4.White,
- BorderColour = Color4.Black,
- BorderThickness = THICKNESS,
- });
- }
- }
- }
-}
diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
index 1a6aae294a..cdea200e10 100644
--- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
+++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
@@ -32,6 +32,6 @@ namespace osu.Game.Screens.Edit.Compose
return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer));
}
- protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineHitObjectDisplay(EditorBeatmap);
+ protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineBlueprintContainer();
}
}
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 14d69cddd1..eae94a3c8e 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -26,6 +26,7 @@ using osu.Framework.Input.Bindings;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Cursor;
using osu.Game.Input.Bindings;
+using osu.Game.Rulesets.Edit;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Edit.Timing;
@@ -34,7 +35,8 @@ using osu.Game.Users;
namespace osu.Game.Screens.Edit
{
- public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler
+ [Cached(typeof(IBeatSnapProvider))]
+ public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IBeatSnapProvider
{
public override float BackgroundParallaxAmount => 0.1f;
@@ -44,6 +46,11 @@ namespace osu.Game.Screens.Edit
public override bool DisallowExternalBeatmapRulesetChanges => true;
+ public override bool AllowRateAdjustments => false;
+
+ [Resolved]
+ private BeatmapManager beatmapManager { get; set; }
+
private Box bottomBackground;
private Container screenContainer;
@@ -56,7 +63,6 @@ namespace osu.Game.Screens.Edit
private EditorBeatmap editorBeatmap;
private DependencyContainer dependencies;
- private GameHost host;
protected override UserActivity InitialActivity => new UserActivity.Editing(Beatmap.Value.BeatmapInfo);
@@ -66,8 +72,6 @@ namespace osu.Game.Screens.Edit
[BackgroundDependencyLoader]
private void load(OsuColour colours, GameHost host)
{
- this.host = host;
-
beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor;
beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
@@ -77,11 +81,14 @@ namespace osu.Game.Screens.Edit
clock.ChangeSource(sourceClock);
playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset);
- editorBeatmap = new EditorBeatmap(playableBeatmap);
+ editorBeatmap = new EditorBeatmap(playableBeatmap, beatDivisor);
dependencies.CacheAs(clock);
dependencies.CacheAs(clock);
+
+ // todo: remove caching of this and consume via editorBeatmap?
dependencies.Cache(beatDivisor);
+
dependencies.CacheAs(editorBeatmap);
EditorMenuBar menuBar;
@@ -90,7 +97,8 @@ namespace osu.Game.Screens.Edit
if (RuntimeInfo.IsDesktop)
{
- fileMenuItems.Add(new EditorMenuItem("Export", MenuItemType.Standard, exportBeatmap));
+ fileMenuItems.Add(new EditorMenuItem("Save", MenuItemType.Standard, saveBeatmap));
+ fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap));
fileMenuItems.Add(new EditorMenuItemSpacer());
}
@@ -205,6 +213,15 @@ namespace osu.Game.Screens.Edit
case Key.Right:
seek(e, 1);
return true;
+
+ case Key.S:
+ if (e.ControlPressed)
+ {
+ saveBeatmap();
+ return true;
+ }
+
+ break;
}
return base.OnKeyDown(e);
@@ -243,12 +260,8 @@ namespace osu.Game.Screens.Edit
return false;
}
- public bool OnReleased(GlobalAction action) => action == GlobalAction.Back;
-
- public override void OnResuming(IScreen last)
+ public void OnReleased(GlobalAction action)
{
- base.OnResuming(last);
- Beatmap.Value.Track?.Stop();
}
public override void OnEntering(IScreen last)
@@ -274,7 +287,6 @@ namespace osu.Game.Screens.Edit
private void resetTrack(bool seekToStart = false)
{
- Beatmap.Value.Track?.ResetSpeedAdjustments();
Beatmap.Value.Track?.Stop();
if (seekToStart)
@@ -292,8 +304,6 @@ namespace osu.Game.Screens.Edit
}
}
- private void exportBeatmap() => host.OpenFileExternally(Beatmap.Value.Save());
-
private void onModeChanged(ValueChangedEvent e)
{
currentScreen?.Exit();
@@ -329,5 +339,19 @@ namespace osu.Game.Screens.Edit
else
clock.SeekForward(!clock.IsRunning, amount);
}
+
+ private void saveBeatmap() => beatmapManager.Save(playableBeatmap.BeatmapInfo, editorBeatmap);
+
+ private void exportBeatmap()
+ {
+ saveBeatmap();
+ beatmapManager.Export(Beatmap.Value.BeatmapSetInfo);
+ }
+
+ public double SnapTime(double referenceTime, double duration) => editorBeatmap.SnapTime(referenceTime, duration);
+
+ public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime);
+
+ public int BeatDivisor => beatDivisor.Value;
}
}
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 6ed74dfdb0..9c75d40bec 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -8,11 +8,12 @@ using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Screens.Edit
{
- public class EditorBeatmap : IBeatmap
+ public class EditorBeatmap : IBeatmap, IBeatSnapProvider
{
///
/// Invoked when a is added to this .
@@ -29,13 +30,18 @@ namespace osu.Game.Screens.Edit
///
public event Action StartTimeChanged;
+ public BindableList SelectedHitObjects { get; } = new BindableList();
+
public readonly IBeatmap PlayableBeatmap;
+ private readonly BindableBeatDivisor beatDivisor;
+
private readonly Dictionary> startTimeBindables = new Dictionary>();
- public EditorBeatmap(IBeatmap playableBeatmap)
+ public EditorBeatmap(IBeatmap playableBeatmap, BindableBeatDivisor beatDivisor = null)
{
PlayableBeatmap = playableBeatmap;
+ this.beatDivisor = beatDivisor;
foreach (var obj in HitObjects)
trackStartTime(obj);
@@ -121,5 +127,17 @@ namespace osu.Game.Screens.Edit
return list.Count - 1;
}
+
+ public double SnapTime(double referenceTime, double duration)
+ {
+ double beatLength = GetBeatLengthAtTime(referenceTime);
+
+ // A 1ms offset prevents rounding errors due to minute variations in duration
+ return (int)((duration + 1) / beatLength) * beatLength;
+ }
+
+ public double GetBeatLengthAtTime(double referenceTime) => ControlPointInfo.TimingPointAt(referenceTime).BeatLength / BeatDivisor;
+
+ public int BeatDivisor => beatDivisor?.Value ?? 1;
}
}
diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs
index aa8d99b517..8967f24185 100644
--- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs
+++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Screens.Edit
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
- private TimelineArea timelineArea;
+ private Container timelineContainer;
[BackgroundDependencyLoader(true)]
private void load([CanBeNull] BindableBeatDivisor beatDivisor)
@@ -62,11 +62,10 @@ namespace osu.Game.Screens.Edit
{
new Drawable[]
{
- new Container
+ timelineContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = 5 },
- Child = timelineArea = CreateTimelineArea()
},
new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
},
@@ -100,14 +99,16 @@ namespace osu.Game.Screens.Edit
mainContent.Add(content);
content.FadeInFromZero(300, Easing.OutQuint);
- LoadComponentAsync(CreateTimelineContent(), timelineArea.Add);
+ LoadComponentAsync(new TimelineArea
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = CreateTimelineContent()
+ }, timelineContainer.Add);
});
}
protected abstract Drawable CreateMainContent();
protected virtual Drawable CreateTimelineContent() => new Container();
-
- protected TimelineArea CreateTimelineArea() => new TimelineArea { RelativeSizeAxes = Axes.Both };
}
}
diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs
index fac6b69e1f..6708ce0ba0 100644
--- a/osu.Game/Screens/Menu/Button.cs
+++ b/osu.Game/Screens/Menu/Button.cs
@@ -194,10 +194,10 @@ namespace osu.Game.Screens.Menu
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
boxHoverLayer.FadeTo(0, 1000, Easing.OutQuint);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override bool OnClick(ClickEvent e)
diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs
index ed8e4c70f9..d94f8aa11a 100644
--- a/osu.Game/Screens/Menu/ButtonSystem.cs
+++ b/osu.Game/Screens/Menu/ButtonSystem.cs
@@ -211,7 +211,9 @@ namespace osu.Game.Screens.Menu
}
}
- public bool OnReleased(GlobalAction action) => false;
+ public void OnReleased(GlobalAction action)
+ {
+ }
private bool goBack()
{
diff --git a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs
index aaa3a77e74..db2faeb60a 100644
--- a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs
+++ b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs
@@ -24,16 +24,13 @@ namespace osu.Game.Screens.Menu
return false;
}
- public bool OnReleased(GlobalAction action)
+ public void OnReleased(GlobalAction action)
{
if (action == GlobalAction.Back)
{
if (!Fired)
AbortConfirm();
- return true;
}
-
- return false;
}
}
}
diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs
index b28d572b5c..cb5ceefb0f 100644
--- a/osu.Game/Screens/Menu/MainMenu.cs
+++ b/osu.Game/Screens/Menu/MainMenu.cs
@@ -69,6 +69,9 @@ namespace osu.Game.Screens.Menu
private ExitConfirmOverlay exitConfirmOverlay;
+ private ParallaxContainer buttonsContainer;
+ private SongTicker songTicker;
+
[BackgroundDependencyLoader(true)]
private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics)
{
@@ -89,9 +92,9 @@ namespace osu.Game.Screens.Menu
});
}
- AddRangeInternal(new Drawable[]
+ AddRangeInternal(new[]
{
- new ParallaxContainer
+ buttonsContainer = new ParallaxContainer
{
ParallaxAmount = 0.01f,
Children = new Drawable[]
@@ -107,6 +110,13 @@ namespace osu.Game.Screens.Menu
}
},
sideFlashes = new MenuSideFlashes(),
+ songTicker = new SongTicker
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Margin = new MarginPadding { Right = 15, Top = 5 }
+ },
+ exitConfirmOverlay?.CreateProxy() ?? Drawable.Empty()
});
buttons.StateChanged += state =>
@@ -190,7 +200,7 @@ namespace osu.Game.Screens.Menu
buttons.State = ButtonSystemState.TopLevel;
this.FadeIn(FADE_IN_DURATION, Easing.OutQuint);
- this.MoveTo(new Vector2(0, 0), FADE_IN_DURATION, Easing.OutQuint);
+ buttonsContainer.MoveTo(new Vector2(0, 0), FADE_IN_DURATION, Easing.OutQuint);
sideFlashes.Delay(FADE_IN_DURATION).FadeIn(64, Easing.InQuint);
}
@@ -227,7 +237,7 @@ namespace osu.Game.Screens.Menu
buttons.State = ButtonSystemState.EnteringMode;
this.FadeOut(FADE_OUT_DURATION, Easing.InSine);
- this.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine);
+ buttonsContainer.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine);
sideFlashes.FadeOut(64, Easing.OutQuint);
}
@@ -262,6 +272,9 @@ namespace osu.Game.Screens.Menu
}
buttons.State = ButtonSystemState.Exit;
+
+ songTicker.Hide();
+
this.FadeOut(3000);
return base.OnExiting(next);
}
diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs
index 33b6ee8025..be2f29cbe9 100644
--- a/osu.Game/Screens/Menu/OsuLogo.cs
+++ b/osu.Game/Screens/Menu/OsuLogo.cs
@@ -353,12 +353,11 @@ namespace osu.Game.Screens.Menu
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;
logoBounceContainer.ScaleTo(1f, 500, Easing.OutElastic);
- return true;
}
protected override bool OnClick(ClickEvent e)
diff --git a/osu.Game/Screens/Menu/SongTicker.cs b/osu.Game/Screens/Menu/SongTicker.cs
new file mode 100644
index 0000000000..c4943e77d5
--- /dev/null
+++ b/osu.Game/Screens/Menu/SongTicker.cs
@@ -0,0 +1,72 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osuTK;
+using osu.Game.Graphics;
+using osu.Framework.Bindables;
+using osu.Game.Beatmaps;
+using osu.Framework.Localisation;
+
+namespace osu.Game.Screens.Menu
+{
+ public class SongTicker : Container
+ {
+ private const int fade_duration = 800;
+
+ [Resolved]
+ private Bindable beatmap { get; set; }
+
+ private readonly OsuSpriteText title, artist;
+
+ public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
+
+ public SongTicker()
+ {
+ AutoSizeAxes = Axes.Both;
+ Child = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 3),
+ Children = new Drawable[]
+ {
+ title = new OsuSpriteText
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light, italics: true)
+ },
+ artist = new OsuSpriteText
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Font = OsuFont.GetFont(size: 16)
+ }
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ beatmap.BindValueChanged(_ => Scheduler.AddOnce(show), true);
+ }
+
+ private void show()
+ {
+ var metadata = beatmap.Value.Metadata;
+
+ title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title));
+ artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist));
+
+ this.FadeInFromZero(fade_duration / 2f)
+ .Delay(4000)
+ .Then().FadeOut(fade_duration);
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
index adfbe977a4..98e2bc5a03 100644
--- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs
+++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
@@ -163,8 +163,6 @@ namespace osu.Game.Screens.Play
// Don't let mouse down events through the overlay or people can click circles while paused.
protected override bool OnMouseDown(MouseDownEvent e) => true;
- protected override bool OnMouseUp(MouseUpEvent e) => true;
-
protected override bool OnMouseMove(MouseMoveEvent e) => true;
protected void AddButton(string text, Color4 colour, Action action)
@@ -247,16 +245,8 @@ namespace osu.Game.Screens.Play
return false;
}
- public bool OnReleased(GlobalAction action)
+ public void OnReleased(GlobalAction action)
{
- switch (action)
- {
- case GlobalAction.Back:
- case GlobalAction.Select:
- return true;
- }
-
- return false;
}
private void buttonSelectionChanged(DialogButton button, bool isSelected)
diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs
index 6196ce4026..4d28f00f39 100644
--- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs
+++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs
@@ -77,6 +77,19 @@ namespace osu.Game.Screens.Play.HUD
case ScoreMeterType.HitErrorRight:
createBar(true);
break;
+
+ case ScoreMeterType.ColourBoth:
+ createColour(false);
+ createColour(true);
+ break;
+
+ case ScoreMeterType.ColourLeft:
+ createColour(false);
+ break;
+
+ case ScoreMeterType.ColourRight:
+ createColour(true);
+ break;
}
}
@@ -90,6 +103,24 @@ namespace osu.Game.Screens.Play.HUD
Alpha = 0,
};
+ completeDisplayLoading(display);
+ }
+
+ private void createColour(bool rightAligned)
+ {
+ var display = new ColourHitErrorMeter(hitWindows)
+ {
+ Margin = new MarginPadding(margin),
+ Anchor = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft,
+ Origin = rightAligned ? Anchor.CentreRight : Anchor.CentreLeft,
+ Alpha = 0,
+ };
+
+ completeDisplayLoading(display);
+ }
+
+ private void completeDisplayLoading(HitErrorMeter display)
+ {
Add(display);
display.FadeInFromZero(fade_duration, Easing.OutQuint);
}
diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
index 03a0f23fb6..208bdd17ad 100644
--- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
+++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs
@@ -163,30 +163,9 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
centre.Width = 2.5f;
colourBars.Add(centre);
- Color4 getColour(HitResult result)
- {
- switch (result)
- {
- case HitResult.Meh:
- return colours.Yellow;
-
- case HitResult.Ok:
- return colours.Green;
-
- case HitResult.Good:
- return colours.GreenLight;
-
- case HitResult.Great:
- return colours.Blue;
-
- default:
- return colours.BlueLight;
- }
- }
-
Drawable createColourBar(HitResult result, float height, bool first = false)
{
- var colour = getColour(result);
+ var colour = GetColourForHitResult(result);
if (first)
{
@@ -201,7 +180,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = getColour(result),
+ Colour = colour,
Height = height * gradient_start
},
new Box
diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs
new file mode 100644
index 0000000000..657235bfd4
--- /dev/null
+++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.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 System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Scoring;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Screens.Play.HUD.HitErrorMeters
+{
+ public class ColourHitErrorMeter : HitErrorMeter
+ {
+ private const int animation_duration = 200;
+
+ private readonly JudgementFlow judgementsFlow;
+
+ public ColourHitErrorMeter(HitWindows hitWindows)
+ : base(hitWindows)
+ {
+ AutoSizeAxes = Axes.Both;
+ InternalChild = judgementsFlow = new JudgementFlow();
+ }
+
+ public override void OnNewJudgement(JudgementResult judgement) => judgementsFlow.Push(GetColourForHitResult(HitWindows.ResultFor(judgement.TimeOffset)));
+
+ private class JudgementFlow : FillFlowContainer
+ {
+ private const int max_available_judgements = 20;
+ private const int drawable_judgement_size = 8;
+ private const int spacing = 2;
+
+ public override IEnumerable FlowingChildren => base.FlowingChildren.Reverse();
+
+ public JudgementFlow()
+ {
+ AutoSizeAxes = Axes.X;
+ Height = max_available_judgements * (drawable_judgement_size + spacing) - spacing;
+ Spacing = new Vector2(0, spacing);
+ Direction = FillDirection.Vertical;
+ LayoutDuration = animation_duration;
+ LayoutEasing = Easing.OutQuint;
+ }
+
+ public void Push(Color4 colour)
+ {
+ Add(new HitErrorCircle(colour, drawable_judgement_size));
+
+ if (Children.Count > max_available_judgements)
+ Children.FirstOrDefault(c => !c.IsRemoved)?.Remove();
+ }
+ }
+
+ private class HitErrorCircle : Container
+ {
+ public bool IsRemoved { get; private set; }
+
+ private readonly Circle circle;
+
+ public HitErrorCircle(Color4 colour, int size)
+ {
+ Size = new Vector2(size);
+ Child = circle = new Circle
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ Colour = colour
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ circle.FadeInFromZero(animation_duration, Easing.OutQuint);
+ circle.MoveToY(-DrawSize.Y);
+ circle.MoveToY(0, animation_duration, Easing.OutQuint);
+ }
+
+ public void Remove()
+ {
+ IsRemoved = true;
+
+ this.FadeOut(animation_duration, Easing.OutQuint).Expire();
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs
index dee25048ed..b3edfdedec 100644
--- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs
+++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs
@@ -1,9 +1,12 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
+using osuTK.Graphics;
namespace osu.Game.Screens.Play.HUD.HitErrorMeters
{
@@ -11,11 +14,38 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
{
protected readonly HitWindows HitWindows;
+ [Resolved]
+ private OsuColour colours { get; set; }
+
protected HitErrorMeter(HitWindows hitWindows)
{
HitWindows = hitWindows;
}
public abstract void OnNewJudgement(JudgementResult judgement);
+
+ protected Color4 GetColourForHitResult(HitResult result)
+ {
+ switch (result)
+ {
+ case HitResult.Miss:
+ return colours.Red;
+
+ case HitResult.Meh:
+ return colours.Yellow;
+
+ case HitResult.Ok:
+ return colours.Green;
+
+ case HitResult.Good:
+ return colours.GreenLight;
+
+ case HitResult.Great:
+ return colours.Blue;
+
+ default:
+ return colours.BlueLight;
+ }
+ }
}
}
diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs
index 7946e6d322..684834123b 100644
--- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs
+++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs
@@ -259,16 +259,14 @@ namespace osu.Game.Screens.Play.HUD
return false;
}
- public bool OnReleased(GlobalAction action)
+ public void OnReleased(GlobalAction action)
{
switch (action)
{
case GlobalAction.Back:
AbortConfirm();
- return true;
+ break;
}
-
- return false;
}
protected override bool OnMouseDown(MouseDownEvent e)
@@ -278,11 +276,10 @@ namespace osu.Game.Screens.Play.HUD
return true;
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
if (!e.HasAnyButtonPressed)
AbortConfirm();
- return true;
}
}
}
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index 236bdc8442..a5f8051557 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -131,7 +131,6 @@ namespace osu.Game.Screens.Play
BindDrawableRuleset(drawableRuleset);
Progress.Objects = drawableRuleset.Objects;
- Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value;
Progress.RequestSeek = time => RequestSeek(time);
Progress.ReferenceClock = drawableRuleset.FrameStableClock;
}
diff --git a/osu.Game/Screens/Play/HotkeyExitOverlay.cs b/osu.Game/Screens/Play/HotkeyExitOverlay.cs
index c18aecda55..8d7e2481bf 100644
--- a/osu.Game/Screens/Play/HotkeyExitOverlay.cs
+++ b/osu.Game/Screens/Play/HotkeyExitOverlay.cs
@@ -17,12 +17,11 @@ namespace osu.Game.Screens.Play
return true;
}
- public bool OnReleased(GlobalAction action)
+ public void OnReleased(GlobalAction action)
{
- if (action != GlobalAction.QuickExit) return false;
+ if (action != GlobalAction.QuickExit) return;
AbortConfirm();
- return true;
}
}
}
diff --git a/osu.Game/Screens/Play/HotkeyRetryOverlay.cs b/osu.Game/Screens/Play/HotkeyRetryOverlay.cs
index f1b851f2d5..58fd941f36 100644
--- a/osu.Game/Screens/Play/HotkeyRetryOverlay.cs
+++ b/osu.Game/Screens/Play/HotkeyRetryOverlay.cs
@@ -17,12 +17,11 @@ namespace osu.Game.Screens.Play
return true;
}
- public bool OnReleased(GlobalAction action)
+ public void OnReleased(GlobalAction action)
{
- if (action != GlobalAction.QuickRetry) return false;
+ if (action != GlobalAction.QuickRetry) return;
AbortConfirm();
- return true;
}
}
}
diff --git a/osu.Game/Screens/Play/KeyCounterAction.cs b/osu.Game/Screens/Play/KeyCounterAction.cs
index 33d675358c..00eddcc776 100644
--- a/osu.Game/Screens/Play/KeyCounterAction.cs
+++ b/osu.Game/Screens/Play/KeyCounterAction.cs
@@ -27,15 +27,14 @@ namespace osu.Game.Screens.Play
return false;
}
- public bool OnReleased(T action, bool forwards)
+ public void OnReleased(T action, bool forwards)
{
if (!EqualityComparer.Default.Equals(action, Action))
- return false;
+ return;
IsLit = false;
if (!forwards)
Decrement();
- return false;
}
}
}
diff --git a/osu.Game/Screens/Play/KeyCounterKeyboard.cs b/osu.Game/Screens/Play/KeyCounterKeyboard.cs
index 29188b6b59..187dcc1264 100644
--- a/osu.Game/Screens/Play/KeyCounterKeyboard.cs
+++ b/osu.Game/Screens/Play/KeyCounterKeyboard.cs
@@ -27,10 +27,10 @@ namespace osu.Game.Screens.Play
return base.OnKeyDown(e);
}
- protected override bool OnKeyUp(KeyUpEvent e)
+ protected override void OnKeyUp(KeyUpEvent e)
{
if (e.Key == Key) IsLit = false;
- return base.OnKeyUp(e);
+ base.OnKeyUp(e);
}
}
}
diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouse.cs
index 828441de6e..e55525c5e8 100644
--- a/osu.Game/Screens/Play/KeyCounterMouse.cs
+++ b/osu.Game/Screens/Play/KeyCounterMouse.cs
@@ -45,10 +45,10 @@ namespace osu.Game.Screens.Play
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
if (e.Button == Button) IsLit = false;
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
}
}
diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs
index 772d326c7f..3daf5b1ff1 100644
--- a/osu.Game/Screens/Play/SkipOverlay.cs
+++ b/osu.Game/Screens/Play/SkipOverlay.cs
@@ -143,7 +143,9 @@ namespace osu.Game.Screens.Play
return false;
}
- public bool OnReleased(GlobalAction action) => false;
+ public void OnReleased(GlobalAction action)
+ {
+ }
private class FadeContainer : Container, IStateful
{
@@ -202,10 +204,9 @@ namespace osu.Game.Screens.Play
return true;
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
Show();
- return true;
}
public override void Hide() => State = Visibility.Hidden;
@@ -311,10 +312,10 @@ namespace osu.Game.Screens.Play
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
aspect.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override bool OnClick(ClickEvent e)
diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs
index 713d27bd16..aa745f5ba2 100644
--- a/osu.Game/Screens/Play/SongProgress.cs
+++ b/osu.Game/Screens/Play/SongProgress.cs
@@ -11,6 +11,7 @@ using osu.Framework.Allocation;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Timing;
+using osu.Game.Configuration;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
@@ -18,8 +19,9 @@ namespace osu.Game.Screens.Play
{
public class SongProgress : OverlayContainer
{
+ private const int info_height = 20;
private const int bottom_bar_height = 5;
-
+ private const float graph_height = SquareGraph.Column.WIDTH * 6;
private static readonly Vector2 handle_size = new Vector2(10, 18);
private const float transition_duration = 200;
@@ -30,12 +32,19 @@ namespace osu.Game.Screens.Play
public Action RequestSeek;
- public override bool HandleNonPositionalInput => AllowSeeking;
- public override bool HandlePositionalInput => AllowSeeking;
+ ///
+ /// Whether seeking is allowed and the progress bar should be shown.
+ ///
+ public readonly Bindable AllowSeeking = new Bindable();
+
+ public readonly Bindable ShowGraph = new Bindable();
//TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list).
private double lastHitTime => objects.Last().GetEndTime() + 1;
+ public override bool HandleNonPositionalInput => AllowSeeking.Value;
+ public override bool HandlePositionalInput => AllowSeeking.Value;
+
private double firstHitTime => objects.First().StartTime;
private IEnumerable objects;
@@ -54,27 +63,14 @@ namespace osu.Game.Screens.Play
}
}
- private readonly BindableBool replayLoaded = new BindableBool();
-
public IClock ReferenceClock;
private IClock gameplayClock;
- [BackgroundDependencyLoader(true)]
- private void load(OsuColour colours, GameplayClock clock)
- {
- if (clock != null)
- gameplayClock = clock;
-
- graph.FillColour = bar.FillColour = colours.BlueLighter;
- }
-
public SongProgress()
{
- const float graph_height = SquareGraph.Column.WIDTH * 6;
-
- Height = bottom_bar_height + graph_height + handle_size.Y;
- Y = bottom_bar_height;
+ Masking = true;
+ Height = bottom_bar_height + graph_height + handle_size.Y + info_height;
Children = new Drawable[]
{
@@ -83,8 +79,7 @@ namespace osu.Game.Screens.Play
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Margin = new MarginPadding { Bottom = bottom_bar_height + graph_height },
+ Height = info_height,
},
graph = new SongProgressGraph
{
@@ -96,7 +91,6 @@ namespace osu.Game.Screens.Play
},
bar = new SongProgressBar(bottom_bar_height, graph_height, handle_size)
{
- Alpha = 0,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
OnSeek = time => RequestSeek?.Invoke(time),
@@ -104,46 +98,34 @@ namespace osu.Game.Screens.Play
};
}
- protected override void LoadComplete()
+ [BackgroundDependencyLoader(true)]
+ private void load(OsuColour colours, GameplayClock clock, OsuConfigManager config)
{
base.LoadComplete();
+ if (clock != null)
+ gameplayClock = clock;
+
+ config.BindWith(OsuSetting.ShowProgressGraph, ShowGraph);
+
+ graph.FillColour = bar.FillColour = colours.BlueLighter;
+ }
+
+ protected override void LoadComplete()
+ {
Show();
- replayLoaded.ValueChanged += loaded => AllowSeeking = loaded.NewValue;
- replayLoaded.TriggerChange();
+ AllowSeeking.BindValueChanged(_ => updateBarVisibility(), true);
+ ShowGraph.BindValueChanged(_ => updateGraphVisibility(), true);
}
public void BindDrawableRuleset(DrawableRuleset drawableRuleset)
{
- replayLoaded.BindTo(drawableRuleset.HasReplayLoaded);
- }
-
- private bool allowSeeking;
-
- public bool AllowSeeking
- {
- get => allowSeeking;
- set
- {
- if (allowSeeking == value) return;
-
- allowSeeking = value;
- updateBarVisibility();
- }
- }
-
- private void updateBarVisibility()
- {
- bar.FadeTo(allowSeeking ? 1 : 0, transition_duration, Easing.In);
- this.MoveTo(new Vector2(0, allowSeeking ? 0 : bottom_bar_height), transition_duration, Easing.In);
-
- info.Margin = new MarginPadding { Bottom = Height - (allowSeeking ? 0 : handle_size.Y) };
+ AllowSeeking.BindTo(drawableRuleset.HasReplayLoaded);
}
protected override void PopIn()
{
- updateBarVisibility();
this.FadeIn(500, Easing.OutQuint);
}
@@ -167,5 +149,28 @@ namespace osu.Game.Screens.Play
bar.CurrentTime = gameplayTime;
graph.Progress = (int)(graph.ColumnCount * progress);
}
+
+ private void updateBarVisibility()
+ {
+ bar.ShowHandle = AllowSeeking.Value;
+
+ updateInfoMargin();
+ }
+
+ private void updateGraphVisibility()
+ {
+ float barHeight = bottom_bar_height + handle_size.Y;
+
+ bar.ResizeHeightTo(ShowGraph.Value ? barHeight + graph_height : barHeight, transition_duration, Easing.In);
+ graph.MoveToY(ShowGraph.Value ? 0 : bottom_bar_height + graph_height, transition_duration, Easing.In);
+
+ updateInfoMargin();
+ }
+
+ private void updateInfoMargin()
+ {
+ float finalMargin = bottom_bar_height + (AllowSeeking.Value ? handle_size.Y : 0) + (ShowGraph.Value ? graph_height : 0);
+ info.TransformTo(nameof(info.Margin), new MarginPadding { Bottom = finalMargin }, transition_duration, Easing.In);
+ }
}
}
diff --git a/osu.Game/Screens/Play/SongProgressBar.cs b/osu.Game/Screens/Play/SongProgressBar.cs
index 9df36c9c2b..5052b32335 100644
--- a/osu.Game/Screens/Play/SongProgressBar.cs
+++ b/osu.Game/Screens/Play/SongProgressBar.cs
@@ -19,6 +19,23 @@ namespace osu.Game.Screens.Play
private readonly Box fill;
private readonly Container handleBase;
+ private readonly Container handleContainer;
+
+ private bool showHandle;
+
+ public bool ShowHandle
+ {
+ get => showHandle;
+ set
+ {
+ if (value == showHandle)
+ return;
+
+ showHandle = value;
+
+ handleBase.FadeTo(showHandle ? 1 : 0, 200);
+ }
+ }
public Color4 FillColour
{
@@ -74,7 +91,7 @@ namespace osu.Game.Screens.Play
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Width = 2,
- Height = barHeight + handleBarHeight,
+ Alpha = 0,
Colour = Color4.White,
Position = new Vector2(2, 0),
Children = new Drawable[]
@@ -84,7 +101,7 @@ namespace osu.Game.Screens.Play
Name = "HandleBar box",
RelativeSizeAxes = Axes.Both,
},
- new Container
+ handleContainer = new Container
{
Name = "Handle container",
Origin = Anchor.BottomCentre,
@@ -116,6 +133,7 @@ namespace osu.Game.Screens.Play
{
base.Update();
+ handleBase.Height = Height - handleContainer.Height;
float newX = (float)Interpolation.Lerp(handleBase.X, NormalizedValue * UsableWidth, Math.Clamp(Time.Elapsed / 40, 0, 1));
fill.Width = newX;
@@ -127,7 +145,11 @@ namespace osu.Game.Screens.Play
protected override void OnUserChange(double value)
{
scheduledSeek?.Cancel();
- scheduledSeek = Schedule(() => OnSeek?.Invoke(value));
+ scheduledSeek = Schedule(() =>
+ {
+ if (showHandle)
+ OnSeek?.Invoke(value);
+ });
}
}
}
diff --git a/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs
index 9cc6ea2628..62213720aa 100644
--- a/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs
+++ b/osu.Game/Screens/Ranking/Pages/ReplayDownloadButton.cs
@@ -74,15 +74,15 @@ namespace osu.Game.Screens.Ranking.Pages
switch (replayAvailability)
{
case ReplayAvailability.Local:
- button.TooltipText = @"Watch replay";
+ button.TooltipText = @"watch replay";
break;
case ReplayAvailability.Online:
- button.TooltipText = @"Download replay";
+ button.TooltipText = @"download replay";
break;
default:
- button.TooltipText = @"Replay unavailable";
+ button.TooltipText = @"replay unavailable";
break;
}
}, true);
diff --git a/osu.Game/Screens/Ranking/Pages/RetryButton.cs b/osu.Game/Screens/Ranking/Pages/RetryButton.cs
index 2a281224c1..06d0440b30 100644
--- a/osu.Game/Screens/Ranking/Pages/RetryButton.cs
+++ b/osu.Game/Screens/Ranking/Pages/RetryButton.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Screens.Ranking.Pages
},
};
- TooltipText = "Retry";
+ TooltipText = "retry";
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Screens/Ranking/ResultModeButton.cs b/osu.Game/Screens/Ranking/ResultModeButton.cs
index 38636b0c3b..d7eb5db125 100644
--- a/osu.Game/Screens/Ranking/ResultModeButton.cs
+++ b/osu.Game/Screens/Ranking/ResultModeButton.cs
@@ -92,6 +92,6 @@ namespace osu.Game.Screens.Ranking
protected override void OnDeactivated() => colouredPart.FadeColour(inactiveColour, 200, Easing.OutQuint);
- public string TooltipText { get; private set; }
+ public string TooltipText { get; }
}
}
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index bf2382c4ae..4433543ca1 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -412,6 +412,12 @@ namespace osu.Game.Screens.Select
protected override bool OnKeyDown(KeyDownEvent e)
{
+ // allow for controlling volume when alt is held.
+ // this is required as the VolumeControlReceptor uses OnPressed, which is
+ // executed after all OnKeyDown events.
+ if (e.AltPressed)
+ return base.OnKeyDown(e);
+
int direction = 0;
bool skipDifficulties = false;
diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
index 451708c1cf..cf49cf0228 100644
--- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs
+++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Screens.Select
{
private const float shear_width = 36.75f;
- private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGED_CONTAINER_SIZE.Y, 0);
+ private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0);
private readonly IBindable ruleset = new Bindable();
@@ -363,7 +363,7 @@ namespace osu.Game.Screens.Select
public class InfoLabel : Container, IHasTooltip
{
- public string TooltipText { get; private set; }
+ public string TooltipText { get; }
public InfoLabel(BeatmapStatistic statistic)
{
diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs
index 68a6ad8845..2ffb73f226 100644
--- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs
+++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs
@@ -44,6 +44,8 @@ namespace osu.Game.Screens.Select.Carousel
match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(Beatmap.Metadata.Artist) ||
criteria.Artist.Matches(Beatmap.Metadata.ArtistUnicode);
+ match &= !criteria.UserStarDifficulty.HasFilter || criteria.UserStarDifficulty.IsInRange(Beatmap.StarDifficulty);
+
if (match)
{
var terms = new List();
diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs
index 5b81303788..c851b201d7 100644
--- a/osu.Game/Screens/Select/FilterControl.cs
+++ b/osu.Game/Screens/Select/FilterControl.cs
@@ -42,9 +42,15 @@ namespace osu.Game.Screens.Select
Group = groupMode.Value,
Sort = sortMode.Value,
AllowConvertedBeatmaps = showConverted.Value,
- Ruleset = ruleset.Value
+ Ruleset = ruleset.Value,
};
+ if (!minimumStars.IsDefault)
+ criteria.UserStarDifficulty.Min = minimumStars.Value;
+
+ if (!maximumStars.IsDefault)
+ criteria.UserStarDifficulty.Max = maximumStars.Value;
+
FilterQueryParser.ApplyQueries(criteria, query);
return criteria;
}
@@ -142,7 +148,9 @@ namespace osu.Game.Screens.Select
private readonly IBindable ruleset = new Bindable();
- private Bindable showConverted;
+ private readonly Bindable showConverted = new Bindable();
+ private readonly Bindable minimumStars = new Bindable();
+ private readonly Bindable maximumStars = new Bindable();
public readonly Box Background;
@@ -151,9 +159,15 @@ namespace osu.Game.Screens.Select
{
sortTabs.AccentColour = colours.GreenLight;
- showConverted = config.GetBindable(OsuSetting.ShowConvertedBeatmaps);
+ config.BindWith(OsuSetting.ShowConvertedBeatmaps, showConverted);
showConverted.ValueChanged += _ => updateCriteria();
+ config.BindWith(OsuSetting.DisplayStarsMinimum, minimumStars);
+ minimumStars.ValueChanged += _ => updateCriteria();
+
+ config.BindWith(OsuSetting.DisplayStarsMaximum, maximumStars);
+ maximumStars.ValueChanged += _ => updateCriteria();
+
ruleset.BindTo(parentRuleset);
ruleset.BindValueChanged(_ => updateCriteria());
diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs
index c4d9996377..9fa57af01d 100644
--- a/osu.Game/Screens/Select/FilterCriteria.cs
+++ b/osu.Game/Screens/Select/FilterCriteria.cs
@@ -26,6 +26,12 @@ namespace osu.Game.Screens.Select
public OptionalTextFilter Creator;
public OptionalTextFilter Artist;
+ public OptionalRange UserStarDifficulty = new OptionalRange
+ {
+ IsLowerInclusive = true,
+ IsUpperInclusive = true
+ };
+
public string[] SearchTerms = Array.Empty();
public RulesetInfo Ruleset;
diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs
index b77da36748..4dcab60548 100644
--- a/osu.Game/Screens/Select/FooterButton.cs
+++ b/osu.Game/Screens/Select/FooterButton.cs
@@ -118,10 +118,10 @@ namespace osu.Game.Screens.Select
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
box.FadeOut(Footer.TRANSITION_LENGTH, Easing.OutQuint);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override bool OnClick(ClickEvent e)
diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs
index 14c9eb2035..9bc5181f6f 100644
--- a/osu.Game/Screens/Select/FooterButtonRandom.cs
+++ b/osu.Game/Screens/Select/FooterButtonRandom.cs
@@ -44,11 +44,11 @@ namespace osu.Game.Screens.Select
return base.OnKeyDown(e);
}
- protected override bool OnKeyUp(KeyUpEvent e)
+ protected override void OnKeyUp(KeyUpEvent e)
{
secondaryActive = e.ShiftPressed;
updateText();
- return base.OnKeyUp(e);
+ base.OnKeyUp(e);
}
private void updateText()
diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs
index ff9beafb23..4e4653cb57 100644
--- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs
+++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs
@@ -60,10 +60,10 @@ namespace osu.Game.Screens.Select.Options
return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(MouseUpEvent e)
+ protected override void OnMouseUp(MouseUpEvent e)
{
flash.FadeTo(0, 1000, Easing.OutQuint);
- return base.OnMouseUp(e);
+ base.OnMouseUp(e);
}
protected override bool OnClick(ClickEvent e)
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 8f7ad2022d..1a29f336ea 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Screens.Select
{
public abstract class SongSelect : OsuScreen, IKeyBindingHandler
{
- public static readonly Vector2 WEDGED_CONTAINER_SIZE = new Vector2(0.5f, 245);
+ public static readonly float WEDGE_HEIGHT = 245;
protected const float BACKGROUND_BLUR = 20;
private const float left_area_padding = 20;
@@ -96,98 +96,116 @@ namespace osu.Game.Screens.Select
AddRangeInternal(new Drawable[]
{
- new ParallaxContainer
- {
- Masking = true,
- ParallaxAmount = 0.005f,
- RelativeSizeAxes = Axes.Both,
- Children = new[]
- {
- new WedgeBackground
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Right = -150 },
- Size = new Vector2(WEDGED_CONTAINER_SIZE.X, 1),
- }
- }
- },
- new Container
- {
- Origin = Anchor.BottomLeft,
- Anchor = Anchor.BottomLeft,
- RelativeSizeAxes = Axes.Both,
- Size = new Vector2(WEDGED_CONTAINER_SIZE.X, 1),
- Padding = new MarginPadding
- {
- Bottom = Footer.HEIGHT,
- Top = WEDGED_CONTAINER_SIZE.Y + left_area_padding,
- Left = left_area_padding,
- Right = left_area_padding * 2,
- },
- Child = BeatmapDetails = new BeatmapDetailArea
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Top = 10, Right = 5 },
- }
- },
- new Container
- {
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Width = 2, //avoid horizontal masking so the panels don't clip when screen stack is pushed.
- Child = new Container
- {
- RelativeSizeAxes = Axes.Both,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Width = 0.5f,
- Children = new Drawable[]
- {
- new Container
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding
- {
- Top = FilterControl.HEIGHT,
- Bottom = Footer.HEIGHT
- },
- Child = Carousel = new BeatmapCarousel
- {
- RelativeSizeAxes = Axes.Both,
- Size = new Vector2(1 - WEDGED_CONTAINER_SIZE.X, 1),
- Anchor = Anchor.CentreRight,
- Origin = Anchor.CentreRight,
- SelectionChanged = updateSelectedBeatmap,
- BeatmapSetsChanged = carouselBeatmapsLoaded,
- },
- },
- FilterControl = new FilterControl
- {
- RelativeSizeAxes = Axes.X,
- Height = FilterControl.HEIGHT,
- FilterChanged = ApplyFilterToCarousel,
- Background = { Width = 2 },
- },
- }
- },
- },
- beatmapInfoWedge = new BeatmapInfoWedge
- {
- Size = WEDGED_CONTAINER_SIZE,
- RelativeSizeAxes = Axes.X,
- Margin = new MarginPadding
- {
- Top = left_area_padding,
- Right = left_area_padding,
- },
- },
new ResetScrollContainer(() => Carousel.ScrollToSelected())
{
RelativeSizeAxes = Axes.Y,
Width = 250,
- }
+ },
+ new VerticalMaskingContainer
+ {
+ Children = new Drawable[]
+ {
+ new GridContainer // used for max width implementation
+ {
+ RelativeSizeAxes = Axes.Both,
+ ColumnDimensions = new[]
+ {
+ new Dimension(),
+ new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 850),
+ },
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ new ParallaxContainer
+ {
+ ParallaxAmount = 0.005f,
+ RelativeSizeAxes = Axes.Both,
+ Child = new WedgeBackground
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Right = -150 },
+ },
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding
+ {
+ Top = FilterControl.HEIGHT,
+ Bottom = Footer.HEIGHT
+ },
+ Child = Carousel = new BeatmapCarousel
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ RelativeSizeAxes = Axes.Both,
+ SelectionChanged = updateSelectedBeatmap,
+ BeatmapSetsChanged = carouselBeatmapsLoaded,
+ },
+ }
+ },
+ }
+ },
+ FilterControl = new FilterControl
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = FilterControl.HEIGHT,
+ FilterChanged = ApplyFilterToCarousel,
+ Background = { Width = 2 },
+ },
+ new GridContainer // used for max width implementation
+ {
+ RelativeSizeAxes = Axes.Both,
+ ColumnDimensions = new[]
+ {
+ new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 650),
+ },
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ new Container
+ {
+ Origin = Anchor.BottomLeft,
+ Anchor = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.Both,
+
+ Children = new Drawable[]
+ {
+ beatmapInfoWedge = new BeatmapInfoWedge
+ {
+ Height = WEDGE_HEIGHT,
+ RelativeSizeAxes = Axes.X,
+ Margin = new MarginPadding
+ {
+ Top = left_area_padding,
+ Right = left_area_padding,
+ },
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding
+ {
+ Bottom = Footer.HEIGHT,
+ Top = WEDGE_HEIGHT + left_area_padding,
+ Left = left_area_padding,
+ Right = left_area_padding * 2,
+ },
+ Child = BeatmapDetails = new BeatmapDetailArea
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Top = 10, Right = 5 },
+ },
+ },
+ }
+ },
+ },
+ }
+ }
+ }
+ },
});
if (ShowFooter)
@@ -681,7 +699,9 @@ namespace osu.Game.Screens.Select
return false;
}
- public bool OnReleased(GlobalAction action) => action == GlobalAction.Select;
+ public void OnReleased(GlobalAction action)
+ {
+ }
protected override bool OnKeyDown(KeyDownEvent e)
{
@@ -703,6 +723,29 @@ namespace osu.Game.Screens.Select
return base.OnKeyDown(e);
}
+ private class VerticalMaskingContainer : Container
+ {
+ private const float panel_overflow = 1.2f;
+
+ protected override Container Content { get; }
+
+ public VerticalMaskingContainer()
+ {
+ RelativeSizeAxes = Axes.Both;
+ Masking = true;
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ Width = panel_overflow; //avoid horizontal masking so the panels don't clip when screen stack is pushed.
+ InternalChild = Content = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Width = 1 / panel_overflow,
+ };
+ }
+ }
+
private class ResetScrollContainer : Container
{
private readonly Action onHoverAction;
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
index 7a84ac009a..94d7395ecf 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Storyboards.Drawables
{
public class DrawableStoryboard : Container
{
- public Storyboard Storyboard { get; private set; }
+ public Storyboard Storyboard { get; }
protected override Container Content { get; }
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs
index ced3b9c1b6..eabb78bac5 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Storyboards.Drawables
{
public class DrawableStoryboardAnimation : TextureAnimation, IFlippable, IVectorScalable
{
- public StoryboardAnimation Animation { get; private set; }
+ public StoryboardAnimation Animation { get; }
private bool flipH;
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
index fd2d441f34..39f5418902 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
@@ -10,7 +10,7 @@ namespace osu.Game.Storyboards.Drawables
{
public class DrawableStoryboardLayer : LifetimeManagementContainer
{
- public StoryboardLayer Layer { get; private set; }
+ public StoryboardLayer Layer { get; }
public bool Enabled;
public override bool IsPresent => Enabled && base.IsPresent;
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs
index c0da0e9c0e..d8d3248659 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Storyboards.Drawables
{
public class DrawableStoryboardSprite : Sprite, IFlippable, IVectorScalable
{
- public StoryboardSprite Sprite { get; private set; }
+ public StoryboardSprite Sprite { get; }
private bool flipH;
diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs
index 22e1929419..f411ad04f3 100644
--- a/osu.Game/Storyboards/StoryboardSprite.cs
+++ b/osu.Game/Storyboards/StoryboardSprite.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Storyboards
private readonly List loops = new List();
private readonly List triggers = new List();
- public string Path { get; set; }
+ public string Path { get; }
public bool IsDrawable => HasCommands;
public Anchor Origin;
diff --git a/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs b/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs
index 3233ee160d..6565f98666 100644
--- a/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs
+++ b/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual
});
}
- protected void AddBlueprint(SelectionBlueprint blueprint)
+ protected void AddBlueprint(OverlaySelectionBlueprint blueprint)
{
Add(blueprint.With(d =>
{
diff --git a/osu.Game/Updater/SimpleUpdateManager.cs b/osu.Game/Updater/SimpleUpdateManager.cs
index f76cba7f41..5412b11b33 100644
--- a/osu.Game/Updater/SimpleUpdateManager.cs
+++ b/osu.Game/Updater/SimpleUpdateManager.cs
@@ -7,8 +7,8 @@ using Newtonsoft.Json;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.IO.Network;
using osu.Framework.Platform;
+using osu.Game.Online.API;
using osu.Game.Overlays.Notifications;
namespace osu.Game.Updater
@@ -36,7 +36,7 @@ namespace osu.Game.Updater
{
try
{
- var releases = new JsonWebRequest("https://api.github.com/repos/ppy/osu/releases/latest");
+ var releases = new OsuJsonWebRequest("https://api.github.com/repos/ppy/osu/releases/latest");
await releases.PerformAsync();
diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs
index ee9af15863..93136e88a0 100644
--- a/osu.Game/Users/Drawables/DrawableAvatar.cs
+++ b/osu.Game/Users/Drawables/DrawableAvatar.cs
@@ -74,7 +74,7 @@ namespace osu.Game.Users.Drawables
private class ClickableArea : OsuClickableContainer
{
- public override string TooltipText => Enabled.Value ? @"View Profile" : null;
+ public override string TooltipText => Enabled.Value ? @"view profile" : null;
protected override bool OnClick(ClickEvent e)
{
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 4fc9e47119..0ea558bbc7 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -23,7 +23,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 760600e6d4..a215bc65e8 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -74,7 +74,7 @@
-
+
@@ -82,7 +82,7 @@
-
+
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index 12571be31d..15ea20084d 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -19,8 +19,8 @@
HINT
DO_NOT_SHOW
HINT
- HINT
- HINT
+ WARNING
+ WARNING
WARNING
WARNING
WARNING
@@ -765,6 +765,7 @@ See the LICENCE file in the repository root for full licence text.
<Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+
True
True
True