diff --git a/README.md b/README.md index 0df99f7d6b..19aba5a31f 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh ![](https://puu.sh/DCmvA/f6a74f5fbb.png) -If you are not interested in developing the game, you can consume our [binary releases](https://github.com/ppy/osu/releases). +If you are not interested in developing the game, you can still consume our [binary releases](https://github.com/ppy/osu/releases). -**Latest build:*** +**Latest build:** | [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | | ------------- | ------------- | diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 5a8cf32f14..1f1d2cea5f 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Development; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -12,7 +13,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; -using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -61,7 +61,7 @@ namespace osu.Desktop.Overlays }, new OsuSpriteText { - Colour = DebugUtils.IsDebug ? colours.Red : Color4.White, + Colour = DebugUtils.IsDebugBuild ? colours.Red : Color4.White, Text = game.Version }, } diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 2f88a4b01d..06414af865 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -39,8 +40,7 @@ namespace osu.Game.Tests.Visual.Online { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Both, Width = 0.8f, Children = new Drawable[] { @@ -173,7 +173,9 @@ namespace osu.Game.Tests.Visual.Online s.Statistics.Add(HitResult.Miss, RNG.Next(2000)); } - scoresContainer.Scores = scores; + AddStep("Load all scores", () => scoresContainer.Scores = scores); + AddStep("Load null scores", () => scoresContainer.Scores = null); + AddStep("Load only one score", () => scoresContainer.Scores = new[] { scores.First() }); } [BackgroundDependencyLoader] diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index d9230090fc..2285c9b799 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -39,13 +39,27 @@ namespace osu.Game.Tests.Visual.Online header = new ProfileHeader(); Add(header); - AddStep("Show offline dummy", () => header.User.Value = TestSceneUserProfileOverlay.TEST_USER); + AddStep("Show test dummy", () => header.User.Value = TestSceneUserProfileOverlay.TEST_USER); AddStep("Show null dummy", () => header.User.Value = new User { Username = "Null" }); + AddStep("Show online dummy", () => header.User.Value = new User + { + Username = "IAmOnline", + LastVisit = DateTimeOffset.Now, + IsOnline = true, + }); + + AddStep("Show offline dummy", () => header.User.Value = new User + { + Username = "IAmOffline", + LastVisit = DateTimeOffset.Now, + IsOnline = false, + }); + addOnlineStep("Show ppy", new User { Username = @"peppy", diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMods.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs similarity index 95% rename from osu.Game.Tests/Visual/UserInterface/TestSceneMods.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 2e36ba39ed..5a903b9417 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMods.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -24,11 +24,10 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface { [Description("mod select and icon display")] - public class TestSceneMods : OsuTestScene + public class TestSceneModSelectOverlay : OsuTestScene { public override IReadOnlyList RequiredTypes => new[] { - typeof(ModSelectOverlay), typeof(ModDisplay), typeof(ModSection), typeof(ModIcon), @@ -217,13 +216,13 @@ namespace osu.Game.Tests.Visual.UserInterface private void testRankedText(Mod mod) { - AddWaitStep("wait for fade", 1); + waitForLoad(); AddAssert("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0); selectNext(mod); - AddWaitStep("wait for fade", 1); + waitForLoad(); AddAssert("check for unranked", () => modSelect.UnrankedLabel.Alpha != 0); selectPrevious(mod); - AddWaitStep("wait for fade", 1); + waitForLoad(); AddAssert("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0); } @@ -233,6 +232,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void checkSelected(Mod mod) { + waitForLoad(); AddAssert($"check {mod.Name} is selected", () => { var button = modSelect.GetModButton(mod); @@ -240,8 +240,14 @@ namespace osu.Game.Tests.Visual.UserInterface }); } + private void waitForLoad() + { + AddUntilStep("wait for icons to load", () => modSelect.AllLoaded); + } + private void checkNotSelected(Mod mod) { + waitForLoad(); AddAssert($"check {mod.Name} is not selected", () => { var button = modSelect.GetModButton(mod); @@ -255,6 +261,8 @@ namespace osu.Game.Tests.Visual.UserInterface { public new Bindable> SelectedMods => base.SelectedMods; + public bool AllLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded); + public ModButton GetModButton(Mod mod) { var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 6b7427cef5..d8a4514df1 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -18,9 +18,6 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneNotificationOverlay : OsuTestScene { - private readonly NotificationOverlay manager; - private readonly List progressingNotifications = new List(); - public override IReadOnlyList RequiredTypes => new[] { typeof(NotificationSection), @@ -31,25 +28,33 @@ namespace osu.Game.Tests.Visual.UserInterface typeof(Notification) }; - public TestSceneNotificationOverlay() + private NotificationOverlay notificationOverlay; + + private readonly List progressingNotifications = new List(); + + private SpriteText displayedCount; + + [SetUp] + public void SetUp() => Schedule(() => { progressingNotifications.Clear(); - Content.Add(manager = new NotificationOverlay + Content.Children = new Drawable[] { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }); + notificationOverlay = new NotificationOverlay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }, + displayedCount = new OsuSpriteText() + }; - SpriteText displayedCount = new OsuSpriteText(); - - Content.Add(displayedCount); - - void setState(Visibility state) => AddStep(state.ToString(), () => manager.State.Value = state); - void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected); - - manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; }; + notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; }; + }); + [Test] + public void TestBasicFlow() + { setState(Visibility.Visible); AddStep(@"simple #1", sendHelloNotification); AddStep(@"simple #2", sendAmazingNotification); @@ -61,6 +66,7 @@ namespace osu.Game.Tests.Visual.UserInterface setState(Visibility.Hidden); AddRepeatStep(@"add many simple", sendManyNotifications, 3); + AddWaitStep("wait some", 5); checkProgressingCount(0); @@ -69,18 +75,122 @@ namespace osu.Game.Tests.Visual.UserInterface checkProgressingCount(1); - AddAssert("Displayed count is 33", () => manager.UnreadCount.Value == 33); + checkDisplayedCount(33); AddWaitStep("wait some", 10); checkProgressingCount(0); - - setState(Visibility.Visible); - - //AddStep(@"barrage", () => sendBarrage()); } - private void sendBarrage(int remaining = 10) + [Test] + public void TestImportantWhileClosed() + { + AddStep(@"simple #1", sendHelloNotification); + + AddAssert("Is visible", () => notificationOverlay.State.Value == Visibility.Visible); + + checkDisplayedCount(1); + + AddStep(@"progress #1", sendUploadProgress); + AddStep(@"progress #2", sendDownloadProgress); + + checkProgressingCount(2); + checkDisplayedCount(3); + } + + [Test] + public void TestUnimportantWhileClosed() + { + AddStep(@"background #1", sendBackgroundNotification); + + AddAssert("Is not visible", () => notificationOverlay.State.Value == Visibility.Hidden); + + checkDisplayedCount(1); + + AddStep(@"background progress #1", sendBackgroundUploadProgress); + + AddWaitStep("wait some", 5); + + checkProgressingCount(0); + + checkDisplayedCount(2); + + AddStep(@"simple #1", sendHelloNotification); + + checkDisplayedCount(3); + } + + [Test] + public void TestSpam() + { + setState(Visibility.Visible); + AddRepeatStep("send barrage", sendBarrage, 10); + } + + protected override void Update() + { + base.Update(); + + progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed); + + if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3) + { + var p = progressingNotifications.Find(n => n.State == ProgressNotificationState.Queued); + + if (p != null) + p.State = ProgressNotificationState.Active; + } + + foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active)) + { + if (n.Progress < 1) + n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle(); + else + n.State = ProgressNotificationState.Completed; + } + } + + private void checkDisplayedCount(int expected) => + AddAssert($"Displayed count is {expected}", () => notificationOverlay.UnreadCount.Value == expected); + + private void sendDownloadProgress() + { + var n = new ProgressNotification + { + Text = @"Downloading Haitai...", + CompletionText = "Downloaded Haitai!", + }; + notificationOverlay.Post(n); + progressingNotifications.Add(n); + } + + private void sendUploadProgress() + { + var n = new ProgressNotification + { + Text = @"Uploading to BSS...", + CompletionText = "Uploaded to BSS!", + }; + notificationOverlay.Post(n); + progressingNotifications.Add(n); + } + + private void sendBackgroundUploadProgress() + { + var n = new BackgroundProgressNotification + { + Text = @"Uploading to BSS...", + CompletionText = "Uploaded to BSS!", + }; + notificationOverlay.Post(n); + progressingNotifications.Add(n); + } + + private void setState(Visibility state) => AddStep(state.ToString(), () => notificationOverlay.State.Value = state); + + private void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected); + + private void sendBarrage() { switch (RNG.Next(0, 4)) { @@ -100,69 +210,37 @@ namespace osu.Game.Tests.Visual.UserInterface sendDownloadProgress(); break; } - - if (remaining > 0) - Scheduler.AddDelayed(() => sendBarrage(remaining - 1), 80); - } - - protected override void Update() - { - base.Update(); - - progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed); - - if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3) - { - var p = progressingNotifications.Find(n => n.State == ProgressNotificationState.Queued); - if (p != null) - p.State = ProgressNotificationState.Active; - } - - foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active)) - { - if (n.Progress < 1) - n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle(); - else - n.State = ProgressNotificationState.Completed; - } - } - - private void sendDownloadProgress() - { - var n = new ProgressNotification - { - Text = @"Downloading Haitai...", - CompletionText = "Downloaded Haitai!", - }; - manager.Post(n); - progressingNotifications.Add(n); - } - - private void sendUploadProgress() - { - var n = new ProgressNotification - { - Text = @"Uploading to BSS...", - CompletionText = "Uploaded to BSS!", - }; - manager.Post(n); - progressingNotifications.Add(n); } private void sendAmazingNotification() { - manager.Post(new SimpleNotification { Text = @"You are amazing" }); + notificationOverlay.Post(new SimpleNotification { Text = @"You are amazing" }); } private void sendHelloNotification() { - manager.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" }); + notificationOverlay.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" }); + } + + private void sendBackgroundNotification() + { + notificationOverlay.Post(new BackgroundNotification { Text = @"Welcome to osu!. Enjoy your stay!" }); } private void sendManyNotifications() { for (int i = 0; i < 10; i++) - manager.Post(new SimpleNotification { Text = @"Spam incoming!!" }); + notificationOverlay.Post(new SimpleNotification { Text = @"Spam incoming!!" }); + } + + private class BackgroundNotification : SimpleNotification + { + public override bool IsImportant => false; + } + + private class BackgroundProgressNotification : ProgressNotification + { + public override bool IsImportant => false; } } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 3734c8d05c..dd7b3d77bf 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -363,7 +363,7 @@ namespace osu.Game.Beatmaps foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu"))) { using (var raw = reader.GetStream(name)) - using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit + using (var ms = new MemoryStream()) //we need a memory stream so we can seek using (var sr = new StreamReader(ms)) { raw.CopyTo(ms); diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index ae4a056033..9dab2f2aba 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -130,6 +130,11 @@ namespace osu.Game.Online.Chat public StandAloneDrawableChannel(Channel channel) : base(channel) + { + } + + [BackgroundDependencyLoader] + private void load() { ChatLineFlow.Padding = new MarginPadding { Horizontal = 0 }; } diff --git a/osu.Game/Online/Leaderboards/DrawableRank.cs b/osu.Game/Online/Leaderboards/DrawableRank.cs index ce64395dde..9bbaa28e2a 100644 --- a/osu.Game/Online/Leaderboards/DrawableRank.cs +++ b/osu.Game/Online/Leaderboards/DrawableRank.cs @@ -3,72 +3,44 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Scoring; +using System; namespace osu.Game.Online.Leaderboards { - public class DrawableRank : Container + public class DrawableRank : Sprite { - private readonly Sprite rankSprite; - private TextureStore textures; - - public ScoreRank Rank { get; private set; } + private readonly ScoreRank rank; public DrawableRank(ScoreRank rank) { - Rank = rank; - - Children = new Drawable[] - { - rankSprite = new Sprite - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fit - }, - }; + this.rank = rank; } - [BackgroundDependencyLoader] - private void load(TextureStore textures) + [BackgroundDependencyLoader(true)] + private void load(TextureStore ts) { - this.textures = textures; - updateTexture(); + if (ts == null) + throw new ArgumentNullException(nameof(ts)); + + Texture = ts.Get($@"Grades/{getTextureName()}"); } - private void updateTexture() + private string getTextureName() { - string textureName; - - switch (Rank) + switch (rank) { default: - textureName = Rank.GetDescription(); - break; + return rank.GetDescription(); case ScoreRank.SH: - textureName = "SPlus"; - break; + return "SPlus"; case ScoreRank.XH: - textureName = "SSPlus"; - break; + return "SSPlus"; } - - rankSprite.Texture = textures.Get($@"Grades/{textureName}"); - } - - public void UpdateRank(ScoreRank newRank) - { - Rank = newRank; - - if (LoadState >= LoadState.Ready) - updateTexture(); } } } diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index c6db939f6b..9840b59805 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -17,7 +17,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.UI; using osu.Game.Scoring; -using osu.Game.Users; +using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; @@ -65,7 +65,7 @@ namespace osu.Game.Online.Leaderboards statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s)).ToList(); - Avatar innerAvatar; + DrawableAvatar innerAvatar; Children = new Drawable[] { @@ -112,7 +112,7 @@ namespace osu.Game.Online.Leaderboards Children = new[] { avatar = new DelayedLoadWrapper( - innerAvatar = new Avatar(user) + innerAvatar = new DrawableAvatar(user) { RelativeSizeAxes = Axes.Both, CornerRadius = corner_radius, @@ -157,7 +157,7 @@ namespace osu.Game.Online.Leaderboards Masking = true, Children = new Drawable[] { - new DrawableFlag(user.Country) + new UpdateableFlag(user.Country) { Width = 30, RelativeSizeAxes = Axes.Y, @@ -193,7 +193,7 @@ namespace osu.Game.Online.Leaderboards Size = new Vector2(40f, 20f), Children = new[] { - scoreRank = new DrawableRank(score.Rank) + scoreRank = new UpdateableRank(score.Rank) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Online/Leaderboards/UpdateableRank.cs b/osu.Game/Online/Leaderboards/UpdateableRank.cs new file mode 100644 index 0000000000..64230a92db --- /dev/null +++ b/osu.Game/Online/Leaderboards/UpdateableRank.cs @@ -0,0 +1,31 @@ +// 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.Game.Scoring; + +namespace osu.Game.Online.Leaderboards +{ + public class UpdateableRank : ModelBackedDrawable + { + public ScoreRank Rank + { + get => Model; + set => Model = value; + } + + public UpdateableRank(ScoreRank rank) + { + Rank = rank; + } + + protected override Drawable CreateDrawable(ScoreRank rank) => new DrawableRank(rank) + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fit, + }; + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f38eecef81..692e7189a3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -184,6 +184,8 @@ namespace osu.Game LocalConfig.BindWith(OsuSetting.VolumeInactive, userInactiveVolume); IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true); + + Beatmap.BindValueChanged(beatmapChanged, true); } private ExternalLinkOpener externalLinkOpener; @@ -284,6 +286,23 @@ namespace osu.Game }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true); } + #region Beatmap jukebox progression + + private void beatmapChanged(ValueChangedEvent beatmap) + { + var nextBeatmap = beatmap.NewValue; + if (nextBeatmap?.Track != null) + nextBeatmap.Track.Completed += currentTrackCompleted; + } + + private void currentTrackCompleted() + { + if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled) + musicController.NextTrack(); + } + + #endregion + private ScheduledDelegate performFromMainMenuTask; /// @@ -446,7 +465,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(new MusicController + loadComponentSingleFile(musicController = new MusicController { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, @@ -719,8 +738,11 @@ namespace osu.Game private Container topMostOverlayContent; private FrameworkConfigManager frameworkConfig; + private ScalingContainer screenContainer; + private MusicController musicController; + protected override bool OnExiting() { if (screenStack.CurrentScreen is Loader) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 637708a0e5..87ff721bbb 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Development; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.IO.Stores; @@ -34,7 +35,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Skinning; using osuTK.Input; -using DebugUtils = osu.Game.Utils.DebugUtils; namespace osu.Game { @@ -97,7 +97,7 @@ namespace osu.Game get { if (!IsDeployedBuild) - return @"local " + (DebugUtils.IsDebug ? @"debug" : @"release"); + return @"local " + (DebugUtils.IsDebugBuild ? @"debug" : @"release"); var version = AssemblyVersion; return $@"{version.Major}.{version.Minor}.{version.Build}"; diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs index 7331faa618..096e91b65b 100644 --- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs +++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; -using osu.Game.Users; +using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Effects; diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index a0f71d05c0..fb6c50d867 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -45,7 +45,7 @@ namespace osu.Game.Overlays.BeatmapSet ExternalLinkButton externalLink; RelativeSizeAxes = Axes.X; - Height = 400; + AutoSizeAxes = Axes.Y; Masking = true; EdgeEffect = new EdgeEffectParameters @@ -72,7 +72,8 @@ namespace osu.Game.Overlays.BeatmapSet }, new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Top = tabs_height }, Children = new Drawable[] { @@ -84,6 +85,7 @@ namespace osu.Game.Overlays.BeatmapSet cover = new UpdateableBeatmapSetCover { RelativeSizeAxes = Axes.Both, + Masking = true, }, new Box { @@ -94,18 +96,26 @@ namespace osu.Game.Overlays.BeatmapSet }, new Container { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 20, Bottom = 30, Horizontal = BeatmapSetOverlay.X_PADDING }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Top = 20, + Bottom = 30, + Left = BeatmapSetOverlay.X_PADDING, + Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH, + }, Child = new FillFlowContainer { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Children = new Drawable[] { new Container { RelativeSizeAxes = Axes.X, - Height = 113, + AutoSizeAxes = Axes.Y, Child = Picker = new BeatmapPicker(), }, new FillFlowContainer @@ -158,7 +168,7 @@ namespace osu.Game.Overlays.BeatmapSet Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Right = BeatmapSetOverlay.X_PADDING }, + Margin = new MarginPadding { Top = BeatmapSetOverlay.TOP_PADDING, Right = BeatmapSetOverlay.X_PADDING }, Direction = FillDirection.Vertical, Spacing = new Vector2(10), Children = new Drawable[] diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 693ce958dd..15816be327 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; using osu.Game.Rulesets.UI; using osu.Game.Scoring; -using osu.Game.Users; +using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; @@ -103,7 +103,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Text = $"#{index + 1}", Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) }, - new DrawableRank(score.Rank) + new UpdateableRank(score.Rank) { Size = new Vector2(30, 20) }, @@ -135,7 +135,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Spacing = new Vector2(5, 0), Children = new Drawable[] { - new DrawableFlag(score.User.Country) { Size = new Vector2(20, 13) }, + new UpdateableFlag(score.User.Country) + { + Size = new Vector2(20, 13), + ShowPlaceholderOnNull = false, + }, username } }, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 8ef3f71fe3..3e6c938802 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -124,6 +124,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores loading = false; scoreTable.Scores = scores?.Count > 1 ? scores : new List(); + scoreTable.FadeTo(scores?.Count > 1 ? 1 : 0); if (scores?.Any() == true) { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index cbcf3e6160..5ee143850f 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; using osu.Game.Scoring; -using osu.Game.Users; +using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; @@ -22,11 +22,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores public class TopScoreUserSection : CompositeDrawable { private readonly SpriteText rankText; - private readonly DrawableRank rank; + private readonly UpdateableRank rank; private readonly UpdateableAvatar avatar; private readonly LinkFlowContainer usernameText; private readonly SpriteText date; - private readonly DrawableFlag flag; + private readonly UpdateableFlag flag; public TopScoreUserSection() { @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Text = "#1", Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) }, - rank = new DrawableRank(ScoreRank.D) + rank = new UpdateableRank(ScoreRank.D) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -67,6 +67,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Offset = new Vector2(0, 2), Radius = 1, }, + ShowGuestOnNull = false, }, new FillFlowContainer { @@ -89,11 +90,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold) }, - flag = new DrawableFlag + flag = new UpdateableFlag { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Size = new Vector2(20, 13), + ShowPlaceholderOnNull = false, }, } } @@ -121,7 +123,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores usernameText.Clear(); usernameText.AddUserLink(value.User); - rank.UpdateRank(value.Rank); + rank.Rank = value.Rank; } } } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 1e687267a3..205909ce7d 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -24,6 +24,7 @@ namespace osu.Game.Overlays private const int fade_duration = 300; public const float X_PADDING = 40; + public const float TOP_PADDING = 25; public const float RIGHT_WIDTH = 275; private readonly Header header; diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 86bbe91d35..e29216dffd 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -74,17 +74,12 @@ namespace osu.Game.Overlays.Chat } } + private bool senderHasBackground => !string.IsNullOrEmpty(message.Sender.Colour); + [BackgroundDependencyLoader] private void load(OsuColour colours) { customUsernameColour = colours.ChatBlue; - } - - private bool senderHasBackground => !string.IsNullOrEmpty(message.Sender.Colour); - - protected override void LoadComplete() - { - base.LoadComplete(); bool hasBackground = senderHasBackground; @@ -179,6 +174,11 @@ namespace osu.Game.Overlays.Chat }; updateMessageContent(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); FinishTransforms(true); } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 7d28df3210..8d56e250fc 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using osu.Framework.Allocation; using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -17,15 +18,18 @@ namespace osu.Game.Overlays.Chat public class DrawableChannel : Container { public readonly Channel Channel; - protected readonly ChatLineContainer ChatLineFlow; - private readonly OsuScrollContainer scroll; + protected ChatLineContainer ChatLineFlow; + private OsuScrollContainer scroll; public DrawableChannel(Channel channel) { Channel = channel; - RelativeSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] + private void load() + { Children = new Drawable[] { scroll = new OsuScrollContainer @@ -48,18 +52,17 @@ namespace osu.Game.Overlays.Chat }, } }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); newMessagesArrived(Channel.Messages); Channel.NewMessagesArrived += newMessagesArrived; Channel.MessageRemoved += messageRemoved; Channel.PendingMessageResolved += pendingMessageResolved; + } + protected override void LoadComplete() + { + base.LoadComplete(); scrollToEnd(); } diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index b8165e70cb..9e87bae864 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Online.Chat; -using osu.Game.Users; +using osu.Game.Users.Drawables; using osuTK; namespace osu.Game.Overlays.Chat.Tabs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Chat.Tabs if (value.Type != ChannelType.PM) throw new ArgumentException("Argument value needs to have the targettype user!"); - Avatar avatar; + DrawableAvatar avatar; AddRange(new Drawable[] { @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Chat.Tabs Anchor = Anchor.Centre, Origin = Anchor.Centre, Masking = true, - Child = new DelayedLoadWrapper(avatar = new Avatar(value.Users.First()) + Child = new DelayedLoadWrapper(avatar = new DrawableAvatar(value.Users.First()) { RelativeSizeAxes = Axes.Both, OpenOnClick = { Value = false }, diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index dd48a5d29e..e6f2983488 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -31,12 +31,13 @@ namespace osu.Game.Overlays private ChannelManager channelManager; - private readonly Container currentChannelContainer; + private Container currentChannelContainer; + private readonly List loadedChannels = new List(); - private readonly LoadingAnimation loading; + private LoadingAnimation loading; - private readonly FocusedTextBox textbox; + private FocusedTextBox textbox; private const int transition_length = 500; @@ -44,17 +45,17 @@ namespace osu.Game.Overlays public const float TAB_AREA_HEIGHT = 50; - private readonly ChannelTabControl channelTabControl; + private ChannelTabControl channelTabControl; - private readonly Container chatContainer; - private readonly TabsArea tabsArea; - private readonly Box chatBackground; - private readonly Box tabBackground; + private Container chatContainer; + private TabsArea tabsArea; + private Box chatBackground; + private Box tabBackground; public Bindable ChatHeight { get; set; } - private readonly Container channelSelectionContainer; - private readonly ChannelSelectionOverlay channelSelectionOverlay; + private Container channelSelectionContainer; + private ChannelSelectionOverlay channelSelectionOverlay; public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || (channelSelectionOverlay.State.Value == Visibility.Visible && channelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos)); @@ -64,7 +65,11 @@ namespace osu.Game.Overlays RelativePositionAxes = Axes.Both; Anchor = Anchor.BottomLeft; Origin = Anchor.BottomLeft; + } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config, OsuColour colours, ChannelManager channelManager) + { const float padding = 5; Children = new Drawable[] @@ -154,7 +159,7 @@ namespace osu.Game.Overlays Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, - OnRequestLeave = channel => channelManager.LeaveChannel(channel) + OnRequestLeave = channelManager.LeaveChannel }, } }, @@ -186,9 +191,45 @@ namespace osu.Game.Overlays }; channelSelectionOverlay.OnRequestJoin = channel => channelManager.JoinChannel(channel); - channelSelectionOverlay.OnRequestLeave = channel => channelManager.LeaveChannel(channel); + channelSelectionOverlay.OnRequestLeave = channelManager.LeaveChannel; + + ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); + ChatHeight.ValueChanged += height => + { + chatContainer.Height = (float)height.NewValue; + channelSelectionContainer.Height = 1f - (float)height.NewValue; + tabBackground.FadeTo(height.NewValue == 1 ? 1 : 0.8f, 200); + }; + ChatHeight.TriggerChange(); + + chatBackground.Colour = colours.ChatBlue; + + this.channelManager = channelManager; + + loading.Show(); + + // This is a relatively expensive (and blocking) operation. + // Scheduling it ensures that it won't be performed unless the user decides to open chat. + // TODO: Refactor OsuFocusedOverlayContainer / OverlayContainer to support delayed content loading. + Schedule(() => + { + // TODO: consider scheduling bindable callbacks to not perform when overlay is not present. + channelManager.JoinedChannels.ItemsAdded += onChannelAddedToJoinedChannels; + channelManager.JoinedChannels.ItemsRemoved += onChannelRemovedFromJoinedChannels; + foreach (Channel channel in channelManager.JoinedChannels) + channelTabControl.AddChannel(channel); + + channelManager.AvailableChannels.ItemsAdded += availableChannelsChanged; + channelManager.AvailableChannels.ItemsRemoved += availableChannelsChanged; + channelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels); + + currentChannel = channelManager.CurrentChannel.GetBoundCopy(); + currentChannel.BindValueChanged(currentChannelChanged, true); + }); } + private Bindable currentChannel; + private void currentChannelChanged(ValueChangedEvent e) { if (e.NewValue == null) @@ -331,35 +372,6 @@ namespace osu.Game.Overlays base.PopOut(); } - [BackgroundDependencyLoader] - private void load(OsuConfigManager config, OsuColour colours, ChannelManager channelManager) - { - ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); - ChatHeight.ValueChanged += height => - { - chatContainer.Height = (float)height.NewValue; - channelSelectionContainer.Height = 1f - (float)height.NewValue; - tabBackground.FadeTo(height.NewValue == 1 ? 1 : 0.8f, 200); - }; - ChatHeight.TriggerChange(); - - chatBackground.Colour = colours.ChatBlue; - - loading.Show(); - - this.channelManager = channelManager; - channelManager.CurrentChannel.ValueChanged += currentChannelChanged; - channelManager.JoinedChannels.ItemsAdded += onChannelAddedToJoinedChannels; - channelManager.JoinedChannels.ItemsRemoved += onChannelRemovedFromJoinedChannels; - channelManager.AvailableChannels.ItemsAdded += availableChannelsChanged; - channelManager.AvailableChannels.ItemsRemoved += availableChannelsChanged; - - //for the case that channelmanager was faster at fetching the channels than our attachment to CollectionChanged. - channelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels); - foreach (Channel channel in channelManager.JoinedChannels) - channelTabControl.AddChannel(channel); - } - private void onChannelAddedToJoinedChannels(IEnumerable channels) { foreach (Channel channel in channels) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 50400e254f..dedd397fa5 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mods; using System; using System.Linq; using System.Collections.Generic; +using System.Threading; using osu.Framework.Input.Events; using osu.Game.Graphics; @@ -33,6 +34,13 @@ namespace osu.Game.Overlays.Mods public IEnumerable SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null); + private CancellationTokenSource modsLoadCts; + + /// + /// True when all mod icons have completed loading. + /// + public bool ModIconsLoaded { get; private set; } = true; + public IEnumerable Mods { set @@ -48,8 +56,28 @@ namespace osu.Game.Overlays.Mods }; }).ToArray(); - ButtonsContainer.Children = modContainers; + modsLoadCts?.Cancel(); + ModIconsLoaded = false; + + LoadComponentsAsync(modContainers, c => + { + ModIconsLoaded = true; + ButtonsContainer.ChildrenEnumerable = c; + }, (modsLoadCts = new CancellationTokenSource()).Token); + buttons = modContainers.OfType().ToArray(); + + if (value.Any()) + { + headerLabel.FadeIn(200); + this.FadeIn(200); + } + else + { + // transition here looks weird as mods instantly disappear. + headerLabel.Hide(); + Hide(); + } } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 8e5c9588ce..9ff320841a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -172,6 +172,8 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.Y, Spacing = new Vector2(0f, 10f), Width = content_width, + LayoutDuration = 200, + LayoutEasing = Easing.OutQuint, Children = new ModSection[] { new DifficultyReductionSection { Action = modButtonPressed }, diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 8b9bac877b..36937def2b 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -70,9 +70,6 @@ namespace osu.Game.Overlays { Width = 400; Margin = new MarginPadding(10); - - // required to let MusicController handle beatmap cycling. - AlwaysPresent = true; } [BackgroundDependencyLoader] @@ -349,18 +346,11 @@ namespace osu.Game.Overlays direction = last > next ? TransformDirection.Prev : TransformDirection.Next; } - - //current.Track.Completed -= currentTrackCompleted; } - current = beatmap.NewValue; - - if (current != null) - current.Track.Completed += currentTrackCompleted; - progressBar.CurrentTime = 0; - updateDisplay(current, direction); + updateDisplay(current = beatmap.NewValue, direction); updateAudioAdjustments(); queuedDirection = null; @@ -378,12 +368,6 @@ namespace osu.Game.Overlays mod.ApplyToClock(track); } - private void currentTrackCompleted() => Schedule(() => - { - if (!current.Track.Looping && !beatmap.Disabled && beatmapSets.Any()) - next(); - }); - private ScheduledDelegate pendingBeatmapSwitch; private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction) @@ -447,10 +431,6 @@ namespace osu.Game.Overlays { base.PopOut(); - // This is here mostly as a performance fix. - // If the playlist is not hidden it will update children even when the music controller is hidden (due to AlwaysPresent). - playlist.Hide(); - this.FadeOut(transition_length, Easing.OutQuint); dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint); } @@ -548,5 +528,10 @@ namespace osu.Game.Overlays return base.OnDragEnd(e); } } + + /// + /// Play the next random or playlist track. + /// + public void NextTrack() => next(); } } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 2e4c504645..320aa707e2 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -35,8 +35,6 @@ namespace osu.Game.Overlays Width = width; RelativeSizeAxes = Axes.Y; - AlwaysPresent = true; - Children = new Drawable[] { new Box @@ -100,9 +98,6 @@ namespace osu.Game.Overlays OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true); } - private int totalCount => sections.Select(c => c.DisplayedCount).Sum(); - private int unreadCount => sections.Select(c => c.UnreadCount).Sum(); - public readonly BindableInt UnreadCount = new BindableInt(); private int runningDepth; @@ -111,6 +106,8 @@ namespace osu.Game.Overlays private readonly Scheduler postScheduler = new Scheduler(); + public override bool IsPresent => base.IsPresent || postScheduler.HasPendingTasks; + private bool processingPosts = true; public void Post(Notification notification) => postScheduler.Add(() => @@ -160,7 +157,7 @@ namespace osu.Game.Overlays private void updateCounts() { - UnreadCount.Value = unreadCount; + UnreadCount.Value = sections.Select(c => c.UnreadCount).Sum(); } private void markAllRead() diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index c8e081d29f..99836705c4 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -23,10 +23,16 @@ namespace osu.Game.Overlays.Notifications public string CompletionText { get; set; } = "Task has completed!"; + private float progress; + public float Progress { - get => progressBar.Progress; - set => Schedule(() => progressBar.Progress = value); + get => progress; + set + { + progress = value; + Scheduler.AddOnce(() => progressBar.Progress = progress); + } } protected override void LoadComplete() @@ -34,59 +40,56 @@ namespace osu.Game.Overlays.Notifications base.LoadComplete(); //we may have received changes before we were displayed. - State = state; + updateState(); } private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); public CancellationToken CancellationToken => cancellationTokenSource.Token; - public virtual ProgressNotificationState State + public ProgressNotificationState State { get => state; - set => - Schedule(() => - { - bool stateChanged = state != value; - state = value; + set + { + if (state == value) return; - if (IsLoaded) - { - switch (state) - { - case ProgressNotificationState.Queued: - Light.Colour = colourQueued; - Light.Pulsate = false; - progressBar.Active = false; - break; + state = value; - case ProgressNotificationState.Active: - Light.Colour = colourActive; - Light.Pulsate = true; - progressBar.Active = true; - break; + if (IsLoaded) + Schedule(updateState); + } + } - case ProgressNotificationState.Cancelled: - cancellationTokenSource.Cancel(); + private void updateState() + { + switch (state) + { + case ProgressNotificationState.Queued: + Light.Colour = colourQueued; + Light.Pulsate = false; + progressBar.Active = false; + break; - Light.Colour = colourCancelled; - Light.Pulsate = false; - progressBar.Active = false; - break; - } - } + case ProgressNotificationState.Active: + Light.Colour = colourActive; + Light.Pulsate = true; + progressBar.Active = true; + break; - if (stateChanged) - { - switch (state) - { - case ProgressNotificationState.Completed: - NotificationContent.MoveToY(-DrawSize.Y / 2, 200, Easing.OutQuint); - this.FadeOut(200).Finally(d => Completed()); - break; - } - } - }); + case ProgressNotificationState.Cancelled: + cancellationTokenSource.Cancel(); + + Light.Colour = colourCancelled; + Light.Pulsate = false; + progressBar.Active = false; + break; + + case ProgressNotificationState.Completed: + NotificationContent.MoveToY(-DrawSize.Y / 2, 200, Easing.OutQuint); + this.FadeOut(200).Finally(d => Completed()); + break; + } } private ProgressNotificationState state; diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index ffbb9ad218..e7f7c2f490 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -87,7 +87,12 @@ namespace osu.Game.Overlays.Profile.Header addSpacer(topLinkContainer); - if (user.LastVisit.HasValue) + if (user.IsOnline) + { + topLinkContainer.AddText("Currently online"); + addSpacer(topLinkContainer); + } + else if (user.LastVisit.HasValue) { topLinkContainer.AddText("Last seen "); topLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), embolden); diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index f26cc360a2..afafc120e4 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -200,7 +200,7 @@ namespace osu.Game.Overlays.Profile.Header Direction = FillDirection.Vertical, Children = new Drawable[] { - new DrawableRank(rank) + new UpdateableRank(rank) { RelativeSizeAxes = Axes.X, Height = 30, diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 6fe55e2368..dc6c3f56f8 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Users; +using osu.Game.Users.Drawables; using osuTK; namespace osu.Game.Overlays.Profile.Header @@ -27,7 +28,7 @@ namespace osu.Game.Overlays.Profile.Header private OsuSpriteText usernameText; private ExternalLinkButton openUserExternally; private OsuSpriteText titleText; - private DrawableFlag userFlag; + private UpdateableFlag userFlag; private OsuSpriteText userCountryText; private FillFlowContainer userStats; @@ -51,7 +52,7 @@ namespace osu.Game.Overlays.Profile.Header AutoSizeAxes = Axes.X, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Children = new[] + Children = new Drawable[] { avatar = new UpdateableAvatar { @@ -59,6 +60,7 @@ namespace osu.Game.Overlays.Profile.Header Masking = true, CornerRadius = avatar_size * 0.25f, OpenOnClick = { Value = false }, + ShowGuestOnNull = false, }, new Container { @@ -115,9 +117,10 @@ namespace osu.Game.Overlays.Profile.Header Margin = new MarginPadding { Top = 5 }, Children = new Drawable[] { - userFlag = new DrawableFlag + userFlag = new UpdateableFlag { - Size = new Vector2(30, 20) + Size = new Vector2(30, 20), + ShowPlaceholderOnNull = false, }, userCountryText = new OsuSpriteText { diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index f4e08b8db1..0a90c9b135 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) }); } - protected override Drawable CreateLeftVisual() => new DrawableRank(Score.Rank) + protected override Drawable CreateLeftVisual() => new UpdateableRank(Score.Rank) { RelativeSizeAxes = Axes.Y, Width = 60, diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs index 8fab29e42c..b5a508bff7 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent switch (activity.Type) { case RecentActivityType.Rank: - return new DrawableRank(activity.ScoreRank) + return new UpdateableRank(activity.ScoreRank) { RelativeSizeAxes = Axes.Y, Width = 60, diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index b5ee4b4f0c..a815480094 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Development; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -12,7 +13,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; -using DebugUtils = osu.Game.Utils.DebugUtils; namespace osu.Game.Overlays.Settings { @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Settings Text = game.Name, Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold), }, - new BuildDisplay(game.Version, DebugUtils.IsDebug) + new BuildDisplay(game.Version, DebugUtils.IsDebugBuild) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index ea15e5498b..bccef3d9fe 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Effects; using osu.Game.Graphics; using osu.Game.Online.API; using osu.Game.Users; +using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index cf5d247482..88537322ad 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Menu Scheduler.AddDelayed(this.Exit, fadeOutTime); - //don't want to fade out completely else we will stop running updates and shit will hit the fan. + //don't want to fade out completely else we will stop running updates. Game.FadeTo(0.01f, fadeOutTime); base.OnResuming(last); diff --git a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs b/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs index 6570051040..a55db096af 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Users; +using osu.Game.Users.Drawables; using osuTK; namespace osu.Game.Screens.Multi.Lounge.Components @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components hostText.AddText("hosted by "); hostText.AddUserLink(host.NewValue, s => s.Font = s.Font.With(Typeface.Exo, weight: FontWeight.Bold, italics: true)); - flagContainer.Child = new DrawableFlag(host.NewValue.Country) { RelativeSizeAxes = Axes.Both }; + flagContainer.Child = new UpdateableFlag(host.NewValue.Country) { RelativeSizeAxes = Axes.Both }; } }, true); diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index 1a18f742a9..5030d8cb50 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -19,6 +19,7 @@ using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Components; using osu.Game.Users; +using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; diff --git a/osu.Game/Screens/Multi/Match/Components/HostInfo.cs b/osu.Game/Screens/Multi/Match/Components/HostInfo.cs index b898cd0466..8851a96605 100644 --- a/osu.Game/Screens/Multi/Match/Components/HostInfo.cs +++ b/osu.Game/Screens/Multi/Match/Components/HostInfo.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Users; +using osu.Game.Users.Drawables; using osuTK; namespace osu.Game.Screens.Multi.Match.Components diff --git a/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs index fab227c7f4..a82156e34e 100644 --- a/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs +++ b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Ranking.Pages RelativeSizeAxes = Axes.X, Height = user_header_height, }, - new DrawableRank(Score.Rank) + new UpdateableRank(Score.Rank) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 51ca9902d2..4ceb82d4cc 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -143,10 +143,10 @@ namespace osu.Game.Screens.Select.Carousel Origin = Anchor.Centre, FillMode = FillMode.Fill, }, - new FillFlowContainer + // Todo: This should be a fill flow, but has invalidation issues (see https://github.com/ppy/osu-framework/issues/223) + new Container { Depth = -1, - Direction = FillDirection.Horizontal, RelativeSizeAxes = Axes.Both, // This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle Shear = new Vector2(0.8f, 0), @@ -157,6 +157,7 @@ namespace osu.Game.Screens.Select.Carousel new Box { RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, Colour = Color4.Black, Width = 0.4f, }, @@ -164,20 +165,26 @@ namespace osu.Game.Screens.Select.Carousel new Box { RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, Colour = ColourInfo.GradientHorizontal(Color4.Black, new Color4(0f, 0f, 0f, 0.9f)), Width = 0.05f, + X = 0.4f, }, new Box { RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.9f), new Color4(0f, 0f, 0f, 0.1f)), Width = 0.2f, + X = 0.45f, }, new Box { RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.1f), new Color4(0, 0, 0, 0)), Width = 0.05f, + X = 0.65f, }, } }, diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs index b513b460bc..1dcce6e870 100644 --- a/osu.Game/Users/Country.cs +++ b/osu.Game/Users/Country.cs @@ -1,14 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using Newtonsoft.Json; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; namespace osu.Game.Users { @@ -26,54 +19,4 @@ namespace osu.Game.Users [JsonProperty(@"code")] public string FlagName; } - - public class DrawableFlag : Container, IHasTooltip - { - private readonly Sprite sprite; - private TextureStore textures; - - private Country country; - - public Country Country - { - get => country; - set - { - if (value == country) - return; - - country = value; - - if (LoadState >= LoadState.Ready) - sprite.Texture = getFlagTexture(); - } - } - - public string TooltipText => country?.FullName; - - public DrawableFlag(Country country = null) - { - this.country = country; - - Children = new Drawable[] - { - sprite = new Sprite - { - RelativeSizeAxes = Axes.Both, - }, - }; - } - - [BackgroundDependencyLoader] - private void load(TextureStore ts) - { - if (ts == null) - throw new ArgumentNullException(nameof(ts)); - - textures = ts; - sprite.Texture = getFlagTexture(); - } - - private Texture getFlagTexture() => textures.Get($@"Flags/{country?.FlagName ?? @"__"}"); - } } diff --git a/osu.Game/Users/Avatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs similarity index 95% rename from osu.Game/Users/Avatar.cs rename to osu.Game/Users/Drawables/DrawableAvatar.cs index 8937f94768..ee3cf6331b 100644 --- a/osu.Game/Users/Avatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -11,9 +11,9 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; -namespace osu.Game.Users +namespace osu.Game.Users.Drawables { - public class Avatar : Container + public class DrawableAvatar : Container { /// /// Whether to open the user's profile when clicked. @@ -29,7 +29,7 @@ namespace osu.Game.Users /// An avatar for specified user. /// /// The user. A null value will get a placeholder avatar. - public Avatar(User user = null) + public DrawableAvatar(User user = null) { this.user = user; } diff --git a/osu.Game/Users/Drawables/DrawableFlag.cs b/osu.Game/Users/Drawables/DrawableFlag.cs new file mode 100644 index 0000000000..368354e48e --- /dev/null +++ b/osu.Game/Users/Drawables/DrawableFlag.cs @@ -0,0 +1,32 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Users.Drawables +{ + public class DrawableFlag : Sprite, IHasTooltip + { + private readonly Country country; + + public string TooltipText => country?.FullName; + + public DrawableFlag(Country country) + { + this.country = country; + } + + [BackgroundDependencyLoader] + private void load(TextureStore ts) + { + if (ts == null) + throw new ArgumentNullException(nameof(ts)); + + Texture = ts.Get($@"Flags/{country?.FlagName ?? @"__"}"); + } + } +} diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs new file mode 100644 index 0000000000..795b90ba11 --- /dev/null +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -0,0 +1,71 @@ +// 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.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; + +namespace osu.Game.Users.Drawables +{ + /// + /// An avatar which can update to a new user when needed. + /// + public class UpdateableAvatar : ModelBackedDrawable + { + public User User + { + get => Model; + set => Model = value; + } + + public new bool Masking + { + get => base.Masking; + set => base.Masking = value; + } + + public new float CornerRadius + { + get => base.CornerRadius; + set => base.CornerRadius = value; + } + + public new EdgeEffectParameters EdgeEffect + { + get => base.EdgeEffect; + set => base.EdgeEffect = value; + } + + /// + /// Whether to show a default guest representation on null user (as opposed to nothing). + /// + public bool ShowGuestOnNull = true; + + /// + /// Whether to open the user's profile when clicked. + /// + public readonly BindableBool OpenOnClick = new BindableBool(true); + + public UpdateableAvatar(User user = null) + { + User = user; + } + + protected override Drawable CreateDrawable(User user) + { + if (user == null && !ShowGuestOnNull) + return null; + + var avatar = new DrawableAvatar(user) + { + RelativeSizeAxes = Axes.Both, + }; + + avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint); + avatar.OpenOnClick.BindTo(OpenOnClick); + + return avatar; + } + } +} diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs new file mode 100644 index 0000000000..abc16b2390 --- /dev/null +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -0,0 +1,38 @@ +// 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; + +namespace osu.Game.Users.Drawables +{ + public class UpdateableFlag : ModelBackedDrawable + { + public Country Country + { + get => Model; + set => Model = value; + } + + /// + /// Whether to show a place holder on null country. + /// + public bool ShowPlaceholderOnNull = true; + + public UpdateableFlag(Country country = null) + { + Country = country; + } + + protected override Drawable CreateDrawable(Country country) + { + if (country == null && !ShowPlaceholderOnNull) + return null; + + return new DrawableFlag(country) + { + RelativeSizeAxes = Axes.Both, + }; + } + } +} diff --git a/osu.Game/Users/UpdateableAvatar.cs b/osu.Game/Users/UpdateableAvatar.cs deleted file mode 100644 index 7259468674..0000000000 --- a/osu.Game/Users/UpdateableAvatar.cs +++ /dev/null @@ -1,69 +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.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Users -{ - /// - /// An avatar which can update to a new user when needed. - /// - public class UpdateableAvatar : Container - { - private Drawable displayedAvatar; - - private User user; - - /// - /// Whether to show a default guest representation on null user (as opposed to nothing). - /// - public bool ShowGuestOnNull = true; - - public User User - { - get => user; - set - { - if (user?.Id == value?.Id) - return; - - user = value; - - if (IsLoaded) - updateAvatar(); - } - } - - /// - /// Whether to open the user's profile when clicked. - /// - public readonly BindableBool OpenOnClick = new BindableBool(true); - - protected override void LoadComplete() - { - base.LoadComplete(); - updateAvatar(); - } - - private void updateAvatar() - { - displayedAvatar?.FadeOut(300); - displayedAvatar?.Expire(); - - if (user != null || ShowGuestOnNull) - { - var avatar = new Avatar(user) - { - RelativeSizeAxes = Axes.Both, - }; - - avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint); - avatar.OpenOnClick.BindTo(OpenOnClick); - - Add(displayedAvatar = new DelayedLoadWrapper(avatar)); - } - } - } -} diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index c3ecd62e10..df41e194b0 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -78,6 +78,9 @@ namespace osu.Game.Users [JsonProperty(@"is_active")] public bool Active; + [JsonProperty(@"is_online")] + public bool IsOnline; + [JsonProperty(@"pm_friends_only")] public bool PMFriendsOnly; diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 4b2029e6fd..c63c12773e 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -20,6 +20,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Containers; using osu.Game.Overlays.Profile.Header.Components; +using osu.Game.Users.Drawables; namespace osu.Game.Users { @@ -137,7 +138,7 @@ namespace osu.Game.Users Spacing = new Vector2(5f, 0f), Children = new Drawable[] { - new DrawableFlag(user.Country) + new UpdateableFlag(user.Country) { Width = 30f, RelativeSizeAxes = Axes.Y, diff --git a/osu.Game/Utils/DebugUtils.cs b/osu.Game/Utils/DebugUtils.cs deleted file mode 100644 index 9b1d355a83..0000000000 --- a/osu.Game/Utils/DebugUtils.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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.Utils -{ - public static class DebugUtils - { - public static bool IsDebug - { - get - { - // ReSharper disable once RedundantAssignment - bool isDebug = false; - // Debug.Assert conditions are only evaluated in debug mode - System.Diagnostics.Debug.Assert(isDebug = true); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - return isDebug; - } - } - } -}