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 -