diff --git a/Gemfile.lock b/Gemfile.lock
index 17c0db12e7..f7c19064b4 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -6,7 +6,7 @@ GEM
public_suffix (>= 2.0.2, < 4.0)
atomos (0.1.3)
babosa (1.0.2)
- claide (1.0.2)
+ claide (1.0.3)
colored (1.2)
colored2 (3.1.2)
commander-fastlane (4.4.6)
@@ -14,11 +14,11 @@ GEM
declarative (0.0.10)
declarative-option (0.1.0)
digest-crc (0.4.1)
- domain_name (0.5.20180417)
+ domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
- dotenv (2.7.1)
+ dotenv (2.7.5)
emoji_regex (1.0.1)
- excon (0.62.0)
+ excon (0.66.0)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
@@ -27,7 +27,7 @@ GEM
faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.5)
- fastlane (2.117.0)
+ fastlane (2.129.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
@@ -46,8 +46,8 @@ GEM
google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
- mini_magick (~> 4.5.1)
- multi_json
+ jwt (~> 2.1.0)
+ mini_magick (>= 4.9.4, < 5.0.0)
multi_xml (~> 0.5)
multipart-post (~> 2.0.0)
plist (>= 3.1.0, < 4.0.0)
@@ -56,15 +56,15 @@ GEM
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
- terminal-notifier (>= 1.6.2, < 2.0.0)
+ terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
- xcodeproj (>= 1.6.0, < 2.0.0)
+ xcodeproj (>= 1.8.1, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
- fastlane-plugin-clean_testflight_testers (0.2.0)
+ fastlane-plugin-clean_testflight_testers (0.3.0)
fastlane-plugin-souyuz (0.8.1)
souyuz (>= 0.8.1)
fastlane-plugin-xamarin (0.6.3)
@@ -79,7 +79,7 @@ GEM
signet (~> 0.9)
google-cloud-core (1.3.0)
google-cloud-env (~> 1.0)
- google-cloud-env (1.0.5)
+ google-cloud-env (1.2.0)
faraday (~> 0.11)
google-cloud-storage (1.16.0)
digest-crc (~> 0.4)
@@ -102,17 +102,17 @@ GEM
memoist (0.16.0)
mime-types (3.2.2)
mime-types-data (~> 3.2015)
- mime-types-data (3.2018.0812)
- mini_magick (4.5.1)
+ mime-types-data (3.2019.0331)
+ mini_magick (4.9.5)
mini_portile2 (2.4.0)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.6)
naturally (2.2.0)
- nokogiri (1.10.1)
+ nokogiri (1.10.4)
mini_portile2 (~> 2.4.0)
- os (1.0.0)
+ os (1.0.1)
plist (3.5.0)
public_suffix (2.0.5)
representable (3.0.4)
@@ -121,7 +121,7 @@ GEM
uber (< 0.2.0)
retriable (3.1.2)
rouge (2.0.7)
- rubyzip (1.2.2)
+ rubyzip (1.2.3)
security (0.1.3)
signet (0.11.0)
addressable (~> 2.3)
@@ -136,20 +136,20 @@ GEM
fastlane (>= 2.29.0)
highline (~> 1.7)
nokogiri (~> 1.7)
- terminal-notifier (1.8.0)
+ terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
- tty-cursor (0.6.1)
- tty-screen (0.6.5)
- tty-spinner (0.9.0)
- tty-cursor (~> 0.6.0)
+ tty-cursor (0.7.0)
+ tty-screen (0.7.0)
+ tty-spinner (0.9.1)
+ tty-cursor (~> 0.7)
uber (0.1.0)
unf (0.1.4)
unf_ext
- unf_ext (0.0.7.5)
- unicode-display_width (1.4.1)
+ unf_ext (0.0.7.6)
+ unicode-display_width (1.6.0)
word_wrap (1.0.0)
- xcodeproj (1.8.1)
+ xcodeproj (1.12.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
diff --git a/README.md b/README.md
index c4d676f4be..56491a4be4 100644
--- a/README.md
+++ b/README.md
@@ -19,8 +19,9 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh
## Requirements
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
+- When running on linux, please have a system-wide ffmpeg installation available to support video decoding.
+- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
-- Note that there are **[additional requirements for Windows 7 and Windows 8.1](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** which you may need to manually install if your operating system is not up-to-date.
## Running osu!
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 48c16caf0f..0b60e28b0f 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -1,22 +1,6 @@
update_fastlane
-default_platform(:ios)
-
platform :ios do
- lane :testflight_prune_dry do
- clean_testflight_testers(days_of_inactivity:45, dry_run: true)
- end
-
- # Specify a custom number for what's "inactive"
- lane :testflight_prune do
- clean_testflight_testers(days_of_inactivity: 45) # 120 days, so about 4 months
- end
-
- lane :update_version do |options|
- options[:plist_path] = '../osu.iOS/Info.plist'
- app_version(options)
- end
-
desc 'Deploy to testflight'
lane :beta do |options|
update_version(options)
@@ -62,4 +46,17 @@ platform :ios do
match(options)
end
+
+ lane :update_version do |options|
+ options[:plist_path] = '../osu.iOS/Info.plist'
+ app_version(options)
+ end
+
+ lane :testflight_prune_dry do
+ clean_testflight_testers(days_of_inactivity:45, dry_run: true)
+ end
+
+ lane :testflight_prune do
+ clean_testflight_testers(days_of_inactivity: 45)
+ end
end
diff --git a/fastlane/README.md b/fastlane/README.md
index 53bbc62cae..fbccf1c8c0 100644
--- a/fastlane/README.md
+++ b/fastlane/README.md
@@ -16,21 +16,6 @@ or alternatively using `brew cask install fastlane`
# Available Actions
## iOS
-### ios testflight_prune_dry
-```
-fastlane ios testflight_prune_dry
-```
-
-### ios testflight_prune
-```
-fastlane ios testflight_prune
-```
-
-### ios update_version
-```
-fastlane ios update_version
-```
-
### ios beta
```
fastlane ios beta
@@ -46,6 +31,21 @@ Compile the project
fastlane ios provision
```
Install provisioning profiles using match
+### ios update_version
+```
+fastlane ios update_version
+```
+
+### ios testflight_prune_dry
+```
+fastlane ios testflight_prune_dry
+```
+
+### ios testflight_prune
+```
+fastlane ios testflight_prune
+```
+
----
diff --git a/osu.Android.props b/osu.Android.props
index 721d341c08..bb283dc0c5 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -49,9 +49,7 @@
osu.licenseheader
-
- Always
-
+
@@ -63,6 +61,6 @@
-
+
diff --git a/osu.Android/lib/arm64-v8a/libbass.so b/osu.Android/lib/arm64-v8a/libbass.so
deleted file mode 100644
index d5c24b3e4b..0000000000
Binary files a/osu.Android/lib/arm64-v8a/libbass.so and /dev/null differ
diff --git a/osu.Android/lib/arm64-v8a/libbass_fx.so b/osu.Android/lib/arm64-v8a/libbass_fx.so
deleted file mode 100644
index e498b10117..0000000000
Binary files a/osu.Android/lib/arm64-v8a/libbass_fx.so and /dev/null differ
diff --git a/osu.Android/lib/armeabi-v7a/libbass.so b/osu.Android/lib/armeabi-v7a/libbass.so
deleted file mode 100644
index f7d23b9052..0000000000
Binary files a/osu.Android/lib/armeabi-v7a/libbass.so and /dev/null differ
diff --git a/osu.Android/lib/armeabi-v7a/libbass_fx.so b/osu.Android/lib/armeabi-v7a/libbass_fx.so
deleted file mode 100644
index 006e2feb30..0000000000
Binary files a/osu.Android/lib/armeabi-v7a/libbass_fx.so and /dev/null differ
diff --git a/osu.Android/lib/x86/libbass.so b/osu.Android/lib/x86/libbass.so
deleted file mode 100644
index b0f758a42b..0000000000
Binary files a/osu.Android/lib/x86/libbass.so and /dev/null differ
diff --git a/osu.Android/lib/x86/libbass_fx.so b/osu.Android/lib/x86/libbass_fx.so
deleted file mode 100644
index 526dca39ca..0000000000
Binary files a/osu.Android/lib/x86/libbass_fx.so and /dev/null differ
diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs
index 1f1d2cea5f..51e801c185 100644
--- a/osu.Desktop/Overlays/VersionManager.cs
+++ b/osu.Desktop/Overlays/VersionManager.cs
@@ -113,13 +113,14 @@ namespace osu.Desktop.Overlays
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours, ChangelogOverlay changelog)
+ private void load(OsuColour colours, ChangelogOverlay changelog, NotificationOverlay notificationOverlay)
{
Icon = FontAwesome.Solid.CheckSquare;
IconBackgound.Colour = colours.BlueDark;
Activated = delegate
{
+ notificationOverlay.Hide();
changelog.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version);
return true;
};
diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj
index 37e7c45a4e..7990c35e09 100644
--- a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj
@@ -13,14 +13,6 @@
-
- libbass.a
- PreserveNewest
-
-
- libbass_fx.a
- PreserveNewest
-
Linker.xml
diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj
index 24abccb19d..58c2e2aa5a 100644
--- a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj
@@ -13,14 +13,6 @@
-
- libbass.a
- PreserveNewest
-
-
- libbass_fx.a
- PreserveNewest
-
Linker.xml
diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj
index 9930a166e3..c7787bd162 100644
--- a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj
@@ -13,14 +13,6 @@
-
- libbass.a
- PreserveNewest
-
-
- libbass_fx.a
- PreserveNewest
-
Linker.xml
diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj
index d2817b743c..3e46bb89af 100644
--- a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj
@@ -13,14 +13,6 @@
-
- libbass.a
- PreserveNewest
-
-
- libbass_fx.a
- PreserveNewest
-
Linker.xml
diff --git a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj
index ea5ab699f3..5c0713b895 100644
--- a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj
+++ b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj
@@ -13,14 +13,6 @@
-
- libbass.a
- PreserveNewest
-
-
- libbass_fx.a
- PreserveNewest
-
Linker.xml
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs
index d42b61ea55..0c5ead10cf 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHoldForMenuButton.cs
@@ -17,6 +17,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
private bool exitAction;
+ protected override double TimePerAction => 100; // required for the early exit test, since hold-to-confirm delay is 200ms
+
[BackgroundDependencyLoader]
private void load()
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
index ead7a4b7fc..ff8437311e 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
@@ -21,32 +21,38 @@ namespace osu.Game.Tests.Visual.Gameplay
private readonly Container storyboardContainer;
private DrawableStoryboard storyboard;
+ [Cached]
+ private MusicController musicController = new MusicController();
+
public TestSceneStoryboard()
{
Clock = new FramedClock();
- Add(new Container
+ AddRange(new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Children = new Drawable[]
+ musicController,
+ new Container
{
- new Box
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- },
- storyboardContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ storyboardContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
},
},
- });
-
- Add(new MusicController
- {
- Origin = Anchor.TopRight,
- Anchor = Anchor.TopRight,
- State = { Value = Visibility.Visible },
+ new NowPlayingOverlay
+ {
+ Origin = Anchor.TopRight,
+ Anchor = Anchor.TopRight,
+ State = { Value = Visibility.Visible },
+ }
});
AddStep("Restart", restart);
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs
new file mode 100644
index 0000000000..df79584167
--- /dev/null
+++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs
@@ -0,0 +1,15 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Screens;
+using osu.Game.Screens.Menu;
+
+namespace osu.Game.Tests.Visual.Menus
+{
+ [TestFixture]
+ public class TestSceneIntroTriangles : IntroTestScene
+ {
+ protected override IScreen CreateScreen() => new IntroTriangles();
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs
new file mode 100644
index 0000000000..68ad0b42b4
--- /dev/null
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapPanel.cs
@@ -0,0 +1,53 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Game.Beatmaps;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Screens.Multi.Match.Components;
+using osu.Framework.Graphics;
+using osu.Framework.MathUtils;
+using osu.Game.Audio;
+using osu.Framework.Allocation;
+
+namespace osu.Game.Tests.Visual.Multiplayer
+{
+ [Cached(typeof(IPreviewTrackOwner))]
+ public class TestSceneMatchBeatmapPanel : MultiplayerTestScene, IPreviewTrackOwner
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(MatchBeatmapPanel)
+ };
+
+ [Resolved]
+ private PreviewTrackManager previewTrackManager { get; set; }
+
+ public TestSceneMatchBeatmapPanel()
+ {
+ Add(new MatchBeatmapPanel
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ });
+
+ Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1763072 } });
+ Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2101557 } });
+ Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1973466 } });
+ Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2109801 } });
+ Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1922035 } });
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ AddStep("Select random beatmap", () =>
+ {
+ Room.CurrentItem.Value = Room.Playlist[RNG.Next(Room.Playlist.Count)];
+ previewTrackManager.StopAnyPlaying(this);
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs
index 3f0c0b07b7..a6c036a876 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchInfo.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
typeof(Info),
typeof(HeaderButton),
typeof(ReadyButton),
- typeof(ViewBeatmapButton)
+ typeof(MatchBeatmapPanel)
};
[BackgroundDependencyLoader]
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs
new file mode 100644
index 0000000000..d09a50b12c
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs
@@ -0,0 +1,69 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Overlays.Profile.Header.Components;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ [TestFixture]
+ public class TestSceneUserProfilePreviousUsernames : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(PreviousUsernames)
+ };
+
+ [Resolved]
+ private IAPIProvider api { get; set; }
+
+ private readonly Bindable user = new Bindable();
+
+ public TestSceneUserProfilePreviousUsernames()
+ {
+ Child = new PreviousUsernames
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ User = { BindTarget = user },
+ };
+
+ User[] users =
+ {
+ new User { PreviousUsernames = new[] { "username1" } },
+ new User { PreviousUsernames = new[] { "longusername", "longerusername" } },
+ new User { PreviousUsernames = new[] { "test", "angelsim", "verylongusername" } },
+ new User { PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext", "anylonger" } },
+ new User { PreviousUsernames = new string[0] },
+ null
+ };
+
+ AddStep("single username", () => user.Value = users[0]);
+ AddStep("two usernames", () => user.Value = users[1]);
+ AddStep("three usernames", () => user.Value = users[2]);
+ AddStep("four usernames", () => user.Value = users[3]);
+ AddStep("no username", () => user.Value = users[4]);
+ AddStep("null user", () => user.Value = users[5]);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ AddStep("online user (Angelsim)", () =>
+ {
+ var request = new GetUserRequest(1777162);
+ request.Success += user => this.user.Value = user;
+ api.Queue(request);
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs
index 28f0cc027e..94228e22f0 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs
@@ -3,6 +3,7 @@
using System;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -22,30 +23,36 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneBeatSyncedContainer : OsuTestScene
{
- private readonly MusicController mc;
+ private readonly NowPlayingOverlay np;
+
+ [Cached]
+ private MusicController musicController = new MusicController();
public TestSceneBeatSyncedContainer()
{
Clock = new FramedClock();
Clock.ProcessFrame();
- Add(new BeatContainer
+ AddRange(new Drawable[]
{
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- });
-
- Add(mc = new MusicController
- {
- Origin = Anchor.TopRight,
- Anchor = Anchor.TopRight,
+ musicController,
+ new BeatContainer
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ },
+ np = new NowPlayingOverlay
+ {
+ Origin = Anchor.TopRight,
+ Anchor = Anchor.TopRight,
+ }
});
}
protected override void LoadComplete()
{
base.LoadComplete();
- mc.ToggleVisibility();
+ np.ToggleVisibility();
}
private class BeatContainer : BeatSyncedContainer
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs
index 7e6cf1285e..f787754aa4 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Menu;
@@ -12,7 +13,13 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneHoldToConfirmOverlay : OsuTestScene
{
- public override IReadOnlyList RequiredTypes => new[] { typeof(ExitConfirmOverlay) };
+ protected override double TimePerAction => 100; // required for the early exit test, since hold-to-confirm delay is 200ms
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ExitConfirmOverlay),
+ typeof(HoldToConfirmContainer),
+ };
public TestSceneHoldToConfirmOverlay()
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs
similarity index 58%
rename from osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs
rename to osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs
index ab2ca47100..e3daa9c279 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Overlays;
@@ -9,22 +10,27 @@ using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
- public class TestSceneMusicController : OsuTestScene
+ public class TestSceneNowPlayingOverlay : OsuTestScene
{
- public TestSceneMusicController()
+ [Cached]
+ private MusicController musicController = new MusicController();
+
+ public TestSceneNowPlayingOverlay()
{
Clock = new FramedClock();
- var mc = new MusicController
+ var np = new NowPlayingOverlay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre
};
- Add(mc);
- AddStep(@"show", () => mc.Show());
+ Add(musicController);
+ Add(np);
+
+ AddStep(@"show", () => np.Show());
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
- AddStep(@"show", () => mc.Hide());
+ AddStep(@"show", () => np.Hide());
}
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs
index d900526c07..45720548c8 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs
@@ -7,6 +7,7 @@ using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Graphics;
using osu.Game.Overlays;
+using osu.Game.Overlays.OSD;
namespace osu.Game.Tests.Visual.UserInterface
{
@@ -22,6 +23,12 @@ namespace osu.Game.Tests.Visual.UserInterface
osd.BeginTracking(this, config);
Add(osd);
+ AddStep("Display empty osd toast", () => osd.Display(new EmptyToast()));
+ AddAssert("Toast width is 240", () => osd.Child.Width == 240);
+
+ AddStep("Display toast with lengthy text", () => osd.Display(new LengthyToast()));
+ AddAssert("Toast width is greater than 240", () => osd.Child.Width > 240);
+
AddRepeatStep("Change toggle (no bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingNoKeybind), 2);
AddRepeatStep("Change toggle (with bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingWithKeybind), 2);
AddRepeatStep("Change enum (no bind)", () => config.IncrementEnumSetting(TestConfigSetting.EnumSettingNoKeybind), 3);
@@ -86,6 +93,22 @@ namespace osu.Game.Tests.Visual.UserInterface
Setting4
}
+ private class EmptyToast : Toast
+ {
+ public EmptyToast()
+ : base("", "", "")
+ {
+ }
+ }
+
+ private class LengthyToast : Toast
+ {
+ public LengthyToast()
+ : base("Toast with a very very very long text", "A very very very very very very long text also", "A very very very very very long shortcut")
+ {
+ }
+ }
+
private class TestOnScreenDisplay : OnScreenDisplay
{
protected override void DisplayTemporarily(Drawable toDisplay) => toDisplay.FadeIn().ResizeHeightTo(110);
diff --git a/osu.Game/Configuration/IntroSequence.cs b/osu.Game/Configuration/IntroSequence.cs
new file mode 100644
index 0000000000..1eb953be36
--- /dev/null
+++ b/osu.Game/Configuration/IntroSequence.cs
@@ -0,0 +1,11 @@
+// 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.Configuration
+{
+ public enum IntroSequence
+ {
+ Circles,
+ Triangles
+ }
+}
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 1da7c7ec1d..19f46c1d6a 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -105,6 +105,8 @@ namespace osu.Game.Configuration
Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f);
Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
+
+ Set(OsuSetting.IntroSequence, IntroSequence.Triangles);
}
public OsuConfigManager(Storage storage)
@@ -167,6 +169,7 @@ namespace osu.Game.Configuration
ScalingPositionY,
ScalingSizeX,
ScalingSizeY,
- UIScale
+ UIScale,
+ IntroSequence
}
}
diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs
index cda5e150de..773265d19b 100644
--- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs
+++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs
@@ -12,9 +12,11 @@ namespace osu.Game.Graphics.Containers
{
public Action Action;
- private const int activate_delay = 400;
+ private const int default_activation_delay = 200;
private const int fadeout_delay = 200;
+ private readonly double activationDelay;
+
private bool fired;
private bool confirming;
@@ -25,13 +27,22 @@ namespace osu.Game.Graphics.Containers
public Bindable Progress = new BindableDouble();
+ ///
+ /// Create a new instance.
+ ///
+ /// The time requried before an action is confirmed.
+ protected HoldToConfirmContainer(double activationDelay = default_activation_delay)
+ {
+ this.activationDelay = activationDelay;
+ }
+
protected void BeginConfirm()
{
if (confirming || (!AllowMultipleFires && fired)) return;
confirming = true;
- this.TransformBindableTo(Progress, 1, activate_delay * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm());
+ this.TransformBindableTo(Progress, 1, activationDelay * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm());
}
protected virtual void Confirm()
diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
index 47324ee646..6593531099 100644
--- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
@@ -72,17 +72,11 @@ namespace osu.Game.Graphics.UserInterface
Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1;
}
- protected override void LoadComplete()
+ [BackgroundDependencyLoader]
+ private void load(AudioManager audio)
{
- base.LoadComplete();
-
- Current.ValueChanged += enabled =>
- {
- if (enabled.NewValue)
- sampleChecked?.Play();
- else
- sampleUnchecked?.Play();
- };
+ sampleChecked = audio.Samples.Get(@"UI/check-on");
+ sampleUnchecked = audio.Samples.Get(@"UI/check-off");
}
protected override bool OnHover(HoverEvent e)
@@ -99,11 +93,13 @@ namespace osu.Game.Graphics.UserInterface
base.OnHoverLost(e);
}
- [BackgroundDependencyLoader]
- private void load(AudioManager audio)
+ protected override void OnUserChange(bool value)
{
- sampleChecked = audio.Samples.Get(@"UI/check-on");
- sampleUnchecked = audio.Samples.Get(@"UI/check-off");
+ base.OnUserChange(value);
+ if (value)
+ sampleChecked?.Play();
+ else
+ sampleUnchecked?.Play();
}
}
}
diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs
index 669fd62e45..b70072a222 100644
--- a/osu.Game/Input/Bindings/GlobalActionContainer.cs
+++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Input.Bindings
handler = game;
}
- public override IEnumerable DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings);
+ public override IEnumerable DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings).Concat(AudioControlKeyBindings);
public IEnumerable GlobalKeyBindings => new[]
{
@@ -32,11 +32,6 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
- new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume),
- new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume),
- new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume),
- new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume),
- new KeyBinding(InputKey.F4, GlobalAction.ToggleMute),
new KeyBinding(InputKey.Escape, GlobalAction.Back),
new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back),
@@ -55,6 +50,22 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed),
};
+ public IEnumerable AudioControlKeyBindings => new[]
+ {
+ new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume),
+ new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume),
+ new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume),
+ new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume),
+ new KeyBinding(InputKey.F4, GlobalAction.ToggleMute),
+
+ new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev),
+ new KeyBinding(InputKey.F1, GlobalAction.MusicPrev),
+ new KeyBinding(InputKey.TrackNext, GlobalAction.MusicNext),
+ new KeyBinding(InputKey.F5, GlobalAction.MusicNext),
+ new KeyBinding(InputKey.PlayPause, GlobalAction.MusicPlay),
+ new KeyBinding(InputKey.F3, GlobalAction.MusicPlay)
+ };
+
protected override IEnumerable KeyBindingInputQueue =>
handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler);
}
@@ -115,5 +126,15 @@ namespace osu.Game.Input.Bindings
[Description("Quick exit (Hold)")]
QuickExit,
+
+ // Game-wide beatmap msi ccotolle keybindings
+ [Description("Next track")]
+ MusicNext,
+
+ [Description("Previous track")]
+ MusicPrev,
+
+ [Description("Play / pause")]
+ MusicPlay,
}
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index b9e2b79b05..0e804ecbaf 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -299,7 +299,7 @@ namespace osu.Game
}, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);
}
- #region Beatmap jukebox progression
+ #region Beatmap progression
private void beatmapChanged(ValueChangedEvent beatmap)
{
@@ -469,6 +469,8 @@ namespace osu.Game
loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add);
loadComponentSingleFile(new OnScreenDisplay(), Add, true);
+ loadComponentSingleFile(musicController = new MusicController(), Add, true);
+
loadComponentSingleFile(notifications = new NotificationOverlay
{
GetToolbarHeight = () => ToolbarOffset,
@@ -495,7 +497,7 @@ namespace osu.Game
Origin = Anchor.TopRight,
}, rightFloatingOverlayContent.Add, true);
- loadComponentSingleFile(musicController = new MusicController
+ loadComponentSingleFile(new NowPlayingOverlay
{
GetToolbarHeight = () => ToolbarOffset,
Anchor = Anchor.TopRight,
diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs
index 8d56e250fc..f831266b1b 100644
--- a/osu.Game/Overlays/Chat/DrawableChannel.cs
+++ b/osu.Game/Overlays/Chat/DrawableChannel.cs
@@ -30,27 +30,24 @@ namespace osu.Game.Overlays.Chat
[BackgroundDependencyLoader]
private void load()
{
- Children = new Drawable[]
+ Child = new OsuContextMenuContainer
{
- scroll = new OsuScrollContainer
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Child = scroll = new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
// Some chat lines have effects that slightly protrude to the bottom,
// which we do not want to mask away, hence the padding.
Padding = new MarginPadding { Bottom = 5 },
- Child = new OsuContextMenuContainer
+ Child = ChatLineFlow = new ChatLineContainer
{
+ Padding = new MarginPadding { Left = 20, Right = 20 },
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
- Child = ChatLineFlow = new ChatLineContainer
- {
- Padding = new MarginPadding { Left = 20, Right = 20 },
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- }
- },
- }
+ Direction = FillDirection.Vertical,
+ }
+ },
};
newMessagesArrived(Channel.Messages);
diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
index 7e33d7ba27..56e93b6a1e 100644
--- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
+++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
@@ -15,6 +15,7 @@ namespace osu.Game.Overlays.KeyBinding
public GlobalKeyBindingsSection(GlobalActionContainer manager)
{
Add(new DefaultBindingsSubsection(manager));
+ Add(new AudioControlKeyBindingsSubsection(manager));
Add(new InGameKeyBindingsSubsection(manager));
}
@@ -39,5 +40,16 @@ namespace osu.Game.Overlays.KeyBinding
Defaults = manager.InGameKeyBindings;
}
}
+
+ private class AudioControlKeyBindingsSubsection : KeyBindingsSubsection
+ {
+ protected override string Header => "Audio";
+
+ public AudioControlKeyBindingsSubsection(GlobalActionContainer manager)
+ : base(null)
+ {
+ Defaults = manager.AudioControlKeyBindings;
+ }
+ }
}
}
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index abbcec5094..f6208c46cb 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -1,292 +1,112 @@
-// 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;
using System.Collections.Generic;
using System.Linq;
-using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
-using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Effects;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
-using osu.Framework.Input.Events;
-using osu.Framework.Localisation;
+using osu.Framework.Input.Bindings;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
-using osu.Game.Graphics;
-using osu.Game.Graphics.Containers;
-using osu.Game.Graphics.Sprites;
-using osu.Game.Graphics.UserInterface;
-using osu.Game.Overlays.Music;
+using osu.Game.Input.Bindings;
+using osu.Game.Overlays.OSD;
using osu.Game.Rulesets.Mods;
-using osuTK;
-using osuTK.Graphics;
namespace osu.Game.Overlays
{
- public class MusicController : OsuFocusedOverlayContainer
+ ///
+ /// Handles playback of the global music track.
+ ///
+ public class MusicController : Component, IKeyBindingHandler
{
- private const float player_height = 130;
- private const float transition_length = 800;
- private const float progress_height = 10;
- private const float bottom_black_area_height = 55;
-
- private Drawable background;
- private ProgressBar progressBar;
-
- private IconButton prevButton;
- private IconButton playButton;
- private IconButton nextButton;
- private IconButton playlistButton;
-
- private SpriteText title, artist;
-
- private PlaylistOverlay playlist;
-
- private BeatmapManager beatmaps;
+ [Resolved]
+ private BeatmapManager beatmaps { get; set; }
private List beatmapSets;
- private Container dragContainer;
- private Container playerContainer;
-
public bool IsUserPaused { get; private set; }
+ ///
+ /// Fired when the global has changed.
+ /// Includes direction information for display purposes.
+ ///
+ public event Action TrackChanged;
+
[Resolved]
- private Bindable beatmap { get; set; }
+ private IBindable beatmap { get; set; }
[Resolved]
private IBindable> mods { get; set; }
- ///
- /// Provide a source for the toolbar height.
- ///
- public Func GetToolbarHeight;
-
- public MusicController()
- {
- Width = 400;
- Margin = new MarginPadding(10);
- }
+ [Resolved(canBeNull: true)]
+ private OnScreenDisplay onScreenDisplay { get; set; }
[BackgroundDependencyLoader]
- private void load(Bindable beatmap, BeatmapManager beatmaps, OsuColour colours)
+ private void load()
{
- this.beatmaps = beatmaps;
-
- Children = new Drawable[]
- {
- dragContainer = new DragContainer
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Children = new Drawable[]
- {
- playlist = new PlaylistOverlay
- {
- RelativeSizeAxes = Axes.X,
- Y = player_height + 10,
- OrderChanged = playlistOrderChanged
- },
- playerContainer = new Container
- {
- RelativeSizeAxes = Axes.X,
- Height = player_height,
- Masking = true,
- CornerRadius = 5,
- EdgeEffect = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Shadow,
- Colour = Color4.Black.Opacity(40),
- Radius = 5,
- },
- Children = new[]
- {
- background = new Background(),
- title = new OsuSpriteText
- {
- Origin = Anchor.BottomCentre,
- Anchor = Anchor.TopCentre,
- Position = new Vector2(0, 40),
- Font = OsuFont.GetFont(size: 25, italics: true),
- Colour = Color4.White,
- Text = @"Nothing to play",
- },
- artist = new OsuSpriteText
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Position = new Vector2(0, 45),
- Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold, italics: true),
- Colour = Color4.White,
- Text = @"Nothing to play",
- },
- new Container
- {
- Padding = new MarginPadding { Bottom = progress_height },
- Height = bottom_black_area_height,
- RelativeSizeAxes = Axes.X,
- Origin = Anchor.BottomCentre,
- Anchor = Anchor.BottomCentre,
- Children = new Drawable[]
- {
- new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Spacing = new Vector2(5),
- Origin = Anchor.Centre,
- Anchor = Anchor.Centre,
- Children = new[]
- {
- prevButton = new MusicIconButton
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Action = prev,
- Icon = FontAwesome.Solid.StepBackward,
- },
- playButton = new MusicIconButton
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Scale = new Vector2(1.4f),
- IconScale = new Vector2(1.4f),
- Action = togglePause,
- Icon = FontAwesome.Regular.PlayCircle,
- },
- nextButton = new MusicIconButton
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Action = () => next(),
- Icon = FontAwesome.Solid.StepForward,
- },
- }
- },
- playlistButton = new MusicIconButton
- {
- Origin = Anchor.Centre,
- Anchor = Anchor.CentreRight,
- Position = new Vector2(-bottom_black_area_height / 2, 0),
- Icon = FontAwesome.Solid.Bars,
- Action = () => playlist.ToggleVisibility(),
- },
- }
- },
- progressBar = new ProgressBar
- {
- Origin = Anchor.BottomCentre,
- Anchor = Anchor.BottomCentre,
- Height = progress_height,
- FillColour = colours.Yellow,
- OnSeek = attemptSeek
- }
- },
- },
- }
- }
- };
-
beatmapSets = beatmaps.GetAllUsableBeatmapSets();
beatmaps.ItemAdded += handleBeatmapAdded;
beatmaps.ItemRemoved += handleBeatmapRemoved;
-
- playlist.State.ValueChanged += s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
}
- private ScheduledDelegate seekDelegate;
-
- private void attemptSeek(double progress)
+ protected override void LoadComplete()
{
- seekDelegate?.Cancel();
- seekDelegate = Schedule(() =>
- {
- if (!beatmap.Disabled)
- current?.Track.Seek(progress);
- });
+ beatmap.BindValueChanged(beatmapChanged, true);
+ mods.BindValueChanged(_ => updateAudioAdjustments(), true);
+ base.LoadComplete();
}
- private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index)
+ ///
+ /// Change the position of a in the current playlist.
+ ///
+ /// The beatmap to move.
+ /// The new position.
+ public void ChangeBeatmapSetPosition(BeatmapSetInfo beatmapSetInfo, int index)
{
beatmapSets.Remove(beatmapSetInfo);
beatmapSets.Insert(index, beatmapSetInfo);
}
- private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() => beatmapSets.Add(set));
+ ///
+ /// Returns whether the current beatmap track is playing.
+ ///
+ public bool IsPlaying => beatmap.Value.Track.IsRunning;
- private void handleBeatmapRemoved(BeatmapSetInfo set) => Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID));
+ private void handleBeatmapAdded(BeatmapSetInfo set) =>
+ Schedule(() => beatmapSets.Add(set));
- protected override void LoadComplete()
+ private void handleBeatmapRemoved(BeatmapSetInfo set) =>
+ Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID));
+
+ private ScheduledDelegate seekDelegate;
+
+ public void SeekTo(double position)
{
- beatmap.BindValueChanged(beatmapChanged, true);
- beatmap.BindDisabledChanged(beatmapDisabledChanged, true);
- mods.BindValueChanged(_ => updateAudioAdjustments(), true);
- base.LoadComplete();
- }
-
- private void beatmapDisabledChanged(bool disabled)
- {
- if (disabled)
- playlist.Hide();
-
- playButton.Enabled.Value = !disabled;
- prevButton.Enabled.Value = !disabled;
- nextButton.Enabled.Value = !disabled;
- playlistButton.Enabled.Value = !disabled;
- }
-
- protected override void UpdateAfterChildren()
- {
- base.UpdateAfterChildren();
- Height = dragContainer.Height;
-
- dragContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
- }
-
- protected override void Update()
- {
- base.Update();
-
- if (pendingBeatmapSwitch != null)
+ seekDelegate?.Cancel();
+ seekDelegate = Schedule(() =>
{
- pendingBeatmapSwitch();
- pendingBeatmapSwitch = null;
- }
-
- var track = current?.TrackLoaded ?? false ? current.Track : null;
-
- if (track?.IsDummyDevice == false)
- {
- progressBar.EndTime = track.Length;
- progressBar.CurrentTime = track.CurrentTime;
-
- playButton.Icon = track.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
- }
- else
- {
- progressBar.CurrentTime = 0;
- progressBar.EndTime = 1;
- playButton.Icon = FontAwesome.Regular.PlayCircle;
- }
+ if (!beatmap.Disabled)
+ current?.Track.Seek(position);
+ });
}
- private void togglePause()
+ ///
+ /// Toggle pause / play.
+ ///
+ /// Whether the operation was successful.
+ public bool TogglePause()
{
var track = current?.Track;
if (track == null)
{
- if (!beatmap.Disabled)
- next(true);
- return;
+ if (beatmap.Disabled)
+ return false;
+
+ next(true);
+ return true;
}
if (track.IsRunning)
@@ -299,48 +119,70 @@ namespace osu.Game.Overlays
track.Start();
IsUserPaused = false;
}
+
+ return true;
}
- private void prev()
+ ///
+ /// Play the previous track.
+ ///
+ /// Whether the operation was successful.
+ public bool PrevTrack()
{
- queuedDirection = TransformDirection.Prev;
+ queuedDirection = TrackChangeDirection.Prev;
var playable = beatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? beatmapSets.LastOrDefault();
if (playable != null)
{
- beatmap.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value);
+ if (beatmap is Bindable working)
+ working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value);
beatmap.Value.Track.Restart();
+
+ return true;
}
+
+ return false;
}
- private void next(bool instant = false)
+ ///
+ /// Play the next random or playlist track.
+ ///
+ /// Whether the operation was successful.
+ public bool NextTrack() => next();
+
+ private bool next(bool instant = false)
{
if (!instant)
- queuedDirection = TransformDirection.Next;
+ queuedDirection = TrackChangeDirection.Next;
var playable = beatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? beatmapSets.FirstOrDefault();
if (playable != null)
{
- beatmap.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value);
+ if (beatmap is Bindable working)
+ working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value);
beatmap.Value.Track.Restart();
+ return true;
}
+
+ return false;
}
private WorkingBeatmap current;
- private TransformDirection? queuedDirection;
+
+ private TrackChangeDirection? queuedDirection;
private void beatmapChanged(ValueChangedEvent beatmap)
{
- TransformDirection direction = TransformDirection.None;
+ TrackChangeDirection direction = TrackChangeDirection.None;
if (current != null)
{
bool audioEquals = beatmap.NewValue?.BeatmapInfo?.AudioEquals(current.BeatmapInfo) ?? false;
if (audioEquals)
- direction = TransformDirection.None;
+ direction = TrackChangeDirection.None;
else if (queuedDirection.HasValue)
{
direction = queuedDirection.Value;
@@ -352,13 +194,13 @@ namespace osu.Game.Overlays
var last = beatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
var next = beatmap.NewValue == null ? -1 : beatmapSets.TakeWhile(b => b.ID != beatmap.NewValue.BeatmapSetInfo?.ID).Count();
- direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
+ direction = last > next ? TrackChangeDirection.Prev : TrackChangeDirection.Next;
}
}
- progressBar.CurrentTime = 0;
+ current = beatmap.NewValue;
+ TrackChanged?.Invoke(current, direction);
- updateDisplay(current = beatmap.NewValue, direction);
updateAudioAdjustments();
queuedDirection = null;
@@ -376,167 +218,60 @@ namespace osu.Game.Overlays
mod.ApplyToClock(track);
}
- private Action pendingBeatmapSwitch;
-
- private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction)
+ protected override void Dispose(bool isDisposing)
{
- // avoid using scheduler as our scheduler may not be run for a long time, holding references to beatmaps.
- pendingBeatmapSwitch = delegate
+ base.Dispose(isDisposing);
+
+ if (beatmaps != null)
{
- // todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
- Task.Run(() =>
- {
- if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists
- {
- title.Text = @"Nothing to play";
- artist.Text = @"Nothing to play";
- }
- else
- {
- BeatmapMetadata metadata = beatmap.Metadata;
- title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title));
- artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist));
- }
- });
-
- LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground =>
- {
- switch (direction)
- {
- case TransformDirection.Next:
- newBackground.Position = new Vector2(400, 0);
- newBackground.MoveToX(0, 500, Easing.OutCubic);
- background.MoveToX(-400, 500, Easing.OutCubic);
- break;
-
- case TransformDirection.Prev:
- newBackground.Position = new Vector2(-400, 0);
- newBackground.MoveToX(0, 500, Easing.OutCubic);
- background.MoveToX(400, 500, Easing.OutCubic);
- break;
- }
-
- background.Expire();
- background = newBackground;
-
- playerContainer.Add(newBackground);
- });
- };
- }
-
- protected override void PopIn()
- {
- base.PopIn();
-
- this.FadeIn(transition_length, Easing.OutQuint);
- dragContainer.ScaleTo(1, transition_length, Easing.OutElastic);
- }
-
- protected override void PopOut()
- {
- base.PopOut();
-
- this.FadeOut(transition_length, Easing.OutQuint);
- dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint);
- }
-
- private enum TransformDirection
- {
- None,
- Next,
- Prev
- }
-
- private class MusicIconButton : IconButton
- {
- public MusicIconButton()
- {
- AutoSizeAxes = Axes.Both;
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- HoverColour = colours.YellowDark.Opacity(0.6f);
- FlashColour = colours.Yellow;
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- // works with AutoSizeAxes above to make buttons autosize with the scale animation.
- Content.AutoSizeAxes = Axes.None;
- Content.Size = new Vector2(DEFAULT_BUTTON_SIZE);
+ beatmaps.ItemAdded -= handleBeatmapAdded;
+ beatmaps.ItemRemoved -= handleBeatmapRemoved;
}
}
- private class Background : BufferedContainer
+ public bool OnPressed(GlobalAction action)
{
- private readonly Sprite sprite;
- private readonly WorkingBeatmap beatmap;
+ if (beatmap.Disabled)
+ return false;
- public Background(WorkingBeatmap beatmap = null)
+ switch (action)
{
- this.beatmap = beatmap;
- CacheDrawnFrameBuffer = true;
- Depth = float.MaxValue;
- RelativeSizeAxes = Axes.Both;
+ case GlobalAction.MusicPlay:
+ if (TogglePause())
+ onScreenDisplay?.Display(new MusicControllerToast(IsPlaying ? "Play track" : "Pause track"));
+ return true;
- Children = new Drawable[]
- {
- sprite = new Sprite
- {
- RelativeSizeAxes = Axes.Both,
- Colour = OsuColour.Gray(150),
- FillMode = FillMode.Fill,
- },
- new Box
- {
- RelativeSizeAxes = Axes.X,
- Height = bottom_black_area_height,
- Origin = Anchor.BottomCentre,
- Anchor = Anchor.BottomCentre,
- Colour = Color4.Black.Opacity(0.5f)
- }
- };
+ case GlobalAction.MusicNext:
+ if (NextTrack())
+ onScreenDisplay?.Display(new MusicControllerToast("Next track"));
+
+ return true;
+
+ case GlobalAction.MusicPrev:
+ if (PrevTrack())
+ onScreenDisplay?.Display(new MusicControllerToast("Previous track"));
+
+ return true;
}
- [BackgroundDependencyLoader]
- private void load(TextureStore textures)
- {
- sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
- }
+ return false;
}
- private class DragContainer : Container
+ public bool OnReleased(GlobalAction action) => false;
+
+ public class MusicControllerToast : Toast
{
- protected override bool OnDragStart(DragStartEvent e)
+ public MusicControllerToast(string action)
+ : base("Music Playback", action, string.Empty)
{
- return true;
- }
-
- protected override bool OnDrag(DragEvent e)
- {
- Vector2 change = e.MousePosition - e.MouseDownPosition;
-
- // Diminish the drag distance as we go further to simulate "rubber band" feeling.
- change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
-
- this.MoveTo(change);
- return true;
- }
-
- protected override bool OnDragEnd(DragEndEvent e)
- {
- this.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
- return base.OnDragEnd(e);
}
}
+ }
- ///
- /// Play the next random or playlist track.
- ///
- public void NextTrack() => next();
+ public enum TrackChangeDirection
+ {
+ None,
+ Next,
+ Prev
}
}
diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs
new file mode 100644
index 0000000000..a3243a655e
--- /dev/null
+++ b/osu.Game/Overlays/NowPlayingOverlay.cs
@@ -0,0 +1,404 @@
+// 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.Threading.Tasks;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Input.Events;
+using osu.Framework.Localisation;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.Music;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays
+{
+ public class NowPlayingOverlay : OsuFocusedOverlayContainer
+ {
+ private const float player_height = 130;
+ private const float transition_length = 800;
+ private const float progress_height = 10;
+ private const float bottom_black_area_height = 55;
+
+ private Drawable background;
+ private ProgressBar progressBar;
+
+ private IconButton prevButton;
+ private IconButton playButton;
+ private IconButton nextButton;
+ private IconButton playlistButton;
+
+ private SpriteText title, artist;
+
+ private PlaylistOverlay playlist;
+
+ private Container dragContainer;
+ private Container playerContainer;
+
+ ///
+ /// Provide a source for the toolbar height.
+ ///
+ public Func GetToolbarHeight;
+
+ [Resolved]
+ private MusicController musicController { get; set; }
+
+ [Resolved]
+ private Bindable beatmap { get; set; }
+
+ public NowPlayingOverlay()
+ {
+ Width = 400;
+ Margin = new MarginPadding(10);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Children = new Drawable[]
+ {
+ dragContainer = new DragContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ playlist = new PlaylistOverlay
+ {
+ RelativeSizeAxes = Axes.X,
+ Y = player_height + 10,
+ OrderChanged = musicController.ChangeBeatmapSetPosition
+ },
+ playerContainer = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = player_height,
+ Masking = true,
+ CornerRadius = 5,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Shadow,
+ Colour = Color4.Black.Opacity(40),
+ Radius = 5,
+ },
+ Children = new[]
+ {
+ background = new Background(),
+ title = new OsuSpriteText
+ {
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.TopCentre,
+ Position = new Vector2(0, 40),
+ Font = OsuFont.GetFont(size: 25, italics: true),
+ Colour = Color4.White,
+ Text = @"Nothing to play",
+ },
+ artist = new OsuSpriteText
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Position = new Vector2(0, 45),
+ Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold, italics: true),
+ Colour = Color4.White,
+ Text = @"Nothing to play",
+ },
+ new Container
+ {
+ Padding = new MarginPadding { Bottom = progress_height },
+ Height = bottom_black_area_height,
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(5),
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Children = new[]
+ {
+ prevButton = new MusicIconButton
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Action = () => musicController.PrevTrack(),
+ Icon = FontAwesome.Solid.StepBackward,
+ },
+ playButton = new MusicIconButton
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Scale = new Vector2(1.4f),
+ IconScale = new Vector2(1.4f),
+ Action = () => musicController.TogglePause(),
+ Icon = FontAwesome.Regular.PlayCircle,
+ },
+ nextButton = new MusicIconButton
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Action = () => musicController.NextTrack(),
+ Icon = FontAwesome.Solid.StepForward,
+ },
+ }
+ },
+ playlistButton = new MusicIconButton
+ {
+ Origin = Anchor.Centre,
+ Anchor = Anchor.CentreRight,
+ Position = new Vector2(-bottom_black_area_height / 2, 0),
+ Icon = FontAwesome.Solid.Bars,
+ Action = () => playlist.ToggleVisibility(),
+ },
+ }
+ },
+ progressBar = new ProgressBar
+ {
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ Height = progress_height,
+ FillColour = colours.Yellow,
+ OnSeek = musicController.SeekTo
+ }
+ },
+ },
+ }
+ }
+ };
+
+ playlist.State.ValueChanged += s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ beatmap.BindDisabledChanged(beatmapDisabledChanged, true);
+
+ musicController.TrackChanged += trackChanged;
+ trackChanged(beatmap.Value);
+ }
+
+ protected override void PopIn()
+ {
+ base.PopIn();
+
+ this.FadeIn(transition_length, Easing.OutQuint);
+ dragContainer.ScaleTo(1, transition_length, Easing.OutElastic);
+ }
+
+ protected override void PopOut()
+ {
+ base.PopOut();
+
+ this.FadeOut(transition_length, Easing.OutQuint);
+ dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint);
+ }
+
+ protected override void UpdateAfterChildren()
+ {
+ base.UpdateAfterChildren();
+
+ Height = dragContainer.Height;
+ dragContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (pendingBeatmapSwitch != null)
+ {
+ pendingBeatmapSwitch();
+ pendingBeatmapSwitch = null;
+ }
+
+ var track = beatmap.Value?.TrackLoaded ?? false ? beatmap.Value.Track : null;
+
+ if (track?.IsDummyDevice == false)
+ {
+ progressBar.EndTime = track.Length;
+ progressBar.CurrentTime = track.CurrentTime;
+
+ playButton.Icon = track.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
+ }
+ else
+ {
+ progressBar.CurrentTime = 0;
+ progressBar.EndTime = 1;
+ playButton.Icon = FontAwesome.Regular.PlayCircle;
+ }
+ }
+
+ private Action pendingBeatmapSwitch;
+
+ private void trackChanged(WorkingBeatmap beatmap, TrackChangeDirection direction = TrackChangeDirection.None)
+ {
+ // avoid using scheduler as our scheduler may not be run for a long time, holding references to beatmaps.
+ pendingBeatmapSwitch = delegate
+ {
+ // todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
+ Task.Run(() =>
+ {
+ if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists
+ {
+ title.Text = @"Nothing to play";
+ artist.Text = @"Nothing to play";
+ }
+ else
+ {
+ BeatmapMetadata metadata = beatmap.Metadata;
+ title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title));
+ artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist));
+ }
+ });
+
+ LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground =>
+ {
+ switch (direction)
+ {
+ case TrackChangeDirection.Next:
+ newBackground.Position = new Vector2(400, 0);
+ newBackground.MoveToX(0, 500, Easing.OutCubic);
+ background.MoveToX(-400, 500, Easing.OutCubic);
+ break;
+
+ case TrackChangeDirection.Prev:
+ newBackground.Position = new Vector2(-400, 0);
+ newBackground.MoveToX(0, 500, Easing.OutCubic);
+ background.MoveToX(400, 500, Easing.OutCubic);
+ break;
+ }
+
+ background.Expire();
+ background = newBackground;
+
+ playerContainer.Add(newBackground);
+ });
+ };
+ }
+
+ private void beatmapDisabledChanged(bool disabled)
+ {
+ if (disabled)
+ playlist.Hide();
+
+ playButton.Enabled.Value = !disabled;
+ prevButton.Enabled.Value = !disabled;
+ nextButton.Enabled.Value = !disabled;
+ playlistButton.Enabled.Value = !disabled;
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ if (musicController != null)
+ musicController.TrackChanged -= trackChanged;
+ }
+
+ private class MusicIconButton : IconButton
+ {
+ public MusicIconButton()
+ {
+ AutoSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ HoverColour = colours.YellowDark.Opacity(0.6f);
+ FlashColour = colours.Yellow;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // works with AutoSizeAxes above to make buttons autosize with the scale animation.
+ Content.AutoSizeAxes = Axes.None;
+ Content.Size = new Vector2(DEFAULT_BUTTON_SIZE);
+ }
+ }
+
+ private class Background : BufferedContainer
+ {
+ private readonly Sprite sprite;
+ private readonly WorkingBeatmap beatmap;
+
+ public Background(WorkingBeatmap beatmap = null)
+ {
+ this.beatmap = beatmap;
+ CacheDrawnFrameBuffer = true;
+ Depth = float.MaxValue;
+ RelativeSizeAxes = Axes.Both;
+
+ Children = new Drawable[]
+ {
+ sprite = new Sprite
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.Gray(150),
+ FillMode = FillMode.Fill,
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = bottom_black_area_height,
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ Colour = Color4.Black.Opacity(0.5f)
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
+ }
+ }
+
+ private class DragContainer : Container
+ {
+ protected override bool OnDragStart(DragStartEvent e)
+ {
+ return true;
+ }
+
+ protected override bool OnDrag(DragEvent e)
+ {
+ Vector2 change = e.MousePosition - e.MouseDownPosition;
+
+ // Diminish the drag distance as we go further to simulate "rubber band" feeling.
+ change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
+
+ this.MoveTo(change);
+ return true;
+ }
+
+ protected override bool OnDragEnd(DragEndEvent e)
+ {
+ this.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
+ return base.OnDragEnd(e);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs
new file mode 100644
index 0000000000..46c53ec409
--- /dev/null
+++ b/osu.Game/Overlays/OSD/Toast.cs
@@ -0,0 +1,84 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.OSD
+{
+ public abstract class Toast : Container
+ {
+ private const int toast_minimum_width = 240;
+
+ private readonly Container content;
+ protected override Container Content => content;
+
+ protected readonly OsuSpriteText ValueText;
+
+ protected Toast(string description, string value, string shortcut)
+ {
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+
+ // A toast's height is decided (and transformed) by the containing OnScreenDisplay.
+ RelativeSizeAxes = Axes.Y;
+ AutoSizeAxes = Axes.X;
+
+ InternalChildren = new Drawable[]
+ {
+ new Container //this container exists just to set a minimum width for the toast
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Width = toast_minimum_width
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ Alpha = 0.7f
+ },
+ content = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ },
+ new OsuSpriteText
+ {
+ Padding = new MarginPadding(10),
+ Name = "Description",
+ Font = OsuFont.GetFont(size: 14, weight: FontWeight.Black),
+ Spacing = new Vector2(1, 0),
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = description.ToUpperInvariant()
+ },
+ ValueText = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light),
+ Padding = new MarginPadding { Left = 10, Right = 10 },
+ Name = "Value",
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Text = value
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ Name = "Shortcut",
+ Alpha = 0.3f,
+ Margin = new MarginPadding { Bottom = 15 },
+ Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
+ Text = string.IsNullOrEmpty(shortcut) ? "NO KEY BOUND" : shortcut.ToUpperInvariant()
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game/Overlays/OSD/TrackedSettingToast.cs b/osu.Game/Overlays/OSD/TrackedSettingToast.cs
new file mode 100644
index 0000000000..8e8a99a0a7
--- /dev/null
+++ b/osu.Game/Overlays/OSD/TrackedSettingToast.cs
@@ -0,0 +1,147 @@
+// 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.Configuration.Tracking;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.OSD
+{
+ public class TrackedSettingToast : Toast
+ {
+ private const int lights_bottom_margin = 40;
+
+ public TrackedSettingToast(SettingDescription description)
+ : base(description.Name, description.Value, description.Shortcut)
+ {
+ FillFlowContainer optionLights;
+
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ Margin = new MarginPadding { Bottom = lights_bottom_margin },
+ Children = new Drawable[]
+ {
+ optionLights = new FillFlowContainer
+ {
+ Margin = new MarginPadding { Bottom = 5 },
+ Spacing = new Vector2(5, 0),
+ Direction = FillDirection.Horizontal,
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ AutoSizeAxes = Axes.Both
+ },
+ }
+ }
+ };
+
+ int optionCount = 0;
+ int selectedOption = -1;
+
+ switch (description.RawValue)
+ {
+ case bool val:
+ optionCount = 1;
+ if (val) selectedOption = 0;
+ break;
+
+ case Enum _:
+ var values = Enum.GetValues(description.RawValue.GetType());
+ optionCount = values.Length;
+ selectedOption = Convert.ToInt32(description.RawValue);
+ break;
+ }
+
+ ValueText.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre;
+
+ for (int i = 0; i < optionCount; i++)
+ optionLights.Add(new OptionLight { Glowing = i == selectedOption });
+ }
+
+ private class OptionLight : Container
+ {
+ private Color4 glowingColour, idleColour;
+
+ private const float transition_speed = 300;
+
+ private const float glow_strength = 0.4f;
+
+ private readonly Box fill;
+
+ public OptionLight()
+ {
+ Children = new[]
+ {
+ fill = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 1,
+ },
+ };
+ }
+
+ private bool glowing;
+
+ public bool Glowing
+ {
+ set
+ {
+ glowing = value;
+ if (!IsLoaded) return;
+
+ updateGlow();
+ }
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ fill.Colour = idleColour = Color4.White.Opacity(0.4f);
+ glowingColour = Color4.White;
+
+ Size = new Vector2(25, 5);
+
+ Masking = true;
+ CornerRadius = 3;
+
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Colour = colours.BlueDark.Opacity(glow_strength),
+ Type = EdgeEffectType.Glow,
+ Radius = 8,
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ updateGlow();
+ FinishTransforms(true);
+ }
+
+ private void updateGlow()
+ {
+ if (glowing)
+ {
+ fill.FadeColour(glowingColour, transition_speed, Easing.OutQuint);
+ FadeEdgeEffectTo(glow_strength, transition_speed, Easing.OutQuint);
+ }
+ else
+ {
+ FadeEdgeEffectTo(0, transition_speed, Easing.OutQuint);
+ fill.FadeColour(idleColour, transition_speed, Easing.OutQuint);
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs
index 88a1edddc5..a92320945e 100644
--- a/osu.Game/Overlays/OnScreenDisplay.cs
+++ b/osu.Game/Overlays/OnScreenDisplay.cs
@@ -8,34 +8,25 @@ using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Game.Graphics;
using osuTK;
-using osuTK.Graphics;
-using osu.Framework.Extensions.Color4Extensions;
-using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Threading;
using osu.Game.Configuration;
-using osu.Game.Graphics.Sprites;
+using osu.Game.Overlays.OSD;
namespace osu.Game.Overlays
{
+ ///
+ /// An on-screen display which automatically tracks and displays toast notifications for .
+ /// Can also display custom content via
+ ///
public class OnScreenDisplay : Container
{
private readonly Container box;
- private readonly SpriteText textLine1;
- private readonly SpriteText textLine2;
- private readonly SpriteText textLine3;
-
private const float height = 110;
- private const float height_notext = 98;
private const float height_contracted = height * 0.9f;
- private readonly FillFlowContainer optionLights;
-
public OnScreenDisplay()
{
RelativeSizeAxes = Axes.Both;
@@ -52,64 +43,6 @@ namespace osu.Game.Overlays
Height = height_contracted,
Alpha = 0,
CornerRadius = 20,
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- Alpha = 0.7f,
- },
- new Container // purely to add a minimum width
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Width = 240,
- RelativeSizeAxes = Axes.Y,
- },
- textLine1 = new OsuSpriteText
- {
- Padding = new MarginPadding(10),
- Font = OsuFont.GetFont(size: 14, weight: FontWeight.Black),
- Spacing = new Vector2(1, 0),
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- },
- textLine2 = new OsuSpriteText
- {
- Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light),
- Padding = new MarginPadding { Left = 10, Right = 10 },
- Anchor = Anchor.Centre,
- Origin = Anchor.BottomCentre,
- },
- new FillFlowContainer
- {
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Vertical,
- Children = new Drawable[]
- {
- optionLights = new FillFlowContainer
- {
- Padding = new MarginPadding { Top = 20, Bottom = 5 },
- Spacing = new Vector2(5, 0),
- Direction = FillDirection.Horizontal,
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- AutoSizeAxes = Axes.Both
- },
- textLine3 = new OsuSpriteText
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Margin = new MarginPadding { Bottom = 15 },
- Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
- Alpha = 0.3f,
- },
- }
- }
- }
},
};
}
@@ -142,7 +75,7 @@ namespace osu.Game.Overlays
return;
configManager.LoadInto(trackedSettings);
- trackedSettings.SettingChanged += display;
+ trackedSettings.SettingChanged += displayTrackedSettingChange;
trackedConfigManagers.Add((source, configManager), trackedSettings);
}
@@ -162,56 +95,23 @@ namespace osu.Game.Overlays
return;
existing.Unload();
- existing.SettingChanged -= display;
+ existing.SettingChanged -= displayTrackedSettingChange;
trackedConfigManagers.Remove((source, configManager));
}
- private void display(SettingDescription description)
+ ///
+ /// Displays the provided temporarily.
+ ///
+ ///
+ public void Display(Toast toast)
{
- Schedule(() =>
- {
- textLine1.Text = description.Name.ToUpperInvariant();
- textLine2.Text = description.Value;
- textLine3.Text = description.Shortcut.ToUpperInvariant();
-
- if (string.IsNullOrEmpty(textLine3.Text))
- textLine3.Text = "NO KEY BOUND";
-
- DisplayTemporarily(box);
-
- int optionCount = 0;
- int selectedOption = -1;
-
- switch (description.RawValue)
- {
- case bool val:
- optionCount = 1;
- if (val) selectedOption = 0;
- break;
-
- case Enum _:
- var values = Enum.GetValues(description.RawValue.GetType());
- optionCount = values.Length;
- selectedOption = Convert.ToInt32(description.RawValue);
- break;
- }
-
- textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre;
- textLine2.Y = optionCount > 0 ? 0 : 5;
-
- if (optionLights.Children.Count != optionCount)
- {
- optionLights.Clear();
- for (int i = 0; i < optionCount; i++)
- optionLights.Add(new OptionLight());
- }
-
- for (int i = 0; i < optionCount; i++)
- optionLights.Children[i].Glowing = i == selectedOption;
- });
+ box.Child = toast;
+ DisplayTemporarily(box);
}
+ private void displayTrackedSettingChange(SettingDescription description) => Schedule(() => Display(new TrackedSettingToast(description)));
+
private TransformSequence fadeIn;
private ScheduledDelegate fadeOut;
@@ -236,80 +136,5 @@ namespace osu.Game.Overlays
b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint));
}, 500);
}
-
- private class OptionLight : Container
- {
- private Color4 glowingColour, idleColour;
-
- private const float transition_speed = 300;
-
- private const float glow_strength = 0.4f;
-
- private readonly Box fill;
-
- public OptionLight()
- {
- Children = new[]
- {
- fill = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Alpha = 1,
- },
- };
- }
-
- private bool glowing;
-
- public bool Glowing
- {
- set
- {
- glowing = value;
- if (!IsLoaded) return;
-
- updateGlow();
- }
- }
-
- private void updateGlow()
- {
- if (glowing)
- {
- fill.FadeColour(glowingColour, transition_speed, Easing.OutQuint);
- FadeEdgeEffectTo(glow_strength, transition_speed, Easing.OutQuint);
- }
- else
- {
- FadeEdgeEffectTo(0, transition_speed, Easing.OutQuint);
- fill.FadeColour(idleColour, transition_speed, Easing.OutQuint);
- }
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- fill.Colour = idleColour = Color4.White.Opacity(0.4f);
- glowingColour = Color4.White;
-
- Size = new Vector2(25, 5);
-
- Masking = true;
- CornerRadius = 3;
-
- EdgeEffect = new EdgeEffectParameters
- {
- Colour = colours.BlueDark.Opacity(glow_strength),
- Type = EdgeEffectType.Glow,
- Radius = 8,
- };
- }
-
- protected override void LoadComplete()
- {
- updateGlow();
- FinishTransforms(true);
- }
- }
}
}
diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs
index e7f7c2f490..158641d816 100644
--- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs
+++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs
@@ -140,6 +140,9 @@ namespace osu.Game.Overlays.Profile.Header
{
if (string.IsNullOrEmpty(content)) return;
+ // newlines could be contained in API returned user content.
+ content = content.Replace("\n", " ");
+
bottomLinkContainer.AddIcon(icon, text =>
{
text.Font = text.Font.With(size: 10);
diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs
new file mode 100644
index 0000000000..f18f319e27
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs
@@ -0,0 +1,168 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using 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.Input.Events;
+using osu.Game.Graphics;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class PreviousUsernames : CompositeDrawable
+ {
+ private const int duration = 200;
+ private const int margin = 10;
+ private const int width = 310;
+ private const int move_offset = 15;
+
+ public readonly Bindable User = new Bindable();
+
+ private readonly TextFlowContainer text;
+ private readonly Box background;
+ private readonly SpriteText header;
+
+ public PreviousUsernames()
+ {
+ HoverIconContainer hoverIcon;
+
+ AutoSizeAxes = Axes.Y;
+ Width = width;
+ Masking = true;
+ CornerRadius = 5;
+
+ AddRangeInternal(new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ new GridContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.AutoSize)
+ },
+ ColumnDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.Distributed)
+ },
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ hoverIcon = new HoverIconContainer(),
+ header = new SpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Text = @"formerly known as",
+ Font = OsuFont.GetFont(size: 10, italics: true)
+ }
+ },
+ new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ text = new TextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold, italics: true))
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Full,
+ Margin = new MarginPadding { Bottom = margin, Top = margin / 2f }
+ }
+ }
+ }
+ }
+ });
+
+ hoverIcon.ActivateHover += showContent;
+ hideContent();
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ background.Colour = colours.GreySeafoamDarker;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ User.BindValueChanged(onUserChanged, true);
+ }
+
+ private void onUserChanged(ValueChangedEvent user)
+ {
+ text.Text = string.Empty;
+
+ var usernames = user.NewValue?.PreviousUsernames;
+
+ if (usernames?.Any() ?? false)
+ {
+ text.Text = string.Join(", ", usernames);
+ Show();
+ return;
+ }
+
+ Hide();
+ }
+
+ protected override void OnHoverLost(HoverLostEvent e)
+ {
+ base.OnHoverLost(e);
+ hideContent();
+ }
+
+ private void showContent()
+ {
+ text.FadeIn(duration, Easing.OutQuint);
+ header.FadeIn(duration, Easing.OutQuint);
+ background.FadeIn(duration, Easing.OutQuint);
+ this.MoveToY(-move_offset, duration, Easing.OutQuint);
+ }
+
+ private void hideContent()
+ {
+ text.FadeOut(duration, Easing.OutQuint);
+ header.FadeOut(duration, Easing.OutQuint);
+ background.FadeOut(duration, Easing.OutQuint);
+ this.MoveToY(0, duration, Easing.OutQuint);
+ }
+
+ private class HoverIconContainer : Container
+ {
+ public Action ActivateHover;
+
+ public HoverIconContainer()
+ {
+ AutoSizeAxes = Axes.Both;
+ Child = new SpriteIcon
+ {
+ Margin = new MarginPadding { Top = 6, Left = margin, Right = margin * 2 },
+ Size = new Vector2(15),
+ Icon = FontAwesome.Solid.IdCard,
+ };
+ }
+
+ protected override bool OnHover(HoverEvent e)
+ {
+ ActivateHover?.Invoke();
+ return base.OnHover(e);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs
index 9cb9d48de7..de760eedfd 100644
--- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs
+++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs
@@ -196,17 +196,30 @@ namespace osu.Game.Overlays.Profile.Header.Components
}
}
- public string TooltipText => Statistics.Value?.Ranks.Global == null ? "" : $"#{ranks[dayIndex].Value:#,##0}|{ranked_days - ranks[dayIndex].Key + 1}";
+ public object TooltipContent
+ {
+ get
+ {
+ if (Statistics.Value?.Ranks.Global == null)
+ return null;
+
+ var days = ranked_days - ranks[dayIndex].Key + 1;
+
+ return new TooltipDisplayContent
+ {
+ Rank = $"#{ranks[dayIndex].Value:#,##0}",
+ Time = days == 0 ? "now" : $"{days} days ago"
+ };
+ }
+ }
public ITooltip GetCustomTooltip() => new RankGraphTooltip();
- public class RankGraphTooltip : VisibilityContainer, ITooltip
+ private class RankGraphTooltip : VisibilityContainer, ITooltip
{
private readonly OsuSpriteText globalRankingText, timeText;
private readonly Box background;
- public string TooltipText { get; set; }
-
public RankGraphTooltip()
{
AutoSizeAxes = Axes.Both;
@@ -260,11 +273,14 @@ namespace osu.Game.Overlays.Profile.Header.Components
background.Colour = colours.GreySeafoamDark;
}
- public void Refresh()
+ public bool SetContent(object content)
{
- var info = TooltipText.Split('|');
- globalRankingText.Text = info[0];
- timeText.Text = info[1] == "0" ? "now" : $"{info[1]} days ago";
+ if (!(content is TooltipDisplayContent info))
+ return false;
+
+ globalRankingText.Text = info.Rank;
+ timeText.Text = info.Time;
+ return true;
}
private bool instantMove = true;
@@ -280,9 +296,24 @@ namespace osu.Game.Overlays.Profile.Header.Components
this.MoveTo(pos, 200, Easing.OutQuint);
}
+ public void Refresh()
+ {
+ }
+
+ public string TooltipText
+ {
+ set => throw new InvalidOperationException();
+ }
+
protected override void PopIn() => this.FadeIn(200, Easing.OutQuint);
protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
}
+
+ private class TooltipDisplayContent
+ {
+ public string Rank;
+ public string Time;
+ }
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs
index 4e43caff23..5ccdc952ba 100644
--- a/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Audio/MainMenuSettings.cs
@@ -1,7 +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 System;
+using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Graphics;
using osu.Game.Configuration;
namespace osu.Game.Overlays.Settings.Sections.Audio
@@ -13,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
- Children = new[]
+ Children = new Drawable[]
{
new SettingsCheckbox
{
@@ -25,6 +28,12 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
LabelText = "osu! music theme",
Bindable = config.GetBindable(OsuSetting.MenuMusic)
},
+ new SettingsDropdown
+ {
+ LabelText = "Intro sequence",
+ Bindable = config.GetBindable(OsuSetting.IntroSequence),
+ Items = Enum.GetValues(typeof(IntroSequence)).Cast()
+ },
};
}
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs
index f03df2ed93..b29aec5842 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Toolbar
}
[BackgroundDependencyLoader(true)]
- private void load(MusicController music)
+ private void load(NowPlayingOverlay music)
{
StateContainer = music;
}
diff --git a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs
index 2200caeb20..e85ebb5f3a 100644
--- a/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs
+++ b/osu.Game/Rulesets/Edit/DrawableEditRuleset.cs
@@ -2,6 +2,7 @@
// 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.Game.Beatmaps;
@@ -57,7 +58,12 @@ namespace osu.Game.Rulesets.Edit
this.drawableRuleset = drawableRuleset;
InternalChild = drawableRuleset;
+ }
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ drawableRuleset.FrameStablePlayback = false;
Playfield.DisplayJudgements.Value = false;
}
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index ac81fdc719..eb14bd1f24 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -62,6 +62,15 @@ namespace osu.Game.Rulesets.UI
public override GameplayClock FrameStableClock => frameStabilityContainer.GameplayClock;
+ ///
+ /// Whether to enable frame-stable playback.
+ ///
+ internal bool FrameStablePlayback
+ {
+ get => frameStabilityContainer.FrameStablePlayback;
+ set => frameStabilityContainer.FrameStablePlayback = value;
+ }
+
///
/// Invoked when a has been applied by a .
///
diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
index 1cc56fff8b..05d3c02381 100644
--- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
+++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
@@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.UI
///
public int MaxCatchUpFrames { get; set; } = 5;
+ ///
+ /// Whether to enable frame-stable playback.
+ ///
+ internal bool FrameStablePlayback = true;
+
[Cached]
public GameplayClock GameplayClock { get; }
@@ -113,7 +118,13 @@ namespace osu.Game.Rulesets.UI
try
{
- if (firstConsumption)
+ if (!FrameStablePlayback)
+ {
+ manualClock.CurrentTime = newProposedTime;
+ requireMoreUpdateLoops = false;
+ return;
+ }
+ else if (firstConsumption)
{
// On the first update, frame-stability seeking would result in unexpected/unwanted behaviour.
// Instead we perform an initial seek to the proposed time.
diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs
index bbe162cf7c..5dfaceccf5 100644
--- a/osu.Game/Screens/BackgroundScreen.cs
+++ b/osu.Game/Screens/BackgroundScreen.cs
@@ -11,8 +11,11 @@ namespace osu.Game.Screens
{
public abstract class BackgroundScreen : Screen, IEquatable
{
- protected BackgroundScreen()
+ private readonly bool animateOnEnter;
+
+ protected BackgroundScreen(bool animateOnEnter = true)
{
+ this.animateOnEnter = animateOnEnter;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
}
@@ -39,11 +42,14 @@ namespace osu.Game.Screens
public override void OnEntering(IScreen last)
{
- this.FadeOut();
- this.MoveToX(x_movement_amount);
+ if (animateOnEnter)
+ {
+ this.FadeOut();
+ this.MoveToX(x_movement_amount);
- this.FadeIn(transition_length, Easing.InOutQuart);
- this.MoveToX(0, transition_length, Easing.InOutQuart);
+ this.FadeIn(transition_length, Easing.InOutQuart);
+ this.MoveToX(0, transition_length, Easing.InOutQuart);
+ }
base.OnEntering(last);
}
diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs
index 55338ea01a..2d7fe6a6a3 100644
--- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs
+++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs
@@ -25,6 +25,11 @@ namespace osu.Game.Screens.Backgrounds
private Bindable user;
private Bindable skin;
+ public BackgroundScreenDefault(bool animateOnEnter = true)
+ : base(animateOnEnter)
+ {
+ }
+
[BackgroundDependencyLoader]
private void load(IAPIProvider api, SkinManager skinManager)
{
diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs
index de00ba2e9f..850349272e 100644
--- a/osu.Game/Screens/Loader.cs
+++ b/osu.Game/Screens/Loader.cs
@@ -9,6 +9,8 @@ using osu.Framework.Graphics.Shaders;
using osu.Game.Screens.Menu;
using osuTK;
using osu.Framework.Screens;
+using osu.Game.Configuration;
+using IntroSequence = osu.Game.Configuration.IntroSequence;
namespace osu.Game.Screens
{
@@ -45,6 +47,8 @@ namespace osu.Game.Screens
private OsuScreen loadableScreen;
private ShaderPrecompiler precompiler;
+ private IntroSequence introSequence;
+
protected virtual OsuScreen CreateLoadableScreen()
{
if (showDisclaimer)
@@ -53,7 +57,17 @@ namespace osu.Game.Screens
return getIntroSequence();
}
- private IntroScreen getIntroSequence() => new IntroCircles();
+ private IntroScreen getIntroSequence()
+ {
+ switch (introSequence)
+ {
+ case IntroSequence.Circles:
+ return new IntroCircles();
+
+ default:
+ return new IntroTriangles();
+ }
+ }
protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler();
@@ -79,9 +93,10 @@ namespace osu.Game.Screens
}
[BackgroundDependencyLoader]
- private void load(OsuGameBase game)
+ private void load(OsuGameBase game, OsuConfigManager config)
{
showDisclaimer = game.IsDeployedBuild;
+ introSequence = config.Get(OsuSetting.IntroSequence);
}
///
diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs
new file mode 100644
index 0000000000..ba0d624959
--- /dev/null
+++ b/osu.Game/Screens/Menu/IntroTriangles.cs
@@ -0,0 +1,413 @@
+// 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.IO;
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
+using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
+using osu.Framework.Screens;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Graphics.Video;
+using osu.Framework.MathUtils;
+using osu.Framework.Timing;
+using osu.Game.Beatmaps;
+using osu.Game.Configuration;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.IO.Archives;
+using osu.Game.Rulesets;
+using osu.Game.Screens.Backgrounds;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Screens.Menu
+{
+ public class IntroTriangles : IntroScreen
+ {
+ private const string menu_music_beatmap_hash = "a1556d0801b3a6b175dda32ef546f0ec812b400499f575c44fccbe9c67f9b1e5";
+
+ private SampleChannel welcome;
+
+ protected override BackgroundScreen CreateBackground() => background = new BackgroundScreenDefault(false)
+ {
+ Alpha = 0,
+ };
+
+ [Resolved]
+ private AudioManager audio { get; set; }
+
+ private Bindable menuMusic;
+ private Track track;
+ private WorkingBeatmap introBeatmap;
+
+ private BackgroundScreenDefault background;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
+ {
+ menuMusic = config.GetBindable(OsuSetting.MenuMusic);
+
+ BeatmapSetInfo setInfo = null;
+
+ if (!menuMusic.Value)
+ {
+ var sets = beatmaps.GetAllUsableBeatmapSets();
+ if (sets.Count > 0)
+ setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID);
+ }
+
+ if (setInfo == null)
+ {
+ setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == menu_music_beatmap_hash);
+
+ if (setInfo == null)
+ {
+ // we need to import the default menu background beatmap
+ setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/triangles.osz"), "triangles.osz")).Result;
+
+ setInfo.Protected = true;
+ beatmaps.Update(setInfo);
+ }
+ }
+
+ introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
+
+ track = introBeatmap.Track;
+ track.Reset();
+
+ if (config.Get(OsuSetting.MenuVoice) && !menuMusic.Value)
+ // triangles has welcome sound included in the track. only play this if the user doesn't want menu music.
+ welcome = audio.Samples.Get(@"welcome");
+ }
+
+ protected override void LogoArriving(OsuLogo logo, bool resuming)
+ {
+ base.LogoArriving(logo, resuming);
+
+ logo.Triangles = true;
+
+ if (!resuming)
+ {
+ Beatmap.Value = introBeatmap;
+ introBeatmap = null;
+
+ PrepareMenuLoad();
+
+ LoadComponentAsync(new TrianglesIntroSequence(logo, background)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Clock = new FramedClock(menuMusic.Value ? track : null),
+ LoadMenu = LoadMenu
+ }, t =>
+ {
+ AddInternal(t);
+ welcome?.Play();
+
+ // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu.
+ if (menuMusic.Value)
+ track.Start();
+ });
+ }
+ }
+
+ public override void OnResuming(IScreen last)
+ {
+ base.OnResuming(last);
+ background.FadeOut(100);
+ }
+
+ public override void OnSuspending(IScreen next)
+ {
+ track = null;
+ base.OnSuspending(next);
+ }
+
+ private class TrianglesIntroSequence : CompositeDrawable
+ {
+ private readonly OsuLogo logo;
+ private readonly BackgroundScreenDefault background;
+ private OsuSpriteText welcomeText;
+
+ private RulesetFlow rulesets;
+ private Container rulesetsScale;
+ private Drawable logoContainerSecondary;
+ private Drawable logoContainer;
+
+ private GlitchingTriangles triangles;
+
+ public Action LoadMenu;
+
+ public TrianglesIntroSequence(OsuLogo logo, BackgroundScreenDefault background)
+ {
+ this.logo = logo;
+ this.background = background;
+ }
+
+ private OsuGameBase game;
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures, OsuGameBase game)
+ {
+ this.game = game;
+
+ InternalChildren = new[]
+ {
+ triangles = new GlitchingTriangles
+ {
+ Alpha = 0,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(0.4f, 0.16f)
+ },
+ welcomeText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Padding = new MarginPadding { Bottom = 10 },
+ Font = OsuFont.GetFont(weight: FontWeight.Light, size: 42),
+ Alpha = 1,
+ Spacing = new Vector2(5),
+ },
+ rulesetsScale = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Children = new Drawable[]
+ {
+ rulesets = new RulesetFlow()
+ }
+ },
+ logoContainerSecondary = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Child = logoContainer = new LazerLogo(textures.GetStream("Menu/logo-triangles.mp4"))
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }
+ },
+ };
+ }
+
+ private const double text_1 = 200;
+ private const double text_2 = 400;
+ private const double text_3 = 700;
+ private const double text_4 = 900;
+ private const double text_glitch = 1060;
+
+ private const double rulesets_1 = 1450;
+ private const double rulesets_2 = 1650;
+ private const double rulesets_3 = 1850;
+
+ private const double logo_scale_duration = 920;
+ private const double logo_1 = 2080;
+ private const double logo_2 = logo_1 + logo_scale_duration;
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ const float scale_start = 1.2f;
+ const float scale_adjust = 0.8f;
+
+ rulesets.Hide();
+ logoContainer.Hide();
+ background.Hide();
+
+ using (BeginAbsoluteSequence(0, true))
+ {
+ using (BeginDelayedSequence(text_1, true))
+ welcomeText.FadeIn().OnComplete(t => t.Text = "wel");
+
+ using (BeginDelayedSequence(text_2, true))
+ welcomeText.FadeIn().OnComplete(t => t.Text = "welcome");
+
+ using (BeginDelayedSequence(text_3, true))
+ welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to");
+
+ using (BeginDelayedSequence(text_4, true))
+ {
+ welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to osu!");
+ welcomeText.TransformTo(nameof(welcomeText.Spacing), new Vector2(50, 0), 5000);
+ }
+
+ using (BeginDelayedSequence(text_glitch, true))
+ triangles.FadeIn();
+
+ using (BeginDelayedSequence(rulesets_1, true))
+ {
+ rulesetsScale.ScaleTo(0.8f, 1000);
+ rulesets.FadeIn().ScaleTo(1).TransformSpacingTo(new Vector2(200, 0));
+ welcomeText.FadeOut();
+ triangles.FadeOut();
+ }
+
+ using (BeginDelayedSequence(rulesets_2, true))
+ {
+ rulesets.ScaleTo(2).TransformSpacingTo(new Vector2(30, 0));
+ }
+
+ using (BeginDelayedSequence(rulesets_3, true))
+ {
+ rulesets.ScaleTo(4).TransformSpacingTo(new Vector2(10, 0));
+ rulesetsScale.ScaleTo(1.3f, 1000);
+ }
+
+ using (BeginDelayedSequence(logo_1, true))
+ {
+ rulesets.FadeOut();
+
+ // matching flyte curve y = 0.25x^2 + (max(0, x - 0.7) / 0.3) ^ 5
+ logoContainer.FadeIn().ScaleTo(scale_start).Then().Delay(logo_scale_duration * 0.7f).ScaleTo(scale_start - scale_adjust, logo_scale_duration * 0.3f, Easing.InQuint);
+ logoContainerSecondary.ScaleTo(scale_start).Then().ScaleTo(scale_start - scale_adjust * 0.25f, logo_scale_duration, Easing.InQuad);
+ }
+
+ using (BeginDelayedSequence(logo_2, true))
+ {
+ logoContainer.FadeOut().OnComplete(_ =>
+ {
+ logo.FadeIn();
+ background.FadeIn();
+
+ game.Add(new GameWideFlash());
+
+ LoadMenu();
+ });
+ }
+ }
+ }
+
+ private class GameWideFlash : Box
+ {
+ private const double flash_length = 1000;
+
+ public GameWideFlash()
+ {
+ Colour = Color4.White;
+ RelativeSizeAxes = Axes.Both;
+ Blending = BlendingMode.Additive;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ this.FadeOutFromOne(flash_length, Easing.Out);
+ }
+ }
+
+ private class LazerLogo : CompositeDrawable
+ {
+ public LazerLogo(Stream videoStream)
+ {
+ Size = new Vector2(960);
+
+ InternalChild = new VideoSprite(videoStream)
+ {
+ RelativeSizeAxes = Axes.Both,
+ Clock = new FramedOffsetClock(Clock) { Offset = -logo_1 }
+ };
+ }
+ }
+
+ private class RulesetFlow : FillFlowContainer
+ {
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ var modes = new List();
+
+ foreach (var ruleset in rulesets.AvailableRulesets)
+ {
+ var icon = new ConstrainedIconContainer
+ {
+ Icon = ruleset.CreateInstance().CreateIcon(),
+ Size = new Vector2(30),
+ };
+
+ modes.Add(icon);
+ }
+
+ AutoSizeAxes = Axes.Both;
+ Children = modes;
+
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ }
+ }
+
+ private class GlitchingTriangles : CompositeDrawable
+ {
+ public GlitchingTriangles()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ private double? lastGenTime;
+
+ private const double time_between_triangles = 22;
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (lastGenTime == null || Time.Current - lastGenTime > time_between_triangles)
+ {
+ lastGenTime = (lastGenTime ?? Time.Current) + time_between_triangles;
+
+ Drawable triangle = new OutlineTriangle(RNG.NextBool(), (RNG.NextSingle() + 0.2f) * 80)
+ {
+ RelativePositionAxes = Axes.Both,
+ Position = new Vector2(RNG.NextSingle(), RNG.NextSingle()),
+ };
+
+ AddInternal(triangle);
+
+ triangle.FadeOutFromOne(120);
+ }
+ }
+
+ ///
+ /// Represents a sprite that is drawn in a triangle shape, instead of a rectangle shape.
+ ///
+ public class OutlineTriangle : BufferedContainer
+ {
+ public OutlineTriangle(bool outlineOnly, float size)
+ {
+ Size = new Vector2(size);
+
+ InternalChildren = new Drawable[]
+ {
+ new Triangle { RelativeSizeAxes = Axes.Both },
+ };
+
+ if (outlineOnly)
+ {
+ AddInternal(new Triangle
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Colour = Color4.Black,
+ Size = new Vector2(size - 5),
+ Blending = BlendingMode.None,
+ });
+ }
+
+ Blending = BlendingMode.Additive;
+ CacheDrawnFrameBuffer = true;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs
index 73994fa369..a52d43acf4 100644
--- a/osu.Game/Screens/Multi/Match/Components/Header.cs
+++ b/osu.Game/Screens/Multi/Match/Components/Header.cs
@@ -25,10 +25,14 @@ namespace osu.Game.Screens.Multi.Match.Components
{
public const float HEIGHT = 200;
- public MatchTabControl Tabs;
+ public readonly BindableBool ShowBeatmapPanel = new BindableBool();
+
+ public MatchTabControl Tabs { get; private set; }
public Action RequestBeatmapSelection;
+ private MatchBeatmapPanel beatmapPanel;
+
public Header()
{
RelativeSizeAxes = Axes.X;
@@ -53,8 +57,14 @@ namespace osu.Game.Screens.Multi.Match.Components
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.4f), Color4.Black.Opacity(0.6f)),
+ Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.7f), Color4.Black.Opacity(0.8f)),
},
+ beatmapPanel = new MatchBeatmapPanel
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Margin = new MarginPadding { Right = 100 },
+ }
}
},
new Box
@@ -114,6 +124,12 @@ namespace osu.Game.Screens.Multi.Match.Components
beatmapButton.Action = () => RequestBeatmapSelection?.Invoke();
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ ShowBeatmapPanel.BindValueChanged(value => beatmapPanel.FadeTo(value.NewValue ? 1 : 0, 200, Easing.OutQuint), true);
+ }
+
private class BeatmapSelectButton : HeaderButton
{
[Resolved(typeof(Room), nameof(Room.RoomID))]
diff --git a/osu.Game/Screens/Multi/Match/Components/Info.cs b/osu.Game/Screens/Multi/Match/Components/Info.cs
index a185c4db50..74f000c21f 100644
--- a/osu.Game/Screens/Multi/Match/Components/Info.cs
+++ b/osu.Game/Screens/Multi/Match/Components/Info.cs
@@ -28,7 +28,6 @@ namespace osu.Game.Screens.Multi.Match.Components
private void load()
{
ReadyButton readyButton;
- ViewBeatmapButton viewBeatmapButton;
HostInfo hostInfo;
InternalChildren = new Drawable[]
@@ -80,7 +79,6 @@ namespace osu.Game.Screens.Multi.Match.Components
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
- viewBeatmapButton = new ViewBeatmapButton(),
readyButton = new ReadyButton
{
Action = () => OnStart?.Invoke()
@@ -91,11 +89,7 @@ namespace osu.Game.Screens.Multi.Match.Components
},
};
- CurrentItem.BindValueChanged(item =>
- {
- viewBeatmapButton.Beatmap.Value = item.NewValue?.Beatmap;
- readyButton.Beatmap.Value = item.NewValue?.Beatmap;
- }, true);
+ CurrentItem.BindValueChanged(item => readyButton.Beatmap.Value = item.NewValue?.Beatmap, true);
hostInfo.Host.BindTo(Host);
}
diff --git a/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs
new file mode 100644
index 0000000000..7c1fe91393
--- /dev/null
+++ b/osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.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 System.Threading;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Overlays.Direct;
+using osu.Game.Rulesets;
+
+namespace osu.Game.Screens.Multi.Match.Components
+{
+ public class MatchBeatmapPanel : MultiplayerComposite
+ {
+ [Resolved]
+ private IAPIProvider api { get; set; }
+
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
+
+ private CancellationTokenSource loadCancellation;
+ private GetBeatmapSetRequest request;
+ private DirectGridPanel panel;
+
+ public MatchBeatmapPanel()
+ {
+ AutoSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ CurrentItem.BindValueChanged(item => loadNewPanel(item.NewValue?.Beatmap), true);
+ }
+
+ private void loadNewPanel(BeatmapInfo beatmap)
+ {
+ loadCancellation?.Cancel();
+ request?.Cancel();
+
+ panel?.FadeOut(200);
+ panel?.Expire();
+ panel = null;
+
+ if (beatmap?.OnlineBeatmapID == null)
+ return;
+
+ loadCancellation = new CancellationTokenSource();
+
+ request = new GetBeatmapSetRequest(beatmap.OnlineBeatmapID.Value, BeatmapSetLookupType.BeatmapId);
+ request.Success += res => Schedule(() =>
+ {
+ panel = new DirectGridPanel(res.ToBeatmapSet(rulesets));
+ LoadComponentAsync(panel, AddInternal, loadCancellation.Token);
+ });
+
+ api.Queue(request);
+ }
+ }
+}
diff --git a/osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs b/osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs
deleted file mode 100644
index 8d1ff21124..0000000000
--- a/osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs
+++ /dev/null
@@ -1,46 +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.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics;
-using osu.Game.Beatmaps;
-using osuTK;
-
-namespace osu.Game.Screens.Multi.Match.Components
-{
- public class ViewBeatmapButton : HeaderButton
- {
- public readonly Bindable Beatmap = new Bindable();
-
- [Resolved(CanBeNull = true)]
- private OsuGame osuGame { get; set; }
-
- public ViewBeatmapButton()
- {
- RelativeSizeAxes = Axes.Y;
- Size = new Vector2(200, 1);
-
- Text = "View beatmap";
- }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- if (osuGame != null)
- Beatmap.BindValueChanged(beatmap => updateAction(beatmap.NewValue), true);
- }
-
- private void updateAction(BeatmapInfo beatmap)
- {
- if (beatmap == null)
- {
- Enabled.Value = false;
- return;
- }
-
- Action = () => osuGame.ShowBeatmap(beatmap.OnlineBeatmapID ?? 0);
- Enabled.Value = true;
- }
- }
-}
diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs
index 5469d4c8c8..f3e10db444 100644
--- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs
+++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs
@@ -7,6 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
+using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.GameTypes;
@@ -18,7 +19,8 @@ using PlaylistItem = osu.Game.Online.Multiplayer.PlaylistItem;
namespace osu.Game.Screens.Multi.Match
{
- public class MatchSubScreen : MultiplayerSubScreen
+ [Cached(typeof(IPreviewTrackOwner))]
+ public class MatchSubScreen : MultiplayerSubScreen, IPreviewTrackOwner
{
public override bool DisallowExternalBeatmapRulesetChanges => true;
@@ -44,6 +46,9 @@ namespace osu.Game.Screens.Multi.Match
[Resolved]
private BeatmapManager beatmapManager { get; set; }
+ [Resolved]
+ private PreviewTrackManager previewTrackManager { get; set; }
+
[Resolved(CanBeNull = true)]
private OsuGame game { get; set; }
@@ -146,18 +151,12 @@ namespace osu.Game.Screens.Multi.Match
{
const float fade_duration = 500;
- if (tab.NewValue is SettingsMatchPage)
- {
- settings.Show();
- info.FadeOut(fade_duration, Easing.OutQuint);
- bottomRow.FadeOut(fade_duration, Easing.OutQuint);
- }
- else
- {
- settings.Hide();
- info.FadeIn(fade_duration, Easing.OutQuint);
- bottomRow.FadeIn(fade_duration, Easing.OutQuint);
- }
+ var settingsDisplayed = tab.NewValue is SettingsMatchPage;
+
+ header.ShowBeatmapPanel.Value = !settingsDisplayed;
+ settings.State.Value = settingsDisplayed ? Visibility.Visible : Visibility.Hidden;
+ info.FadeTo(settingsDisplayed ? 0 : 1, fade_duration, Easing.OutQuint);
+ bottomRow.FadeTo(settingsDisplayed ? 0 : 1, fade_duration, Easing.OutQuint);
}, true);
chat.Exit += () =>
@@ -179,8 +178,8 @@ namespace osu.Game.Screens.Multi.Match
public override bool OnExiting(IScreen next)
{
RoomManager?.PartRoom();
-
Mods.Value = Array.Empty();
+ previewTrackManager.StopAnyPlaying(this);
return base.OnExiting(next);
}
@@ -198,6 +197,8 @@ namespace osu.Game.Screens.Multi.Match
if (e.NewValue?.Ruleset != null)
Ruleset.Value = e.NewValue.Ruleset;
+
+ previewTrackManager.StopAnyPlaying(this);
}
///
@@ -223,6 +224,8 @@ namespace osu.Game.Screens.Multi.Match
private void onStart()
{
+ previewTrackManager.StopAnyPlaying(this);
+
switch (type.Value)
{
default:
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index 7366fa8c17..23c581c6f9 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -24,7 +24,7 @@ using osu.Game.Screens.Select.Carousel;
namespace osu.Game.Screens.Select
{
- public class BeatmapCarousel : OsuScrollContainer
+ public class BeatmapCarousel : CompositeDrawable
{
private const float bleed_top = FilterControl.HEIGHT;
private const float bleed_bottom = Footer.HEIGHT;
@@ -61,6 +61,8 @@ namespace osu.Game.Screens.Select
///
public bool BeatmapSetsLoaded { get; private set; }
+ private readonly OsuScrollContainer scroll;
+
private IEnumerable beatmapSets => root.Children.OfType();
public IEnumerable BeatmapSets
@@ -110,13 +112,17 @@ namespace osu.Game.Screens.Select
public BeatmapCarousel()
{
root = new CarouselRoot(this);
- Child = new OsuContextMenuContainer
+ InternalChild = new OsuContextMenuContainer
{
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Child = scrollableContent = new Container
+ RelativeSizeAxes = Axes.Both,
+ Child = scroll = new CarouselScrollContainer
{
- RelativeSizeAxes = Axes.X,
+ Masking = false,
+ RelativeSizeAxes = Axes.Both,
+ Child = scrollableContent = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ }
}
};
}
@@ -127,7 +133,7 @@ namespace osu.Game.Screens.Select
config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled);
- RightClickScrollingEnabled.ValueChanged += enabled => RightMouseScrollbar = enabled.NewValue;
+ RightClickScrollingEnabled.ValueChanged += enabled => scroll.RightMouseScrollbar = enabled.NewValue;
RightClickScrollingEnabled.TriggerChange();
loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable());
@@ -351,12 +357,12 @@ namespace osu.Game.Screens.Select
///
/// The position of the lower visible bound with respect to the current scroll position.
///
- private float visibleBottomBound => Current + DrawHeight + bleed_bottom;
+ private float visibleBottomBound => scroll.Current + DrawHeight + bleed_bottom;
///
/// The position of the upper visible bound with respect to the current scroll position.
///
- private float visibleUpperBound => Current - bleed_top;
+ private float visibleUpperBound => scroll.Current - bleed_top;
public void FlushPendingFilterOperations()
{
@@ -628,7 +634,7 @@ namespace osu.Game.Screens.Select
private void updateScrollPosition()
{
- if (scrollTarget != null) ScrollTo(scrollTarget.Value);
+ if (scrollTarget != null) scroll.ScrollTo(scrollTarget.Value);
scrollPositionCache.Validate();
}
@@ -688,5 +694,35 @@ namespace osu.Game.Screens.Select
base.PerformSelection();
}
}
+
+ private class CarouselScrollContainer : OsuScrollContainer
+ {
+ private bool rightMouseScrollBlocked;
+
+ protected override bool OnMouseDown(MouseDownEvent e)
+ {
+ if (e.Button == MouseButton.Right)
+ {
+ // we need to block right click absolute scrolling when hovering a carousel item so context menus can display.
+ // this can be reconsidered when we have an alternative to right click scrolling.
+ if (GetContainingInputManager().HoveredDrawables.OfType().Any())
+ {
+ rightMouseScrollBlocked = true;
+ return false;
+ }
+ }
+
+ rightMouseScrollBlocked = false;
+ return base.OnMouseDown(e);
+ }
+
+ protected override bool OnDragStart(DragStartEvent e)
+ {
+ if (rightMouseScrollBlocked)
+ return false;
+
+ return base.OnDragStart(e);
+ }
+ }
}
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 7dd934f91a..8340814db9 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -157,7 +157,6 @@ namespace osu.Game.Screens.Select
},
Child = Carousel = new BeatmapCarousel
{
- Masking = false,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(1 - wedged_container_size.X, 1),
Anchor = Anchor.CentreRight,
diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs
index a5f3578711..9986f70557 100644
--- a/osu.Game/Users/User.cs
+++ b/osu.Game/Users/User.cs
@@ -20,6 +20,9 @@ namespace osu.Game.Users
[JsonProperty(@"username")]
public string Username;
+ [JsonProperty(@"previous_usernames")]
+ public string[] PreviousUsernames;
+
[JsonProperty(@"country")]
public Country Country;
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index b5266fd75d..758c4dda4c 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 103d89cadc..d6ad35b663 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -3,7 +3,8 @@
{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
Resources
PackageReference
- --nolinkaway -gcc_flags "-lstdc++ -framework AudioToolbox -framework SystemConfiguration -framework CFNetwork -framework Accelerate
+ --nolinkaway
+ -lstdc++ -lbz2 -framework AudioToolbox -framework AVFoundation -framework CoreMedia -framework VideoToolbox -framework SystemConfiguration -framework CFNetwork -framework Accelerate
true
@@ -24,7 +25,7 @@
false
Default
- $(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a"
+ $(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)"
false
cjk,mideast,other,rare,west
@@ -44,7 +45,7 @@
NSUrlSessionHandler
Default
- $(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a"
+ $(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)"
false
cjk,mideast,other,rare,west
@@ -62,7 +63,7 @@
NSUrlSessionHandler
Default
- $(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a"
+ $(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)"
false
cjk,mideast,other,rare,west
@@ -86,10 +87,22 @@
NSUrlSessionHandler
Default
- $(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a"
+ $(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)"
false
cjk,mideast,other,rare,west
+
+
+ Static
+ False
+ True
+
+
+ Static
+ False
+ True
+
+
@@ -105,12 +118,12 @@
-
-
+
+
-
+
diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist
index 0775d1522d..a118b329aa 100644
--- a/osu.iOS/Info.plist
+++ b/osu.iOS/Info.plist
@@ -31,6 +31,10 @@
UIStatusBarHidden
+ NSCameraUsageDescription
+ We don't really use the camera.
+ NSMicrophoneUsageDescription
+ We don't really use the microphone.
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
diff --git a/osu.iOS/libbass.a b/osu.iOS/libbass.a
deleted file mode 100644
index c34e6a0a0c..0000000000
Binary files a/osu.iOS/libbass.a and /dev/null differ
diff --git a/osu.iOS/libbass_fx.a b/osu.iOS/libbass_fx.a
deleted file mode 100644
index 007cd28647..0000000000
Binary files a/osu.iOS/libbass_fx.a and /dev/null differ
diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj
index 12505e73e4..19d1acf014 100644
--- a/osu.iOS/osu.iOS.csproj
+++ b/osu.iOS/osu.iOS.csproj
@@ -19,12 +19,6 @@
-
- PreserveNewest
-
-
- PreserveNewest
-