From 16d34bcc0a04b319a53d86f20a863a837eb8d73d Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 12 Apr 2021 16:02:19 -0400 Subject: [PATCH 001/108] Expose the latest end time of storyboard elements Co-authored-by: Marlina Bowring --- osu.Game/Storyboards/IStoryboardElement.cs | 2 ++ osu.Game/Storyboards/Storyboard.cs | 9 +++++++++ osu.Game/Storyboards/StoryboardSample.cs | 2 ++ osu.Game/Storyboards/StoryboardVideo.cs | 2 ++ 4 files changed, 15 insertions(+) diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index c4c150a8a4..03f8b97212 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -12,6 +12,8 @@ namespace osu.Game.Storyboards double StartTime { get; } + double EndTime { get; } + Drawable CreateDrawable(); } } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 1ba25cc11e..41058d9cb3 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -36,6 +36,15 @@ namespace osu.Game.Storyboards /// public double? EarliestEventTime => Layers.SelectMany(l => l.Elements).OrderBy(e => e.StartTime).FirstOrDefault()?.StartTime; + /// + /// Across all layers, find the latest point in time that a storyboard element ends at. + /// Will return null if there are no elements. + /// + /// + /// This iterates all elements and as such should be used sparingly or stored locally. + /// Videos and samples return StartTime as their EndTIme. + /// + public double? LatestEventTime => Layers.SelectMany(l => l.Elements).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. /// diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs index 5d6ce215f5..d0949c93a7 100644 --- a/osu.Game/Storyboards/StoryboardSample.cs +++ b/osu.Game/Storyboards/StoryboardSample.cs @@ -15,6 +15,8 @@ namespace osu.Game.Storyboards public double StartTime { get; } + public double EndTime => StartTime; + public int Volume { get; } public IEnumerable LookupNames => new[] diff --git a/osu.Game/Storyboards/StoryboardVideo.cs b/osu.Game/Storyboards/StoryboardVideo.cs index 4652e45852..1314bd7cb9 100644 --- a/osu.Game/Storyboards/StoryboardVideo.cs +++ b/osu.Game/Storyboards/StoryboardVideo.cs @@ -14,6 +14,8 @@ namespace osu.Game.Storyboards public double StartTime { get; } + public double EndTime => StartTime; + public StoryboardVideo(string path, int offset) { Path = path; From 25b8c2f257f8a52aba5761b819ea0ee7f62b86a4 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Wed, 14 Apr 2021 00:04:03 -0400 Subject: [PATCH 002/108] Allow skipping storyboard outro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuses SkipOverlay by calculating the endtime of the storyboard and using that as a "start point". Upon skipping the outro the score is instantly shown. When the end of the storyboard is reached the score screen automatically shows up. If the player holds ESC (pause) during the outro, the score is displayed The storyboard endtime is calculated by getting the latest endtime of the storyboard's elements, or simply returning 0 if there is no storyboard. Co-Authored-By: Marlina José --- .../Gameplay/TestSceneStoryboardWithOutro.cs | 100 ++++++++++++++++++ .../Multiplayer/MultiplayerPlayer.cs | 1 + .../Screens/Play/GameplayClockContainer.cs | 25 +++++ osu.Game/Screens/Play/Player.cs | 34 ++++++ osu.Game/Screens/Play/PlayerConfiguration.cs | 5 + osu.Game/Storyboards/Storyboard.cs | 1 + 6 files changed, 166 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs new file mode 100644 index 0000000000..1c8b33bb09 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -0,0 +1,100 @@ +// 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.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Screens; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking; +using osu.Game.Storyboards; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneStoryboardWithOutro : PlayerTestScene + { + protected new OutroPlayer Player => (OutroPlayer)base.Player; + + private Storyboard storyboard; + + private const double storyboard_duration = 2000; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.SetValue(OsuSetting.ShowStoryboard, true); + storyboard = new Storyboard(); + var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); + sprite.TimelineGroup.Alpha.Add(Easing.None, 0, storyboard_duration, 1, 0); + storyboard.GetLayer("Background").Add(sprite); + } + + [Test] + public void TestStoryboardSkipOutro() + { + AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space)); + AddAssert("score shown", () => Player.IsScoreShown); + } + + [Test] + public void TestStoryboardNoSkipOutro() + { + AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); + AddUntilStep("storyboard ends", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= storyboard_duration); + AddWaitStep("wait for score", 10); + AddAssert("score shown", () => Player.IsScoreShown); + } + + [Test] + public void TestStoryboardExitToSkipOutro() + { + AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddStep("exit via pause", () => Player.ExitViaPause()); + AddAssert("score shown", () => Player.IsScoreShown); + } + + protected override bool AllowFail => false; + + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OutroPlayer(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new HitCircle()); + return beatmap; + } + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => + new ClockBackedTestWorkingBeatmap(beatmap, storyboard ?? this.storyboard, Clock, Audio); + + protected class OutroPlayer : TestPlayer + { + public void ExitViaPause() => PerformExit(true); + + public bool IsScoreShown => !this.IsCurrentScreen() && this.GetChildScreen() is ResultsScreen; + + public OutroPlayer() + : base(false) + { + } + + protected override Task ImportScore(Score score) + { + // avoid database errors from trying to store the score + return Task.CompletedTask; + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index aaacf891bb..b4e8c13e83 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -49,6 +49,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer AllowPause = false, AllowRestart = false, AllowSkippingIntro = false, + AllowSkippingOutro = false, }) { this.userIds = userIds; diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index ddbb087962..fc45d661cf 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -228,6 +228,11 @@ namespace osu.Game.Screens.Play adjustableClock.ChangeSource(track); } + /// + /// Gets the endtime of the last element in the storyboard in ms, or start time of the last hitobject if there's no storyboard. + /// + public double StoryboardEndTime => beatmap.Storyboard.LatestEventTime ?? 0; + protected override void Update() { if (!IsPaused.Value) @@ -235,6 +240,8 @@ namespace osu.Game.Screens.Play userOffsetClock.ProcessFrame(); } + updateHasStoryboardEnded(); + base.Update(); } @@ -296,5 +303,23 @@ namespace osu.Game.Screens.Play { } } + + # region Storyboard outro logic + + public IBindable HasStoryboardEnded => hasStoryboardEnded; + + public bool HasTimeLeftInStoryboard => GameplayClock.CurrentTime <= StoryboardEndTime; + + private readonly BindableBool hasStoryboardEnded = new BindableBool(true); + + private void updateHasStoryboardEnded() + { + if (StoryboardEndTime == 0) + return; + + hasStoryboardEnded.Value = GameplayClock.CurrentTime >= StoryboardEndTime; + } + + # endregion } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index efe5d26409..13820738c7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -74,6 +74,8 @@ namespace osu.Game.Screens.Play private Bindable mouseWheelDisabled; + private Bindable storyboardEnabled; + private readonly Bindable storyboardReplacesBackground = new Bindable(); protected readonly Bindable LocalUserPlaying = new Bindable(); @@ -106,6 +108,8 @@ namespace osu.Game.Screens.Play private SkipOverlay skipOverlay; + private SkipOverlay skipOutroOverlay; + protected ScoreProcessor ScoreProcessor { get; private set; } protected HealthProcessor HealthProcessor { get; private set; } @@ -190,6 +194,8 @@ namespace osu.Game.Screens.Play mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); + storyboardEnabled = config.GetBindable(OsuSetting.ShowStoryboard); + if (game != null) gameActive.BindTo(game.IsActive); @@ -285,6 +291,9 @@ namespace osu.Game.Screens.Play ScoreProcessor.HasCompleted.ValueChanged += updateCompletionState; HealthProcessor.Failed += onFail; + // Keep track of whether the storyboard ended after the playable portion + GameplayClockContainer.HasStoryboardEnded.ValueChanged += updateCompletionState; + foreach (var mod in Mods.Value.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); @@ -360,6 +369,10 @@ namespace osu.Game.Screens.Play { RequestSkip = performUserRequestedSkip }, + skipOutroOverlay = new SkipOverlay(GameplayClockContainer.StoryboardEndTime) + { + RequestSkip = scheduleCompletion + }, FailOverlay = new FailOverlay { OnRetry = Restart, @@ -389,6 +402,9 @@ namespace osu.Game.Screens.Play if (!Configuration.AllowSkippingIntro) skipOverlay.Expire(); + if (!Configuration.AllowSkippingOutro) + skipOutroOverlay.Expire(); + if (Configuration.AllowRestart) { container.Add(new HotkeyRetryOverlay @@ -403,6 +419,8 @@ namespace osu.Game.Screens.Play }); } + skipOutroOverlay.Hide(); + return container; } @@ -523,6 +541,14 @@ namespace osu.Game.Screens.Play Pause(); return; } + + // show the score if in storyboard outro (score has been set) + bool scoreReady = prepareScoreForDisplayTask != null && prepareScoreForDisplayTask.IsCompleted; + + if (scoreReady) + { + scheduleCompletion(); + } } this.Exit(); @@ -611,6 +637,14 @@ namespace osu.Game.Screens.Play return score.ScoreInfo; }); + // show skip overlay if storyboard is enabled and has an outro + if (storyboardEnabled.Value && GameplayClockContainer.HasTimeLeftInStoryboard) + { + skipOutroOverlay.Show(); + completionProgressDelegate = null; + return; + } + using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY)) scheduleCompletion(); } diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs index cd30ead638..ad29563d54 100644 --- a/osu.Game/Screens/Play/PlayerConfiguration.cs +++ b/osu.Game/Screens/Play/PlayerConfiguration.cs @@ -24,5 +24,10 @@ namespace osu.Game.Screens.Play /// Whether the player should be allowed to skip the intro, advancing to the start of gameplay. /// public bool AllowSkippingIntro { get; set; } = true; + + /// + /// Whether the player should be allowed to skip the outro, advancing to the end of a storyboard. + /// + public bool AllowSkippingOutro { get; set; } = true; } } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 41058d9cb3..e22c586048 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -45,6 +45,7 @@ namespace osu.Game.Storyboards /// Videos and samples return StartTime as their EndTIme. /// public double? LatestEventTime => Layers.SelectMany(l => l.Elements).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; + /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. /// From 0e545e1ed9df1d33e0d6506a9edc3a053d6aeb7a Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 15 Apr 2021 17:17:02 -0400 Subject: [PATCH 003/108] Add IHasDuration interface with EndTime for storyboard elements to implement --- osu.Game/Storyboards/IHasDuration.cs | 10 ++++++++++ osu.Game/Storyboards/IStoryboardElement.cs | 2 -- osu.Game/Storyboards/Storyboard.cs | 6 +----- osu.Game/Storyboards/StoryboardSample.cs | 2 -- osu.Game/Storyboards/StoryboardSprite.cs | 2 +- osu.Game/Storyboards/StoryboardVideo.cs | 2 -- 6 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Storyboards/IHasDuration.cs diff --git a/osu.Game/Storyboards/IHasDuration.cs b/osu.Game/Storyboards/IHasDuration.cs new file mode 100644 index 0000000000..98f75b8ee2 --- /dev/null +++ b/osu.Game/Storyboards/IHasDuration.cs @@ -0,0 +1,10 @@ +// 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.Storyboards +{ + public interface IHasDuration + { + double EndTime { get; } + } +} diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index 03f8b97212..c4c150a8a4 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -12,8 +12,6 @@ namespace osu.Game.Storyboards double StartTime { get; } - double EndTime { get; } - Drawable CreateDrawable(); } } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index e22c586048..669f6ccc43 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -40,11 +40,7 @@ namespace osu.Game.Storyboards /// Across all layers, find the latest point in time that a storyboard element ends at. /// Will return null if there are no elements. /// - /// - /// This iterates all elements and as such should be used sparingly or stored locally. - /// Videos and samples return StartTime as their EndTIme. - /// - public double? LatestEventTime => Layers.SelectMany(l => l.Elements).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; + public double? LatestEventTime => Layers.SelectMany(l => l.Elements.OfType()).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs index d0949c93a7..5d6ce215f5 100644 --- a/osu.Game/Storyboards/StoryboardSample.cs +++ b/osu.Game/Storyboards/StoryboardSample.cs @@ -15,8 +15,6 @@ namespace osu.Game.Storyboards public double StartTime { get; } - public double EndTime => StartTime; - public int Volume { get; } public IEnumerable LookupNames => new[] diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index fdaa59d7d9..8f2c1e9e0c 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -11,7 +11,7 @@ using JetBrains.Annotations; namespace osu.Game.Storyboards { - public class StoryboardSprite : IStoryboardElement + public class StoryboardSprite : IStoryboardElement, IHasDuration { private readonly List loops = new List(); private readonly List triggers = new List(); diff --git a/osu.Game/Storyboards/StoryboardVideo.cs b/osu.Game/Storyboards/StoryboardVideo.cs index 1314bd7cb9..4652e45852 100644 --- a/osu.Game/Storyboards/StoryboardVideo.cs +++ b/osu.Game/Storyboards/StoryboardVideo.cs @@ -14,8 +14,6 @@ namespace osu.Game.Storyboards public double StartTime { get; } - public double EndTime => StartTime; - public StoryboardVideo(string path, int offset) { Path = path; From b15838b22027746fd9174e5fb72e10ade6a68cd2 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Fri, 16 Apr 2021 00:59:10 -0400 Subject: [PATCH 004/108] Move storyboard outro logic to DrawableStoryboard --- .../Gameplay/TestSceneStoryboardWithOutro.cs | 17 +++++++++++-- .../Multiplayer/MultiplayerPlayer.cs | 3 +-- osu.Game/Screens/Play/DimmableStoryboard.cs | 3 +++ .../Screens/Play/GameplayClockContainer.cs | 20 --------------- osu.Game/Screens/Play/Player.cs | 25 ++++++++----------- osu.Game/Screens/Play/PlayerConfiguration.cs | 9 ++----- .../Drawables/DrawableStoryboard.cs | 23 +++++++++++++++++ ...on.cs => IStoryboardElementHasDuration.cs} | 2 +- osu.Game/Storyboards/Storyboard.cs | 2 +- osu.Game/Storyboards/StoryboardSprite.cs | 2 +- 10 files changed, 57 insertions(+), 49 deletions(-) rename osu.Game/Storyboards/{IHasDuration.cs => IStoryboardElementHasDuration.cs} (81%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 1c8b33bb09..27d203d5d6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -6,12 +6,14 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Scoring; +using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Storyboards; using osuTK; @@ -36,6 +38,16 @@ namespace osu.Game.Tests.Visual.Gameplay storyboard.GetLayer("Background").Add(sprite); } + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + AddStep("ignore user settings", () => + { + Player.DimmableStoryboard.IgnoreUserSettings.Value = true; + }); + } + [Test] public void TestStoryboardSkipOutro() { @@ -50,8 +62,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); AddUntilStep("storyboard ends", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= storyboard_duration); - AddWaitStep("wait for score", 10); - AddAssert("score shown", () => Player.IsScoreShown); + AddUntilStep("wait for score shown", () => Player.IsScoreShown); } [Test] @@ -85,6 +96,8 @@ namespace osu.Game.Tests.Visual.Gameplay public bool IsScoreShown => !this.IsCurrentScreen() && this.GetChildScreen() is ResultsScreen; + public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard; + public OutroPlayer() : base(false) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index b4e8c13e83..ae2042fbe8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -48,8 +48,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { AllowPause = false, AllowRestart = false, - AllowSkippingIntro = false, - AllowSkippingOutro = false, + AllowSkipping = false, }) { this.userIds = userIds; diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 58eb95b7c6..bebde1b15b 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; using osu.Game.Storyboards; @@ -61,5 +62,7 @@ namespace osu.Game.Screens.Play Add(storyboard); OverlayLayerContainer.Add(storyboard.OverlayLayer.CreateProxy()); } + + public IBindable HasStoryboardEnded => drawableStoryboard?.HasStoryboardEnded; } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index fc45d661cf..95419c0b35 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -240,8 +240,6 @@ namespace osu.Game.Screens.Play userOffsetClock.ProcessFrame(); } - updateHasStoryboardEnded(); - base.Update(); } @@ -303,23 +301,5 @@ namespace osu.Game.Screens.Play { } } - - # region Storyboard outro logic - - public IBindable HasStoryboardEnded => hasStoryboardEnded; - - public bool HasTimeLeftInStoryboard => GameplayClock.CurrentTime <= StoryboardEndTime; - - private readonly BindableBool hasStoryboardEnded = new BindableBool(true); - - private void updateHasStoryboardEnded() - { - if (StoryboardEndTime == 0) - return; - - hasStoryboardEnded.Value = GameplayClock.CurrentTime >= StoryboardEndTime; - } - - # endregion } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 14ba9e7e02..81d2621647 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -74,8 +74,6 @@ namespace osu.Game.Screens.Play private Bindable mouseWheelDisabled; - private Bindable storyboardEnabled; - private readonly Bindable storyboardReplacesBackground = new Bindable(); protected readonly Bindable LocalUserPlaying = new Bindable(); @@ -106,7 +104,7 @@ namespace osu.Game.Screens.Play private BreakTracker breakTracker; - private SkipOverlay skipOverlay; + private SkipOverlay skipIntroOverlay; private SkipOverlay skipOutroOverlay; @@ -194,8 +192,6 @@ namespace osu.Game.Screens.Play mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); - storyboardEnabled = config.GetBindable(OsuSetting.ShowStoryboard); - if (game != null) gameActive.BindTo(game.IsActive); @@ -250,7 +246,7 @@ namespace osu.Game.Screens.Play HUDOverlay.ShowHud.Value = false; HUDOverlay.ShowHud.Disabled = true; BreakOverlay.Hide(); - skipOverlay.Hide(); + skipIntroOverlay.Hide(); } DrawableRuleset.FrameStableClock.WaitingOnFrames.BindValueChanged(waiting => @@ -292,7 +288,8 @@ namespace osu.Game.Screens.Play HealthProcessor.Failed += onFail; // Keep track of whether the storyboard ended after the playable portion - GameplayClockContainer.HasStoryboardEnded.ValueChanged += updateCompletionState; + if (DimmableStoryboard.HasStoryboardEnded != null) + DimmableStoryboard.HasStoryboardEnded.ValueChanged += updateCompletionState; foreach (var mod in Mods.Value.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); @@ -365,7 +362,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre }, - skipOverlay = new SkipOverlay(DrawableRuleset.GameplayStartTime) + skipIntroOverlay = new SkipOverlay(DrawableRuleset.GameplayStartTime) { RequestSkip = performUserRequestedSkip }, @@ -399,11 +396,8 @@ namespace osu.Game.Screens.Play } }; - if (!Configuration.AllowSkippingIntro) - skipOverlay.Expire(); - - if (!Configuration.AllowSkippingOutro) - skipOutroOverlay.Expire(); + if (!Configuration.AllowSkipping) + skipIntroOverlay.Expire(); if (Configuration.AllowRestart) { @@ -637,8 +631,9 @@ namespace osu.Game.Screens.Play return score.ScoreInfo; }); - // show skip overlay if storyboard is enabled and has an outro - if (storyboardEnabled.Value && GameplayClockContainer.HasTimeLeftInStoryboard) + var storyboardHasOutro = DimmableStoryboard.ContentDisplayed && (!DimmableStoryboard.HasStoryboardEnded?.Value ?? false); + + if (storyboardHasOutro) { skipOutroOverlay.Show(); completionProgressDelegate = null; diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs index ad29563d54..18ee73374f 100644 --- a/osu.Game/Screens/Play/PlayerConfiguration.cs +++ b/osu.Game/Screens/Play/PlayerConfiguration.cs @@ -21,13 +21,8 @@ namespace osu.Game.Screens.Play public bool AllowRestart { get; set; } = true; /// - /// Whether the player should be allowed to skip the intro, advancing to the start of gameplay. + /// Whether the player should be allowed to skip intros/outros, advancing to the start of gameplay or the end of a storyboard. /// - public bool AllowSkippingIntro { get; set; } = true; - - /// - /// Whether the player should be allowed to skip the outro, advancing to the end of a storyboard. - /// - public bool AllowSkippingOutro { get; set; } = true; + public bool AllowSkipping { get; set; } = true; } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index af4615c895..08c2bf1b4a 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using osuTK; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; @@ -82,5 +83,27 @@ namespace osu.Game.Storyboards.Drawables foreach (var layer in Children) layer.Enabled = passing ? layer.Layer.VisibleWhenPassing : layer.Layer.VisibleWhenFailing; } + + protected override void Update() + { + base.Update(); + updateHasStoryboardEnded(); + } + + /// + /// Whether the storyboard has ended after the gameplay portion of the beatmap. + /// + public IBindable HasStoryboardEnded => hasStoryboardEnded; + + private readonly BindableBool hasStoryboardEnded = new BindableBool(true); + + private void updateHasStoryboardEnded() + { + if (Storyboard.LatestEventTime == null) + return; + + var time = Clock.CurrentTime; + hasStoryboardEnded.Value = time >= Storyboard.LatestEventTime; + } } } diff --git a/osu.Game/Storyboards/IHasDuration.cs b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs similarity index 81% rename from osu.Game/Storyboards/IHasDuration.cs rename to osu.Game/Storyboards/IStoryboardElementHasDuration.cs index 98f75b8ee2..e6ee373812 100644 --- a/osu.Game/Storyboards/IHasDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs @@ -3,7 +3,7 @@ namespace osu.Game.Storyboards { - public interface IHasDuration + public interface IStoryboardElementHasDuration { double EndTime { get; } } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 669f6ccc43..4fa64b7c34 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -40,7 +40,7 @@ namespace osu.Game.Storyboards /// Across all layers, find the latest point in time that a storyboard element ends at. /// Will return null if there are no elements. /// - public double? LatestEventTime => Layers.SelectMany(l => l.Elements.OfType()).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; + public double? LatestEventTime => Layers.SelectMany(l => l.Elements.OfType()).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 8f2c1e9e0c..51bdce3321 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -11,7 +11,7 @@ using JetBrains.Annotations; namespace osu.Game.Storyboards { - public class StoryboardSprite : IStoryboardElement, IHasDuration + public class StoryboardSprite : IStoryboardElement, IStoryboardElementHasDuration { private readonly List loops = new List(); private readonly List triggers = new List(); From 33a665224e6c1f77846e9f03927fa72b4cd3859f Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Fri, 16 Apr 2021 01:03:15 -0400 Subject: [PATCH 005/108] Clean up skipOutroOverlay if skipping is disabled --- osu.Game/Screens/Play/Player.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 81d2621647..7ad9c1a8af 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -397,7 +397,10 @@ namespace osu.Game.Screens.Play }; if (!Configuration.AllowSkipping) + { skipIntroOverlay.Expire(); + skipOutroOverlay.Expire(); + } if (Configuration.AllowRestart) { From 81be562379d5bb60023be3f20ecb1f8990993ef9 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 12:28:31 -0400 Subject: [PATCH 006/108] Read StoryboardEndTime directly from Beatmap --- osu.Game/Screens/Play/GameplayClockContainer.cs | 5 ----- osu.Game/Screens/Play/Player.cs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 95419c0b35..ddbb087962 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -228,11 +228,6 @@ namespace osu.Game.Screens.Play adjustableClock.ChangeSource(track); } - /// - /// Gets the endtime of the last element in the storyboard in ms, or start time of the last hitobject if there's no storyboard. - /// - public double StoryboardEndTime => beatmap.Storyboard.LatestEventTime ?? 0; - protected override void Update() { if (!IsPaused.Value) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 7ad9c1a8af..e998f33cc1 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -366,7 +366,7 @@ namespace osu.Game.Screens.Play { RequestSkip = performUserRequestedSkip }, - skipOutroOverlay = new SkipOverlay(GameplayClockContainer.StoryboardEndTime) + skipOutroOverlay = new SkipOverlay(Beatmap.Value.Storyboard.LatestEventTime ?? 0) { RequestSkip = scheduleCompletion }, From 5a015290b9721e151a689abd29162d2deab1ad18 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 12:34:38 -0400 Subject: [PATCH 007/108] Add remarks back to LatestEventTime --- osu.Game/Storyboards/Storyboard.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 4fa64b7c34..0b02a4921b 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -40,6 +40,10 @@ namespace osu.Game.Storyboards /// Across all layers, find the latest point in time that a storyboard element ends at. /// Will return null if there are no elements. /// + /// + /// This iterates all elements and as such should be used sparingly or stored locally. + /// Videos and samples return StartTime as their EndTIme. + /// public double? LatestEventTime => Layers.SelectMany(l => l.Elements.OfType()).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; /// From 7f5b1e84a16d42763f20ecb69359213e4d6868c9 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 13:57:32 -0400 Subject: [PATCH 008/108] Update TestSceneStoryboardWithOutro.cs - Construct storyboard in CreateWorkingBeatmap() - Use GameplayClockContainer.GameplayClock.CurrentTime - Remove unnecessary lines --- .../Gameplay/TestSceneStoryboardWithOutro.cs | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 27d203d5d6..16474d23ea 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Framework.Testing; @@ -13,7 +12,6 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Scoring; -using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Storyboards; using osuTK; @@ -24,28 +22,14 @@ namespace osu.Game.Tests.Visual.Gameplay { protected new OutroPlayer Player => (OutroPlayer)base.Player; - private Storyboard storyboard; - private const double storyboard_duration = 2000; - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - config.SetValue(OsuSetting.ShowStoryboard, true); - storyboard = new Storyboard(); - var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.TimelineGroup.Alpha.Add(Easing.None, 0, storyboard_duration, 1, 0); - storyboard.GetLayer("Background").Add(sprite); - } - [SetUpSteps] public override void SetUpSteps() { base.SetUpSteps(); - AddStep("ignore user settings", () => - { - Player.DimmableStoryboard.IgnoreUserSettings.Value = true; - }); + AddStep("enable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, true)); + AddStep("set dim level to 0", () => LocalConfig.SetValue(OsuSetting.DimLevel, 0)); } [Test] @@ -61,7 +45,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestStoryboardNoSkipOutro() { AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); - AddUntilStep("storyboard ends", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= storyboard_duration); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= storyboard_duration); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } @@ -87,8 +71,18 @@ namespace osu.Game.Tests.Visual.Gameplay return beatmap; } - protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => - new ClockBackedTestWorkingBeatmap(beatmap, storyboard ?? this.storyboard, Clock, Audio); + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) + { + if (storyboard == null) + { + storyboard = new Storyboard(); + var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); + sprite.TimelineGroup.Alpha.Add(Easing.None, 0, storyboard_duration, 1, 0); + storyboard.GetLayer("Background").Add(sprite); + } + + return base.CreateWorkingBeatmap(beatmap, storyboard); + } protected class OutroPlayer : TestPlayer { @@ -96,8 +90,6 @@ namespace osu.Game.Tests.Visual.Gameplay public bool IsScoreShown => !this.IsCurrentScreen() && this.GetChildScreen() is ResultsScreen; - public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard; - public OutroPlayer() : base(false) { @@ -105,7 +97,6 @@ namespace osu.Game.Tests.Visual.Gameplay protected override Task ImportScore(Score score) { - // avoid database errors from trying to store the score return Task.CompletedTask; } } From e40cb6797df3896d6179dc0f85edef8168c5ee09 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 15:27:22 -0400 Subject: [PATCH 009/108] Use GetEndTime() to get storyboard endtime --- osu.Game/Storyboards/IStoryboardElement.cs | 13 +++++++++++++ .../Storyboards/IStoryboardElementHasDuration.cs | 3 +++ osu.Game/Storyboards/Storyboard.cs | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index c4c150a8a4..377ad57ab2 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -14,4 +14,17 @@ namespace osu.Game.Storyboards Drawable CreateDrawable(); } + + public static class StoryboardElementExtensions + { + /// + /// Returns the end time of this object. + /// + /// + /// This returns the where available, falling back to otherwise. + /// + /// The object. + /// The end time of this object. + public static double GetEndTime(this IStoryboardElement storyboardElement) => (storyboardElement as IStoryboardElementHasDuration)?.EndTime ?? storyboardElement.StartTime; + } } diff --git a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs index e6ee373812..daa0c55704 100644 --- a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs @@ -3,6 +3,9 @@ namespace osu.Game.Storyboards { + /// + /// A StoryboardElement that ends at a different time than its start time. + /// public interface IStoryboardElementHasDuration { double EndTime { get; } diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 0b02a4921b..bc61f704dd 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -44,7 +44,7 @@ namespace osu.Game.Storyboards /// This iterates all elements and as such should be used sparingly or stored locally. /// Videos and samples return StartTime as their EndTIme. /// - public double? LatestEventTime => Layers.SelectMany(l => l.Elements.OfType()).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime; + public double? LatestEventTime => Layers.SelectMany(l => l.Elements).OrderBy(e => e.GetEndTime()).LastOrDefault()?.GetEndTime(); /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. From fdcb5e924c9153de2065731689d902d887652bca Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 17:45:38 -0400 Subject: [PATCH 010/108] Initialize skipOutroOverlay with alpha 0, other small changes --- osu.Game/Screens/Play/Player.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e998f33cc1..ef0caa2fa3 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -247,6 +247,7 @@ namespace osu.Game.Screens.Play HUDOverlay.ShowHud.Disabled = true; BreakOverlay.Hide(); skipIntroOverlay.Hide(); + skipOutroOverlay.Hide(); } DrawableRuleset.FrameStableClock.WaitingOnFrames.BindValueChanged(waiting => @@ -368,7 +369,8 @@ namespace osu.Game.Screens.Play }, skipOutroOverlay = new SkipOverlay(Beatmap.Value.Storyboard.LatestEventTime ?? 0) { - RequestSkip = scheduleCompletion + RequestSkip = scheduleCompletion, + Alpha = 0 }, FailOverlay = new FailOverlay { @@ -416,8 +418,6 @@ namespace osu.Game.Screens.Play }); } - skipOutroOverlay.Hide(); - return container; } @@ -539,13 +539,9 @@ namespace osu.Game.Screens.Play return; } - // show the score if in storyboard outro (score has been set) - bool scoreReady = prepareScoreForDisplayTask != null && prepareScoreForDisplayTask.IsCompleted; - - if (scoreReady) - { + // if the score is ready for display but results screen has not been pushed yet (e.g. storyboard is still playing beyond gameplay), then transition to results screen instead of exiting. + if (prepareScoreForDisplayTask != null) scheduleCompletion(); - } } this.Exit(); @@ -639,7 +635,6 @@ namespace osu.Game.Screens.Play if (storyboardHasOutro) { skipOutroOverlay.Show(); - completionProgressDelegate = null; return; } From 97bacbdc760942ccb7052441b249f6a1c87ce2bd Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 17:55:46 -0400 Subject: [PATCH 011/108] Show score after the end of the storyboard after it was toggled --- osu.Game/Screens/Play/DimmableStoryboard.cs | 3 ++- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index bebde1b15b..73b4153601 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -50,6 +50,7 @@ namespace osu.Game.Screens.Play return; drawableStoryboard = storyboard.CreateDrawable(); + HasStoryboardEnded.BindTo(drawableStoryboard.HasStoryboardEnded); if (async) LoadComponentAsync(drawableStoryboard, onStoryboardCreated); @@ -63,6 +64,6 @@ namespace osu.Game.Screens.Play OverlayLayerContainer.Add(storyboard.OverlayLayer.CreateProxy()); } - public IBindable HasStoryboardEnded => drawableStoryboard?.HasStoryboardEnded; + public IBindable HasStoryboardEnded = new BindableBool(true); } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 08c2bf1b4a..abfb4598ec 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -95,15 +95,13 @@ namespace osu.Game.Storyboards.Drawables /// public IBindable HasStoryboardEnded => hasStoryboardEnded; - private readonly BindableBool hasStoryboardEnded = new BindableBool(true); + private readonly BindableBool hasStoryboardEnded = new BindableBool(); private void updateHasStoryboardEnded() { - if (Storyboard.LatestEventTime == null) - return; - - var time = Clock.CurrentTime; - hasStoryboardEnded.Value = time >= Storyboard.LatestEventTime; + hasStoryboardEnded.Value = + Storyboard.LatestEventTime == null || + Time.Current >= Storyboard.LatestEventTime; } } } From c77f838fb051a4edaf2de036438045d489c10db2 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 21:49:07 -0400 Subject: [PATCH 012/108] HasStoryboardEnded doesn't trigger updateCompletionState() Scores won't be shown prematurely if the storyboard ends before the playable portion of the beatmap. --- osu.Game/Screens/Play/Player.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ef0caa2fa3..622d99f078 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -284,14 +284,16 @@ namespace osu.Game.Screens.Play ScoreProcessor.RevertResult(r); }; + DimmableStoryboard.HasStoryboardEnded.ValueChanged += _ => + { + if (ScoreProcessor.HasCompleted.Value) + scheduleCompletion(); + }; + // Bind the judgement processors to ourselves ScoreProcessor.HasCompleted.ValueChanged += updateCompletionState; HealthProcessor.Failed += onFail; - // Keep track of whether the storyboard ended after the playable portion - if (DimmableStoryboard.HasStoryboardEnded != null) - DimmableStoryboard.HasStoryboardEnded.ValueChanged += updateCompletionState; - foreach (var mod in Mods.Value.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); @@ -630,7 +632,7 @@ namespace osu.Game.Screens.Play return score.ScoreInfo; }); - var storyboardHasOutro = DimmableStoryboard.ContentDisplayed && (!DimmableStoryboard.HasStoryboardEnded?.Value ?? false); + var storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; if (storyboardHasOutro) { From e9f8fa64b88b49c50c2ac9284e8d048034269e4d Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 21:49:29 -0400 Subject: [PATCH 013/108] Added a test for toggling the storyboard after the map is loaded --- .../Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 16474d23ea..e86b7591c6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -58,6 +58,16 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("score shown", () => Player.IsScoreShown); } + [TestCase(false)] + [TestCase(true)] + public void TestStoryboardToggle(bool enabledAtBeginning) + { + AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning)); + AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); + AddStep($"toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); + AddUntilStep("wait for score shown", () => Player.IsScoreShown); + } + protected override bool AllowFail => false; protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); From 7b1d40db7de4fc6544ebc1f4070d600a024df488 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sat, 17 Apr 2021 22:13:28 -0400 Subject: [PATCH 014/108] Remove redundant string interpolation --- osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index e86b7591c6..59d543d686 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning)); AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); - AddStep($"toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); + AddStep("toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } From a73bae7a66889f22f8a321745baf25eafafd3efe Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 18 Apr 2021 06:35:43 +0300 Subject: [PATCH 015/108] Schedule completion when storyboard has actually ended --- osu.Game/Screens/Play/Player.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 622d99f078..cb8ebdcce7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -284,9 +284,9 @@ namespace osu.Game.Screens.Play ScoreProcessor.RevertResult(r); }; - DimmableStoryboard.HasStoryboardEnded.ValueChanged += _ => + DimmableStoryboard.HasStoryboardEnded.ValueChanged += storyboardEnded => { - if (ScoreProcessor.HasCompleted.Value) + if (storyboardEnded.NewValue && ScoreProcessor.HasCompleted.Value) scheduleCompletion(); }; From f6a09be62d4a88ec2f15a4040c3b1e1d9e0e2329 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 18 Apr 2021 06:35:54 +0300 Subject: [PATCH 016/108] Add further xmldoc --- osu.Game/Screens/Play/DimmableStoryboard.cs | 10 ++++++++-- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 2 +- osu.Game/Storyboards/IStoryboardElement.cs | 8 ++++---- osu.Game/Storyboards/IStoryboardElementHasDuration.cs | 5 ++++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 73b4153601..f8cedddfbe 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -20,6 +20,14 @@ namespace osu.Game.Screens.Play private readonly Storyboard storyboard; private DrawableStoryboard drawableStoryboard; + /// + /// Whether the storyboard is considered finished. + /// + /// + /// This is true by default in here, until an actual drawable storyboard is loaded, in which case it'll bind to it. + /// + public IBindable HasStoryboardEnded = new BindableBool(true); + public DimmableStoryboard(Storyboard storyboard) { this.storyboard = storyboard; @@ -63,7 +71,5 @@ namespace osu.Game.Screens.Play Add(storyboard); OverlayLayerContainer.Add(storyboard.OverlayLayer.CreateProxy()); } - - public IBindable HasStoryboardEnded = new BindableBool(true); } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index abfb4598ec..a9a8b8a4ac 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -91,7 +91,7 @@ namespace osu.Game.Storyboards.Drawables } /// - /// Whether the storyboard has ended after the gameplay portion of the beatmap. + /// Whether the storyboard is considered finished. /// public IBindable HasStoryboardEnded => hasStoryboardEnded; diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index 377ad57ab2..2f05e92070 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -18,13 +18,13 @@ namespace osu.Game.Storyboards public static class StoryboardElementExtensions { /// - /// Returns the end time of this object. + /// Returns the end time of this storyboard element. /// /// /// This returns the where available, falling back to otherwise. /// - /// The object. - /// The end time of this object. - public static double GetEndTime(this IStoryboardElement storyboardElement) => (storyboardElement as IStoryboardElementHasDuration)?.EndTime ?? storyboardElement.StartTime; + /// The storyboard element. + /// The end time of this element. + public static double GetEndTime(this IStoryboardElement element) => (element as IStoryboardElementHasDuration)?.EndTime ?? element.StartTime; } } diff --git a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs index daa0c55704..b8d3b66694 100644 --- a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementHasDuration.cs @@ -4,10 +4,13 @@ namespace osu.Game.Storyboards { /// - /// A StoryboardElement that ends at a different time than its start time. + /// A that ends at a different time than its start time. /// public interface IStoryboardElementHasDuration { + /// + /// The time at which the ends. + /// double EndTime { get; } } } From f45aed125903673a1eaa27484bce71c5026d61e8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 18 Apr 2021 07:16:00 +0300 Subject: [PATCH 017/108] Remove new line between skip overlay fields --- osu.Game/Screens/Play/Player.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cb8ebdcce7..da877fcd22 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -105,7 +105,6 @@ namespace osu.Game.Screens.Play private BreakTracker breakTracker; private SkipOverlay skipIntroOverlay; - private SkipOverlay skipOutroOverlay; protected ScoreProcessor ScoreProcessor { get; private set; } From 98460c8febc37c17acb949a864f528c32ef637cb Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sun, 18 Apr 2021 00:45:24 -0400 Subject: [PATCH 018/108] Rename IStoryboardElementHasDuration, remove unnecessary step in tests, add Duration field --- .../Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 4 ---- osu.Game/Storyboards/IStoryboardElement.cs | 4 ++-- ...entHasDuration.cs => IStoryboardElementWithDuration.cs} | 7 ++++++- osu.Game/Storyboards/StoryboardSprite.cs | 4 +++- 4 files changed, 11 insertions(+), 8 deletions(-) rename osu.Game/Storyboards/{IStoryboardElementHasDuration.cs => IStoryboardElementWithDuration.cs} (70%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 59d543d686..fa1c4aa0d1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -35,7 +35,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestStoryboardSkipOutro() { - AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space)); AddAssert("score shown", () => Player.IsScoreShown); @@ -44,7 +43,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestStoryboardNoSkipOutro() { - AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= storyboard_duration); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } @@ -52,7 +50,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestStoryboardExitToSkipOutro() { - AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddStep("exit via pause", () => Player.ExitViaPause()); AddAssert("score shown", () => Player.IsScoreShown); @@ -63,7 +60,6 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestStoryboardToggle(bool enabledAtBeginning) { AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning)); - AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded); AddStep("toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs index 2f05e92070..9a059991e6 100644 --- a/osu.Game/Storyboards/IStoryboardElement.cs +++ b/osu.Game/Storyboards/IStoryboardElement.cs @@ -21,10 +21,10 @@ namespace osu.Game.Storyboards /// Returns the end time of this storyboard element. /// /// - /// This returns the where available, falling back to otherwise. + /// This returns the where available, falling back to otherwise. /// /// The storyboard element. /// The end time of this element. - public static double GetEndTime(this IStoryboardElement element) => (element as IStoryboardElementHasDuration)?.EndTime ?? element.StartTime; + public static double GetEndTime(this IStoryboardElement element) => (element as IStoryboardElementWithDuration)?.EndTime ?? element.StartTime; } } diff --git a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs b/osu.Game/Storyboards/IStoryboardElementWithDuration.cs similarity index 70% rename from osu.Game/Storyboards/IStoryboardElementHasDuration.cs rename to osu.Game/Storyboards/IStoryboardElementWithDuration.cs index b8d3b66694..02b438cb76 100644 --- a/osu.Game/Storyboards/IStoryboardElementHasDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementWithDuration.cs @@ -6,11 +6,16 @@ namespace osu.Game.Storyboards /// /// A that ends at a different time than its start time. /// - public interface IStoryboardElementHasDuration + public interface IStoryboardElementWithDuration : IStoryboardElement { /// /// The time at which the ends. /// double EndTime { get; } + + /// + /// The duration of the StoryboardElement. + /// + double Duration { get; } } } diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 51bdce3321..0aaf264341 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -11,7 +11,7 @@ using JetBrains.Annotations; namespace osu.Game.Storyboards { - public class StoryboardSprite : IStoryboardElement, IStoryboardElementHasDuration + public class StoryboardSprite : IStoryboardElementWithDuration { private readonly List loops = new List(); private readonly List triggers = new List(); @@ -65,6 +65,8 @@ namespace osu.Game.Storyboards } } + public double Duration => EndTime - StartTime; + public bool HasCommands => TimelineGroup.HasCommands || loops.Any(l => l.HasCommands); private delegate void DrawablePropertyInitializer(Drawable drawable, T value); From 99fab456b57adb11d4bbb6a92b959633fb33c21c Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sun, 18 Apr 2021 23:25:20 -0400 Subject: [PATCH 019/108] Storyboard completion calls updateCompletionState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - If the storyboard ends after the beatmap, call updateCompletionState as if the score processor has completed at that time. (completionProgressDelegate is null here since earlier when the score processor actually completed, updateCompletionState returned after showing the skip overlay.) - If the storyboard ends before the beatmap does, updateCompletionState simply returns and waits until the score processor is completed. - If the storyboard and beatmap end at the exact same time, make sure updateCompletionState() is called only once by the score processor completion. Co-Authored-By: Marlina José --- osu.Game/Screens/Play/Player.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index da877fcd22..231fcbc174 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -285,8 +285,8 @@ namespace osu.Game.Screens.Play DimmableStoryboard.HasStoryboardEnded.ValueChanged += storyboardEnded => { - if (storyboardEnded.NewValue && ScoreProcessor.HasCompleted.Value) - scheduleCompletion(); + if (storyboardEnded.NewValue && completionProgressDelegate == null) + updateCompletionState(new ValueChangedEvent(true, ScoreProcessor.HasCompleted.Value)); }; // Bind the judgement processors to ourselves From fd1241cc8513c72674f723a4c8767d9d5672430d Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sun, 18 Apr 2021 23:26:50 -0400 Subject: [PATCH 020/108] Added tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New tests: - storyboard ending during the failing animation - showResults = false - storyboard ending before score processor completion Co-Authored-By: Marlina José --- .../Gameplay/TestSceneStoryboardWithOutro.cs | 92 ++++++++++++++++--- 1 file changed, 78 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index fa1c4aa0d1..8326063f81 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -1,17 +1,22 @@ // 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 NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Storyboards; using osuTK; @@ -20,9 +25,15 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneStoryboardWithOutro : PlayerTestScene { + protected override bool HasCustomSteps => true; + protected new OutroPlayer Player => (OutroPlayer)base.Player; - private const double storyboard_duration = 2000; + private double currentStoryboardDuration; + + private bool showResults = true; + + private event Func currentFailConditions; [SetUpSteps] public override void SetUpSteps() @@ -30,11 +41,15 @@ namespace osu.Game.Tests.Visual.Gameplay base.SetUpSteps(); AddStep("enable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, true)); AddStep("set dim level to 0", () => LocalConfig.SetValue(OsuSetting.DimLevel, 0)); + AddStep("reset fail conditions", () => currentFailConditions = (_, __) => false); + AddStep("set storyboard duration to 2s", () => currentStoryboardDuration = 2000); + AddStep("set ShowResults = true", () => showResults = true); } [Test] public void TestStoryboardSkipOutro() { + CreateTest(null); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space)); AddAssert("score shown", () => Player.IsScoreShown); @@ -43,13 +58,15 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestStoryboardNoSkipOutro() { - AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= storyboard_duration); + CreateTest(null); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } [Test] public void TestStoryboardExitToSkipOutro() { + CreateTest(null); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); AddStep("exit via pause", () => Player.ExitViaPause()); AddAssert("score shown", () => Player.IsScoreShown); @@ -59,16 +76,51 @@ namespace osu.Game.Tests.Visual.Gameplay [TestCase(true)] public void TestStoryboardToggle(bool enabledAtBeginning) { + CreateTest(null); AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning)); AddStep("toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning)); AddUntilStep("wait for score shown", () => Player.IsScoreShown); } - protected override bool AllowFail => false; + [Test] + public void TestOutroEndsDuringFailAnimation() + { + CreateTest(() => + { + AddStep("fail on first judgement", () => currentFailConditions = (_, __) => true); + AddStep("set storyboard duration to 1.3s", () => currentStoryboardDuration = 1300); + }); + AddUntilStep("wait for fail", () => Player.HasFailed); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); + AddUntilStep("wait for fail overlay", () => Player.FailOverlay.State.Value == Visibility.Visible); + } + + [Test] + public void TestShowResultsFalse() + { + CreateTest(() => + { + AddStep("set ShowResults = false", () => showResults = false); + }); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); + AddWaitStep("wait", 10); + AddAssert("no score shown", () => !Player.IsScoreShown); + } + + [Test] + public void TestStoryboardEndsBeforeCompletion() + { + CreateTest(() => AddStep("set storyboard duration to .1s", () => currentStoryboardDuration = 100)); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddUntilStep("wait for score shown", () => Player.IsScoreShown); + } + + protected override bool AllowFail => true; protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); - protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OutroPlayer(); + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OutroPlayer(currentFailConditions, showResults); protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { @@ -79,26 +131,38 @@ namespace osu.Game.Tests.Visual.Gameplay protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) { - if (storyboard == null) - { - storyboard = new Storyboard(); - var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.TimelineGroup.Alpha.Add(Easing.None, 0, storyboard_duration, 1, 0); - storyboard.GetLayer("Background").Add(sprite); - } + return base.CreateWorkingBeatmap(beatmap, createStoryboard(currentStoryboardDuration)); + } - return base.CreateWorkingBeatmap(beatmap, storyboard); + private Storyboard createStoryboard(double duration) + { + var storyboard = new Storyboard(); + var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); + sprite.TimelineGroup.Alpha.Add(Easing.None, 0, duration, 1, 0); + storyboard.GetLayer("Background").Add(sprite); + return storyboard; } protected class OutroPlayer : TestPlayer { public void ExitViaPause() => PerformExit(true); + public new FailOverlay FailOverlay => base.FailOverlay; + public bool IsScoreShown => !this.IsCurrentScreen() && this.GetChildScreen() is ResultsScreen; - public OutroPlayer() - : base(false) + private event Func failConditions; + + public OutroPlayer(Func failConditions, bool showResults = true) + : base(false, showResults) { + this.failConditions = failConditions; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + HealthProcessor.FailConditions += failConditions; } protected override Task ImportScore(Score score) From 0b36dd9bce6e4f958b51e91d52c7fd650abf2673 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 19 Apr 2021 01:23:21 -0400 Subject: [PATCH 021/108] Skip outro overlay and PerformExit() call updateCompletionState() instead of scheduleCompletion() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Marlina José --- osu.Game/Screens/Play/Player.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 231fcbc174..ef2e042260 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -290,7 +290,7 @@ namespace osu.Game.Screens.Play }; // Bind the judgement processors to ourselves - ScoreProcessor.HasCompleted.ValueChanged += updateCompletionState; + ScoreProcessor.HasCompleted.BindValueChanged(e => updateCompletionState(e)); HealthProcessor.Failed += onFail; foreach (var mod in Mods.Value.OfType()) @@ -370,7 +370,7 @@ namespace osu.Game.Screens.Play }, skipOutroOverlay = new SkipOverlay(Beatmap.Value.Storyboard.LatestEventTime ?? 0) { - RequestSkip = scheduleCompletion, + RequestSkip = () => updateCompletionState(new ValueChangedEvent(true, true), true), Alpha = 0 }, FailOverlay = new FailOverlay @@ -542,7 +542,7 @@ namespace osu.Game.Screens.Play // if the score is ready for display but results screen has not been pushed yet (e.g. storyboard is still playing beyond gameplay), then transition to results screen instead of exiting. if (prepareScoreForDisplayTask != null) - scheduleCompletion(); + updateCompletionState(new ValueChangedEvent(true, true), true); } this.Exit(); @@ -581,7 +581,7 @@ namespace osu.Game.Screens.Play private ScheduledDelegate completionProgressDelegate; private Task prepareScoreForDisplayTask; - private void updateCompletionState(ValueChangedEvent completionState) + private void updateCompletionState(ValueChangedEvent completionState, bool skipStoryboardOutro = false) { // screen may be in the exiting transition phase. if (!this.IsCurrentScreen()) @@ -631,6 +631,12 @@ namespace osu.Game.Screens.Play return score.ScoreInfo; }); + if (skipStoryboardOutro) + { + scheduleCompletion(); + return; + } + var storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; if (storyboardHasOutro) From abfa6aec87047027380c54f75fcba502da3941df Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 19 Apr 2021 01:58:19 -0400 Subject: [PATCH 022/108] Remove completionState parameter --- osu.Game/Screens/Play/Player.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ef2e042260..dd7cf944f7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -286,11 +286,11 @@ namespace osu.Game.Screens.Play DimmableStoryboard.HasStoryboardEnded.ValueChanged += storyboardEnded => { if (storyboardEnded.NewValue && completionProgressDelegate == null) - updateCompletionState(new ValueChangedEvent(true, ScoreProcessor.HasCompleted.Value)); + updateCompletionState(); }; // Bind the judgement processors to ourselves - ScoreProcessor.HasCompleted.BindValueChanged(e => updateCompletionState(e)); + ScoreProcessor.HasCompleted.BindValueChanged(_ => updateCompletionState()); HealthProcessor.Failed += onFail; foreach (var mod in Mods.Value.OfType()) @@ -370,7 +370,7 @@ namespace osu.Game.Screens.Play }, skipOutroOverlay = new SkipOverlay(Beatmap.Value.Storyboard.LatestEventTime ?? 0) { - RequestSkip = () => updateCompletionState(new ValueChangedEvent(true, true), true), + RequestSkip = () => updateCompletionState(true), Alpha = 0 }, FailOverlay = new FailOverlay @@ -542,7 +542,7 @@ namespace osu.Game.Screens.Play // if the score is ready for display but results screen has not been pushed yet (e.g. storyboard is still playing beyond gameplay), then transition to results screen instead of exiting. if (prepareScoreForDisplayTask != null) - updateCompletionState(new ValueChangedEvent(true, true), true); + updateCompletionState(true); } this.Exit(); @@ -581,13 +581,13 @@ namespace osu.Game.Screens.Play private ScheduledDelegate completionProgressDelegate; private Task prepareScoreForDisplayTask; - private void updateCompletionState(ValueChangedEvent completionState, bool skipStoryboardOutro = false) + private void updateCompletionState(bool skipStoryboardOutro = false) { // screen may be in the exiting transition phase. if (!this.IsCurrentScreen()) return; - if (!completionState.NewValue) + if (!ScoreProcessor.HasCompleted.Value) { completionProgressDelegate?.Cancel(); completionProgressDelegate = null; From 505a117862cce9818970a71833736c7a294b84c2 Mon Sep 17 00:00:00 2001 From: Denrage Date: Mon, 19 Apr 2021 23:41:51 +0200 Subject: [PATCH 023/108] splitted updateable part of wedge --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 16 +- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 261 ++++++++++-------- 2 files changed, 152 insertions(+), 125 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 7ea6373763..9c10c9751c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -112,8 +112,8 @@ namespace osu.Game.Tests.Visual.SongSelect private void testInfoLabels(int expectedCount) { - AddAssert("check info labels exists", () => infoWedge.Info.ChildrenOfType().Any()); - AddAssert("check info labels count", () => infoWedge.Info.ChildrenOfType().Count() == expectedCount); + AddAssert("check info labels exists", () => infoWedge.Info.ChildrenOfType().Any()); + AddAssert("check info labels count", () => infoWedge.Info.ChildrenOfType().Count() == expectedCount); } [Test] @@ -124,7 +124,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("check default title", () => infoWedge.Info.TitleLabel.Current.Value == Beatmap.Default.BeatmapInfo.Metadata.Title); AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Current.Value == Beatmap.Default.BeatmapInfo.Metadata.Artist); AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any()); - AddAssert("check no info labels", () => !infoWedge.Info.ChildrenOfType().Any()); + AddAssert("check no info labels", () => !infoWedge.Info.ChildrenOfType().Any()); } [Test] @@ -135,15 +135,15 @@ namespace osu.Game.Tests.Visual.SongSelect private void selectBeatmap([CanBeNull] IBeatmap b) { - BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null; + BeatmapInfoWedge.BufferedWedgeBackground backgroundBefore = null; AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => { - infoBefore = infoWedge.Info; + backgroundBefore = infoWedge.Background; infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b); }); - AddUntilStep("wait for async load", () => infoWedge.Info != infoBefore); + AddUntilStep("wait for async load", () => infoWedge.Background != backgroundBefore); } private IBeatmap createTestBeatmap(RulesetInfo ruleset) @@ -193,7 +193,9 @@ namespace osu.Game.Tests.Visual.SongSelect private class TestBeatmapInfoWedge : BeatmapInfoWedge { - public new BufferedWedgeInfo Info => base.Info; + public new BufferedWedgeBackground Background => base.Background; + + public new WedgeInfoText Info => base.Info; } private class TestHitObject : ConvertHitObject, IHasPosition diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 4069dc82ed..0b72970bb0 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -49,7 +49,8 @@ namespace osu.Game.Screens.Select private IBindable beatmapDifficulty; - protected BufferedWedgeInfo Info; + protected BufferedWedgeBackground Background; + protected WedgeInfoText Info; public BeatmapInfoWedge() { @@ -110,9 +111,9 @@ namespace osu.Game.Screens.Select } } - public override bool IsPresent => base.IsPresent || Info == null; // Visibility is updated in the LoadComponentAsync callback + public override bool IsPresent => base.IsPresent || Background == null; // Visibility is updated in the LoadComponentAsync callback - private BufferedWedgeInfo loadingInfo; + private BufferedWedgeBackground loadingInfo; private void updateDisplay() { @@ -127,6 +128,10 @@ namespace osu.Game.Screens.Select Info?.FadeOut(250); Info?.Expire(); Info = null; + + Background?.FadeOut(250); + Background?.Expire(); + Background = null; } if (beatmap == null) @@ -135,17 +140,21 @@ namespace osu.Game.Screens.Select return; } - LoadComponentAsync(loadingInfo = new BufferedWedgeInfo(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()) + LoadComponentAsync(loadingInfo = new BufferedWedgeBackground(beatmap) { Shear = -Shear, - Depth = Info?.Depth + 1 ?? 0 + Depth = Background?.Depth + 1 ?? 0 }, loaded => { // ensure we are the most recent loaded wedge. if (loaded != loadingInfo) return; removeOldInfo(); - Add(Info = loaded); + Add(Background = loaded); + Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()) + { + Shear = -Shear + }); }); } } @@ -156,28 +165,26 @@ namespace osu.Game.Screens.Select cancellationSource?.Cancel(); } - public class BufferedWedgeInfo : BufferedContainer + public class WedgeInfoText : Container { - public OsuSpriteText VersionLabel { get; private set; } + public FillFlowContainer MapperContainer { get; private set; } public OsuSpriteText TitleLabel { get; private set; } public OsuSpriteText ArtistLabel { get; private set; } + public OsuSpriteText VersionLabel { get; private set; } public BeatmapSetOnlineStatusPill StatusPill { get; private set; } - public FillFlowContainer MapperContainer { get; private set; } private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; private Container bpmLabelContainer; + private ModSettingChangeTracker settingChangeTracker; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; private readonly IReadOnlyList mods; private readonly StarDifficulty starDifficulty; - private ModSettingChangeTracker settingChangeTracker; - - public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, StarDifficulty difficulty) - : base(pixelSnapping: true) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, StarDifficulty difficulty) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; @@ -191,7 +198,6 @@ namespace osu.Game.Screens.Select var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - CacheDrawnFrameBuffer = true; RelativeSizeAxes = Axes.Both; titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title)); @@ -199,32 +205,6 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - // We will create the white-to-black gradient by modulating transparency and having - // a black backdrop. This results in an sRGB-space gradient and not linear space, - // transitioning from white to black more perceptually uniformly. - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - // We use a container, such that we can set the colour gradient to go across the - // vertices of the masked container instead of the vertices of the (larger) sprite. - new Container - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)), - Children = new[] - { - // Zoomed-in and cropped beatmap background - new BeatmapBackgroundSprite(beatmap) - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fill, - }, - }, - }, new DifficultyColourBar(starDifficulty) { RelativeSizeAxes = Axes.Y, @@ -329,51 +309,6 @@ namespace osu.Game.Screens.Select addInfoLabels(); } - private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 - ? new StarRatingDisplay(difficulty) - { - Margin = new MarginPadding { Bottom = 5 } - } - : Empty(); - - private void setMetadata(string source) - { - ArtistLabel.Text = artistBinding.Value; - TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value; - ForceRedraw(); - } - - private void addInfoLabels() - { - if (beatmap.Beatmap?.HitObjects?.Any() != true) - return; - - infoLabelContainer.Children = new Drawable[] - { - new InfoLabel(new BeatmapStatistic - { - Name = "Length", - CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), - Content = TimeSpan.FromMilliseconds(beatmap.BeatmapInfo.Length).ToString(@"m\:ss"), - }), - bpmLabelContainer = new Container - { - AutoSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(20, 0), - Children = getRulesetInfoLabels() - } - }; - - settingChangeTracker = new ModSettingChangeTracker(mods); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(); - - refreshBPMLabel(); - } - private InfoLabel[] getRulesetInfoLabels() { try @@ -426,8 +361,43 @@ namespace osu.Game.Screens.Select CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Bpm), Content = labelText }); + } - ForceRedraw(); + private void setMetadata(string source) + { + ArtistLabel.Text = artistBinding.Value; + TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value; + } + + private void addInfoLabels() + { + if (beatmap.Beatmap?.HitObjects?.Any() != true) + return; + + infoLabelContainer.Children = new Drawable[] + { + new InfoLabel(new BeatmapStatistic + { + Name = "Length", + CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), + Content = TimeSpan.FromMilliseconds(beatmap.BeatmapInfo.Length).ToString(@"m\:ss"), + }), + bpmLabelContainer = new Container + { + AutoSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(20, 0), + Children = getRulesetInfoLabels() + } + }; + + settingChangeTracker = new ModSettingChangeTracker(mods); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(); + + refreshBPMLabel(); } private OsuSpriteText[] getMapper(BeatmapMetadata metadata) @@ -450,10 +420,48 @@ namespace osu.Game.Screens.Select }; } - protected override void Dispose(bool isDisposing) + private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 + ? new StarRatingDisplay(difficulty) + { + Margin = new MarginPadding { Bottom = 5 } + } + : Empty(); + + private class DifficultyColourBar : Container { - base.Dispose(isDisposing); - settingChangeTracker?.Dispose(); + private readonly StarDifficulty difficulty; + + public DifficultyColourBar(StarDifficulty difficulty) + { + this.difficulty = difficulty; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + const float full_opacity_ratio = 0.7f; + + var difficultyColour = colours.ForDifficultyRating(difficulty.DifficultyRating); + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = difficultyColour, + Width = full_opacity_ratio, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, + Colour = difficultyColour, + Alpha = 0.5f, + X = full_opacity_ratio, + Width = 1 - full_opacity_ratio, + } + }; + } } public class InfoLabel : Container, IHasTooltip @@ -515,41 +523,58 @@ namespace osu.Game.Screens.Select } } - private class DifficultyColourBar : Container + protected override void Dispose(bool isDisposing) { - private readonly StarDifficulty difficulty; + base.Dispose(isDisposing); + settingChangeTracker?.Dispose(); + } + } - public DifficultyColourBar(StarDifficulty difficulty) + public class BufferedWedgeBackground : BufferedContainer + { + private readonly WorkingBeatmap beatmap; + + public BufferedWedgeBackground(WorkingBeatmap beatmap) + : base(pixelSnapping: true) + { + this.beatmap = beatmap; + } + + [BackgroundDependencyLoader] + private void load(LocalisationManager localisation) + { + CacheDrawnFrameBuffer = true; + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] { - this.difficulty = difficulty; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - const float full_opacity_ratio = 0.7f; - - var difficultyColour = colours.ForDifficultyRating(difficulty.DifficultyRating); - - Children = new Drawable[] + // We will create the white-to-black gradient by modulating transparency and having + // a black backdrop. This results in an sRGB-space gradient and not linear space, + // transitioning from white to black more perceptually uniformly. + new Box { - new Box + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + // We use a container, such that we can set the colour gradient to go across the + // vertices of the masked container instead of the vertices of the (larger) sprite. + new Container + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)), + Children = new[] { - RelativeSizeAxes = Axes.Both, - Colour = difficultyColour, - Width = full_opacity_ratio, + // Zoomed-in and cropped beatmap background + new BeatmapBackgroundSprite(beatmap) + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fill, + }, }, - new Box - { - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Both, - Colour = difficultyColour, - Alpha = 0.5f, - X = full_opacity_ratio, - Width = 1 - full_opacity_ratio, - } - }; - } + }, + }; } } } From 6e72ee5f7647eb77b2cc1447e32baeb3d4c990c4 Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 10:25:12 +0200 Subject: [PATCH 024/108] Added bindable stardifficulty to StarRatingDisplay --- .../Ranking/Expanded/StarRatingDisplay.cs | 79 +++++++++++++------ osu.Game/Screens/Select/BeatmapInfoWedge.cs | 64 +++++++++------ 2 files changed, 95 insertions(+), 48 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index f7e50fdc8a..081b782034 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Globalization; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -22,7 +24,11 @@ namespace osu.Game.Screens.Ranking.Expanded /// public class StarRatingDisplay : CompositeDrawable { - private readonly StarDifficulty difficulty; + private CircularContainer colorContainer; + private OsuTextFlowContainer textContainer; + + private readonly StarDifficulty starDifficulty; + private readonly IBindable bindableStarDifficulty; /// /// Creates a new using an already computed . @@ -30,14 +36,21 @@ namespace osu.Game.Screens.Ranking.Expanded /// The already computed to display the star difficulty of. public StarRatingDisplay(StarDifficulty starDifficulty) { - difficulty = starDifficulty; + this.starDifficulty = starDifficulty; } - [BackgroundDependencyLoader] - private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) + /// + /// Creates a new using a binded nullable . + /// + /// The binded nullable to display the star difficulty of. If null, a new instance of will be created + public StarRatingDisplay(IBindable starDifficulty) { - AutoSizeAxes = Axes.Both; + bindableStarDifficulty = starDifficulty; + } + private void setDifficulty(OsuColour colours) + { + var difficulty = bindableStarDifficulty == null ? starDifficulty : bindableStarDifficulty.Value ?? new StarDifficulty(); var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); string wholePart = starRatingParts[0]; string fractionPart = starRatingParts[1]; @@ -47,9 +60,36 @@ namespace osu.Game.Screens.Ranking.Expanded ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + colorContainer.Colour = backgroundColour; + + textContainer.Text = string.Empty; + + textContainer.With(t => + { + t.AddText($"{wholePart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + + t.AddText($"{separator}{fractionPart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) + { + AutoSizeAxes = Axes.Both; + InternalChildren = new Drawable[] { - new CircularContainer + colorContainer = new CircularContainer { RelativeSizeAxes = Axes.Both, Masking = true, @@ -58,7 +98,6 @@ namespace osu.Game.Screens.Ranking.Expanded new Box { RelativeSizeAxes = Axes.Both, - Colour = backgroundColour }, } }, @@ -78,32 +117,24 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + textContainer = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, TextAnchor = Anchor.BottomLeft, - }.With(t => - { - t.AddText($"{wholePart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - - t.AddText($"{separator}{fractionPart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - }) + }, } } }; + + if (bindableStarDifficulty != null) + { + bindableStarDifficulty.BindValueChanged(_ => setDifficulty(colours)); + } + + setDifficulty(colours); } } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 0b72970bb0..9dccdd2897 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -105,7 +105,6 @@ namespace osu.Game.Screens.Select beatmapDifficulty?.UnbindAll(); beatmapDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token); - beatmapDifficulty.BindValueChanged(_ => updateDisplay()); updateDisplay(); } @@ -151,7 +150,7 @@ namespace osu.Game.Screens.Select removeOldInfo(); Add(Background = loaded); - Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()) + Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty) { Shear = -Shear }); @@ -176,15 +175,16 @@ namespace osu.Game.Screens.Select private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; + private Drawable starRatingDisplay; private Container bpmLabelContainer; private ModSettingChangeTracker settingChangeTracker; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; private readonly IReadOnlyList mods; - private readonly StarDifficulty starDifficulty; + private readonly IBindable starDifficulty; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, StarDifficulty difficulty) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, IBindable difficulty) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; @@ -241,12 +241,13 @@ namespace osu.Game.Screens.Select Shear = wedged_container_shear, Children = new[] { - createStarRatingDisplay(starDifficulty).With(display => + starRatingDisplay = new StarRatingDisplay(starDifficulty) { - display.Anchor = Anchor.TopRight; - display.Origin = Anchor.TopRight; - display.Shear = -wedged_container_shear; - }), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + Margin = new MarginPadding { Bottom = 5 } + }, StatusPill = new BeatmapSetOnlineStatusPill { Anchor = Anchor.TopRight, @@ -309,6 +310,18 @@ namespace osu.Game.Screens.Select addInfoLabels(); } + private void setStarRatingDisplayVisibility() + { + if (starDifficulty.Value.HasValue && starDifficulty.Value.Value.Stars > 0) + { + starRatingDisplay.Show(); + } + else + { + starRatingDisplay.Hide(); + } + } + private InfoLabel[] getRulesetInfoLabels() { try @@ -420,18 +433,14 @@ namespace osu.Game.Screens.Select }; } - private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 - ? new StarRatingDisplay(difficulty) - { - Margin = new MarginPadding { Bottom = 5 } - } - : Empty(); - private class DifficultyColourBar : Container { - private readonly StarDifficulty difficulty; + private Box solidDifficultyBox; + private Box transparentDifficultyBox; - public DifficultyColourBar(StarDifficulty difficulty) + private readonly IBindable difficulty; + + public DifficultyColourBar(IBindable difficulty) { this.difficulty = difficulty; } @@ -441,26 +450,33 @@ namespace osu.Game.Screens.Select { const float full_opacity_ratio = 0.7f; - var difficultyColour = colours.ForDifficultyRating(difficulty.DifficultyRating); - Children = new Drawable[] { - new Box + solidDifficultyBox = new Box { RelativeSizeAxes = Axes.Both, - Colour = difficultyColour, Width = full_opacity_ratio, }, - new Box + transparentDifficultyBox = new Box { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, - Colour = difficultyColour, Alpha = 0.5f, X = full_opacity_ratio, Width = 1 - full_opacity_ratio, } }; + + difficulty.BindValueChanged(_ => setColour(colours)); + setColour(colours); + } + + private void setColour(OsuColour colours) + { + var difficultyColour = colours.ForDifficultyRating(difficulty.Value?.DifficultyRating ?? (new StarDifficulty()).DifficultyRating); + + solidDifficultyBox.Colour = difficultyColour; + transparentDifficultyBox.Colour = difficultyColour; } } From 4e6cd8082ea784ee4223f2f36e7c05f1253aeac5 Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 12:00:04 +0200 Subject: [PATCH 025/108] WIP refresh BPM-Label on mod change --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 27 +++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 9dccdd2897..b3aa4f0ba5 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -150,7 +150,7 @@ namespace osu.Game.Screens.Select removeOldInfo(); Add(Background = loaded); - Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty) + Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods, beatmapDifficulty) { Shear = -Shear }); @@ -181,10 +181,10 @@ namespace osu.Game.Screens.Select private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; - private readonly IReadOnlyList mods; + private readonly IBindable> mods; private readonly IBindable starDifficulty; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, IBindable difficulty) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IBindable> mods, IBindable difficulty) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; @@ -300,14 +300,16 @@ namespace osu.Game.Screens.Select } }; + addInfoLabels(); + titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); + starDifficulty.BindValueChanged(_ => setStarRatingDisplayVisibility(), true); // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); - addInfoLabels(); } private void setStarRatingDisplayVisibility() @@ -349,7 +351,7 @@ namespace osu.Game.Screens.Select return Array.Empty(); } - private void refreshBPMLabel() + private void refreshBPMLabel(IReadOnlyList mods) { var b = beatmap.Beatmap; if (b == null) @@ -407,10 +409,16 @@ namespace osu.Game.Screens.Select } }; - settingChangeTracker = new ModSettingChangeTracker(mods); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(); + // this is currently not triggering when a mod gets (de)selected + mods.BindValueChanged(mods => refreshModInformation(mods), true); + } - refreshBPMLabel(); + private void refreshModInformation(ValueChangedEvent> modsChangedEvent) + { + settingChangeTracker?.Dispose(); + settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); + refreshBPMLabel(mods.Value); } private OsuSpriteText[] getMapper(BeatmapMetadata metadata) @@ -467,8 +475,7 @@ namespace osu.Game.Screens.Select } }; - difficulty.BindValueChanged(_ => setColour(colours)); - setColour(colours); + difficulty.BindValueChanged(_ => setColour(colours), true); } private void setColour(OsuColour colours) From c5d35ab78733cdd64322467e378474e526096a83 Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 12:35:31 +0200 Subject: [PATCH 026/108] removed mods binding passthrough --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index b3aa4f0ba5..c308f14f74 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -41,9 +41,6 @@ namespace osu.Game.Screens.Select [Resolved] private IBindable ruleset { get; set; } - [Resolved] - private IBindable> mods { get; set; } - [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } @@ -150,7 +147,7 @@ namespace osu.Game.Screens.Select removeOldInfo(); Add(Background = loaded); - Add(Info = new WedgeInfoText(beatmap, ruleset.Value, mods, beatmapDifficulty) + Add(Info = new WedgeInfoText(beatmap, ruleset.Value, beatmapDifficulty) { Shear = -Shear }); @@ -172,6 +169,9 @@ namespace osu.Game.Screens.Select public OsuSpriteText VersionLabel { get; private set; } public BeatmapSetOnlineStatusPill StatusPill { get; private set; } + [Resolved] + private IBindable> mods { get; set; } + private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; @@ -181,14 +181,12 @@ namespace osu.Game.Screens.Select private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; - private readonly IBindable> mods; private readonly IBindable starDifficulty; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IBindable> mods, IBindable difficulty) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IBindable difficulty) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; - this.mods = mods; starDifficulty = difficulty; } @@ -409,7 +407,6 @@ namespace osu.Game.Screens.Select } }; - // this is currently not triggering when a mod gets (de)selected mods.BindValueChanged(mods => refreshModInformation(mods), true); } @@ -418,7 +415,7 @@ namespace osu.Game.Screens.Select settingChangeTracker?.Dispose(); settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); - refreshBPMLabel(mods.Value); + refreshBPMLabel(modsChangedEvent.NewValue); } private OsuSpriteText[] getMapper(BeatmapMetadata metadata) From f799a6e733e0d2075f7feafd9333cfaf5c604fad Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 14:18:41 +0200 Subject: [PATCH 027/108] Removed StarDifficulty binding passthrough --- .../Ranking/Expanded/StarRatingDisplay.cs | 42 +++++++---- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 69 ++++++++++--------- 2 files changed, 67 insertions(+), 44 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 081b782034..1aa8f5ca73 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -3,6 +3,7 @@ using System; using System.Globalization; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -24,11 +25,16 @@ namespace osu.Game.Screens.Ranking.Expanded /// public class StarRatingDisplay : CompositeDrawable { + [Resolved] + private OsuColour colours { get; set; } + private CircularContainer colorContainer; private OsuTextFlowContainer textContainer; + private CancellationTokenSource cancellationTokenSource; + private IBindable bindableStarDifficulty; private readonly StarDifficulty starDifficulty; - private readonly IBindable bindableStarDifficulty; + private readonly BeatmapInfo beatmapInfo; /// /// Creates a new using an already computed . @@ -40,17 +46,16 @@ namespace osu.Game.Screens.Ranking.Expanded } /// - /// Creates a new using a binded nullable . + /// Creates a new using a to use a bindable for the difficulty. /// - /// The binded nullable to display the star difficulty of. If null, a new instance of will be created - public StarRatingDisplay(IBindable starDifficulty) + /// The to use to create a bindable for + public StarRatingDisplay(BeatmapInfo beatmapInfo) { - bindableStarDifficulty = starDifficulty; + this.beatmapInfo = beatmapInfo; } - private void setDifficulty(OsuColour colours) + private void setDifficulty(StarDifficulty difficulty) { - var difficulty = bindableStarDifficulty == null ? starDifficulty : bindableStarDifficulty.Value ?? new StarDifficulty(); var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); string wholePart = starRatingParts[0]; string fractionPart = starRatingParts[1]; @@ -83,8 +88,17 @@ namespace osu.Game.Screens.Ranking.Expanded } [BackgroundDependencyLoader] - private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) + private void load(BeatmapDifficultyCache difficultyCache) { + if (beatmapInfo != null) + { + cancellationTokenSource?.Cancel(); + cancellationTokenSource = new CancellationTokenSource(); + + bindableStarDifficulty?.UnbindAll(); + bindableStarDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); + } + AutoSizeAxes = Axes.Both; InternalChildren = new Drawable[] @@ -130,11 +144,15 @@ namespace osu.Game.Screens.Ranking.Expanded }; if (bindableStarDifficulty != null) - { - bindableStarDifficulty.BindValueChanged(_ => setDifficulty(colours)); - } + bindableStarDifficulty.BindValueChanged(valueChanged => setDifficulty(valueChanged.NewValue ?? new StarDifficulty()), true); + else + setDifficulty(starDifficulty); + } - setDifficulty(colours); + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + cancellationTokenSource?.Cancel(); } } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index c308f14f74..e3afd53210 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -41,11 +41,6 @@ namespace osu.Game.Screens.Select [Resolved] private IBindable ruleset { get; set; } - [Resolved] - private BeatmapDifficultyCache difficultyCache { get; set; } - - private IBindable beatmapDifficulty; - protected BufferedWedgeBackground Background; protected WedgeInfoText Info; @@ -87,8 +82,6 @@ namespace osu.Game.Screens.Select private WorkingBeatmap beatmap; - private CancellationTokenSource cancellationSource; - public WorkingBeatmap Beatmap { get => beatmap; @@ -97,12 +90,6 @@ namespace osu.Game.Screens.Select if (beatmap == value) return; beatmap = value; - cancellationSource?.Cancel(); - cancellationSource = new CancellationTokenSource(); - - beatmapDifficulty?.UnbindAll(); - beatmapDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token); - updateDisplay(); } } @@ -147,7 +134,7 @@ namespace osu.Game.Screens.Select removeOldInfo(); Add(Background = loaded); - Add(Info = new WedgeInfoText(beatmap, ruleset.Value, beatmapDifficulty) + Add(Info = new WedgeInfoText(beatmap, ruleset.Value) { Shear = -Shear }); @@ -158,7 +145,6 @@ namespace osu.Game.Screens.Select protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - cancellationSource?.Cancel(); } public class WedgeInfoText : Container @@ -178,24 +164,30 @@ namespace osu.Game.Screens.Select private Drawable starRatingDisplay; private Container bpmLabelContainer; private ModSettingChangeTracker settingChangeTracker; + private CancellationTokenSource cancellationTokenSource; + private IBindable starDifficulty; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; - private readonly IBindable starDifficulty; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IBindable difficulty) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; - starDifficulty = difficulty; } [BackgroundDependencyLoader] - private void load(LocalisationManager localisation) + private void load(LocalisationManager localisation, BeatmapDifficultyCache difficultyCache) { var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); + cancellationTokenSource?.Cancel(); + cancellationTokenSource = new CancellationTokenSource(); + + starDifficulty?.UnbindAll(); + starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); + RelativeSizeAxes = Axes.Both; titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title)); @@ -203,7 +195,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - new DifficultyColourBar(starDifficulty) + new DifficultyColourBar(beatmapInfo) { RelativeSizeAxes = Axes.Y, Width = 20, @@ -239,7 +231,7 @@ namespace osu.Game.Screens.Select Shear = wedged_container_shear, Children = new[] { - starRatingDisplay = new StarRatingDisplay(starDifficulty) + starRatingDisplay = new StarRatingDisplay(beatmapInfo) { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -313,13 +305,9 @@ namespace osu.Game.Screens.Select private void setStarRatingDisplayVisibility() { if (starDifficulty.Value.HasValue && starDifficulty.Value.Value.Stars > 0) - { starRatingDisplay.Show(); - } else - { starRatingDisplay.Hide(); - } } private InfoLabel[] getRulesetInfoLabels() @@ -440,21 +428,32 @@ namespace osu.Game.Screens.Select private class DifficultyColourBar : Container { + [Resolved] + private OsuColour colours { get; set; } + private Box solidDifficultyBox; private Box transparentDifficultyBox; + private CancellationTokenSource cancellationTokenSource; + private IBindable starDifficulty; - private readonly IBindable difficulty; + private readonly BeatmapInfo beatmapInfo; - public DifficultyColourBar(IBindable difficulty) + public DifficultyColourBar(BeatmapInfo beatmapInfo) { - this.difficulty = difficulty; + this.beatmapInfo = beatmapInfo; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(BeatmapDifficultyCache difficultyCache) { const float full_opacity_ratio = 0.7f; + cancellationTokenSource?.Cancel(); + cancellationTokenSource = new CancellationTokenSource(); + + starDifficulty?.UnbindAll(); + starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); + Children = new Drawable[] { solidDifficultyBox = new Box @@ -472,16 +471,22 @@ namespace osu.Game.Screens.Select } }; - difficulty.BindValueChanged(_ => setColour(colours), true); + starDifficulty.BindValueChanged(valueChangedEvent => setColour(valueChangedEvent), true); } - private void setColour(OsuColour colours) + private void setColour(ValueChangedEvent valueChanged) { - var difficultyColour = colours.ForDifficultyRating(difficulty.Value?.DifficultyRating ?? (new StarDifficulty()).DifficultyRating); + var difficultyColour = colours.ForDifficultyRating(valueChanged.NewValue?.DifficultyRating ?? (new StarDifficulty()).DifficultyRating); solidDifficultyBox.Colour = difficultyColour; transparentDifficultyBox.Colour = difficultyColour; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + cancellationTokenSource?.Cancel(); + } } public class InfoLabel : Container, IHasTooltip From df29e61147fd45f0f310453c19393266d10609dd Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 14:22:49 +0200 Subject: [PATCH 028/108] Fix CodeFactor error --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index e3afd53210..44dbd5a5c6 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -299,7 +299,6 @@ namespace osu.Game.Screens.Select // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); - } private void setStarRatingDisplayVisibility() From 583754b22a430dba73db9688d106dd7d734d6be4 Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 14:29:53 +0200 Subject: [PATCH 029/108] Removed unnecessary whitespaces --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 44dbd5a5c6..7cb6f2caf3 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -598,7 +598,7 @@ namespace osu.Game.Screens.Select }, }, }, - }; + }; } } } From e9571b72cf149a1941717bd1a1e9a22015feca20 Mon Sep 17 00:00:00 2001 From: Denrage Date: Tue, 20 Apr 2021 14:53:35 +0200 Subject: [PATCH 030/108] Fixed InspectCode --- osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs | 3 +-- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 9 ++------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 1aa8f5ca73..a3e9336648 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Globalization; using System.Threading; using osu.Framework.Allocation; @@ -46,7 +45,7 @@ namespace osu.Game.Screens.Ranking.Expanded } /// - /// Creates a new using a to use a bindable for the difficulty. + /// Creates a new using a to use a bindable for the difficulty. /// /// The to use to create a bindable for public StarRatingDisplay(BeatmapInfo beatmapInfo) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 7cb6f2caf3..9077c115d4 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -142,11 +142,6 @@ namespace osu.Game.Screens.Select } } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - } - public class WedgeInfoText : Container { public FillFlowContainer MapperContainer { get; private set; } @@ -394,7 +389,7 @@ namespace osu.Game.Screens.Select } }; - mods.BindValueChanged(mods => refreshModInformation(mods), true); + mods.BindValueChanged(refreshModInformation, true); } private void refreshModInformation(ValueChangedEvent> modsChangedEvent) @@ -470,7 +465,7 @@ namespace osu.Game.Screens.Select } }; - starDifficulty.BindValueChanged(valueChangedEvent => setColour(valueChangedEvent), true); + starDifficulty.BindValueChanged(setColour, true); } private void setColour(ValueChangedEvent valueChanged) From de04caeace220426ed615efa6d3cabe4f76c88af Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 21 Apr 2021 13:53:08 +0200 Subject: [PATCH 031/108] Fixed race condition in StarRatingDisplay --- .../Ranking/Expanded/StarRatingDisplay.cs | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index a3e9336648..8367b1fc6d 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -28,9 +29,10 @@ namespace osu.Game.Screens.Ranking.Expanded private OsuColour colours { get; set; } private CircularContainer colorContainer; - private OsuTextFlowContainer textContainer; private CancellationTokenSource cancellationTokenSource; private IBindable bindableStarDifficulty; + private OsuSpriteText wholePartText; + private OsuSpriteText fractionPartText; private readonly StarDifficulty starDifficulty; private readonly BeatmapInfo beatmapInfo; @@ -66,24 +68,10 @@ namespace osu.Game.Screens.Ranking.Expanded colorContainer.Colour = backgroundColour; - textContainer.Text = string.Empty; + wholePartText.Text = $"{wholePart}"; + fractionPartText.Text = $"{separator}{fractionPart}"; - textContainer.With(t => - { - t.AddText($"{wholePart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - - t.AddText($"{separator}{fractionPart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - }); + } [BackgroundDependencyLoader] @@ -130,14 +118,28 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - textContainer = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, TextAnchor = Anchor.BottomLeft, - }, + }.With(t => + { + t.AddText(wholePartText = new OsuSpriteText(), s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size:14); + s.UseFullGlyphHeight = false; + }); + t.AddText(fractionPartText = new OsuSpriteText(), s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + }), } } }; From 9fba87f67ace6c74bb452d8b8720a78dfc97b82c Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 21 Apr 2021 13:53:25 +0200 Subject: [PATCH 032/108] Moved Info and Background into own container --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 8 ++- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 58 +++++++++++++------ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 9c10c9751c..90bd4ceb88 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -135,15 +135,15 @@ namespace osu.Game.Tests.Visual.SongSelect private void selectBeatmap([CanBeNull] IBeatmap b) { - BeatmapInfoWedge.BufferedWedgeBackground backgroundBefore = null; + BeatmapInfoWedge.BeatmapInfoWedgeContainer containerBefore = null; AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => { - backgroundBefore = infoWedge.Background; + containerBefore = infoWedge.Container; infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b); }); - AddUntilStep("wait for async load", () => infoWedge.Background != backgroundBefore); + AddUntilStep("wait for async load", () => infoWedge.Container != containerBefore); } private IBeatmap createTestBeatmap(RulesetInfo ruleset) @@ -196,6 +196,8 @@ namespace osu.Game.Tests.Visual.SongSelect public new BufferedWedgeBackground Background => base.Background; public new WedgeInfoText Info => base.Info; + + public new BeatmapInfoWedgeContainer Container => base.Container; } private class TestHitObject : ConvertHitObject, IHasPosition diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 9077c115d4..5bb7bbe9fd 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -41,8 +41,9 @@ namespace osu.Game.Screens.Select [Resolved] private IBindable ruleset { get; set; } - protected BufferedWedgeBackground Background; - protected WedgeInfoText Info; + protected BeatmapInfoWedgeContainer Container; + protected WedgeInfoText Info => Container.Info; + protected BufferedWedgeBackground Background => Container.Background; public BeatmapInfoWedge() { @@ -94,9 +95,9 @@ namespace osu.Game.Screens.Select } } - public override bool IsPresent => base.IsPresent || Background == null; // Visibility is updated in the LoadComponentAsync callback + public override bool IsPresent => base.IsPresent || Container == null; // Visibility is updated in the LoadComponentAsync callback - private BufferedWedgeBackground loadingInfo; + private BeatmapInfoWedgeContainer loadingInfo; private void updateDisplay() { @@ -108,13 +109,9 @@ namespace osu.Game.Screens.Select { State.Value = beatmap == null ? Visibility.Hidden : Visibility.Visible; - Info?.FadeOut(250); - Info?.Expire(); - Info = null; - - Background?.FadeOut(250); - Background?.Expire(); - Background = null; + Container?.FadeOut(250); + Container?.Expire(); + Container = null; } if (beatmap == null) @@ -123,25 +120,50 @@ namespace osu.Game.Screens.Select return; } - LoadComponentAsync(loadingInfo = new BufferedWedgeBackground(beatmap) + LoadComponentAsync(loadingInfo = new BeatmapInfoWedgeContainer(beatmap, ruleset.Value) { Shear = -Shear, - Depth = Background?.Depth + 1 ?? 0 }, loaded => { // ensure we are the most recent loaded wedge. if (loaded != loadingInfo) return; removeOldInfo(); - Add(Background = loaded); - Add(Info = new WedgeInfoText(beatmap, ruleset.Value) - { - Shear = -Shear - }); + Add(Container = loaded); }); } } + public class BeatmapInfoWedgeContainer : Container + { + private readonly WorkingBeatmap beatmap; + private readonly RulesetInfo ruleset; + + internal BufferedWedgeBackground Background; + internal WedgeInfoText Info; + + public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset) + { + this.beatmap = beatmap; + this.ruleset = ruleset; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + Background = new BufferedWedgeBackground(beatmap) + { + Depth = Background?.Depth + 1 ?? 0, + }, + Info = new WedgeInfoText(beatmap, ruleset), + }; + } + } + public class WedgeInfoText : Container { public FillFlowContainer MapperContainer { get; private set; } From d6928e91fd1046373476a3ba3a53d16ce55563b4 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 21 Apr 2021 14:02:09 +0200 Subject: [PATCH 033/108] Removed BeatmapInfo in StarRatingDisplay --- .../Ranking/Expanded/StarRatingDisplay.cs | 45 +++++-------------- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 14 +++--- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 8367b1fc6d..ea4936bd82 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -29,13 +29,19 @@ namespace osu.Game.Screens.Ranking.Expanded private OsuColour colours { get; set; } private CircularContainer colorContainer; - private CancellationTokenSource cancellationTokenSource; - private IBindable bindableStarDifficulty; private OsuSpriteText wholePartText; private OsuSpriteText fractionPartText; + private StarDifficulty starDifficulty; - private readonly StarDifficulty starDifficulty; - private readonly BeatmapInfo beatmapInfo; + public StarDifficulty StarDifficulty + { + get => starDifficulty; + set + { + starDifficulty = value; + setDifficulty(starDifficulty); + } + } /// /// Creates a new using an already computed . @@ -46,15 +52,6 @@ namespace osu.Game.Screens.Ranking.Expanded this.starDifficulty = starDifficulty; } - /// - /// Creates a new using a to use a bindable for the difficulty. - /// - /// The to use to create a bindable for - public StarRatingDisplay(BeatmapInfo beatmapInfo) - { - this.beatmapInfo = beatmapInfo; - } - private void setDifficulty(StarDifficulty difficulty) { var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); @@ -71,21 +68,12 @@ namespace osu.Game.Screens.Ranking.Expanded wholePartText.Text = $"{wholePart}"; fractionPartText.Text = $"{separator}{fractionPart}"; - + } [BackgroundDependencyLoader] private void load(BeatmapDifficultyCache difficultyCache) { - if (beatmapInfo != null) - { - cancellationTokenSource?.Cancel(); - cancellationTokenSource = new CancellationTokenSource(); - - bindableStarDifficulty?.UnbindAll(); - bindableStarDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); - } - AutoSizeAxes = Axes.Both; InternalChildren = new Drawable[] @@ -144,16 +132,7 @@ namespace osu.Game.Screens.Ranking.Expanded } }; - if (bindableStarDifficulty != null) - bindableStarDifficulty.BindValueChanged(valueChanged => setDifficulty(valueChanged.NewValue ?? new StarDifficulty()), true); - else - setDifficulty(starDifficulty); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - cancellationTokenSource?.Cancel(); + setDifficulty(starDifficulty); } } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 5bb7bbe9fd..7803b190e6 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -178,7 +178,7 @@ namespace osu.Game.Screens.Select private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; - private Drawable starRatingDisplay; + private StarRatingDisplay starRatingDisplay; private Container bpmLabelContainer; private ModSettingChangeTracker settingChangeTracker; private CancellationTokenSource cancellationTokenSource; @@ -246,9 +246,9 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Shear = wedged_container_shear, - Children = new[] + Children = new Drawable[] { - starRatingDisplay = new StarRatingDisplay(beatmapInfo) + starRatingDisplay = new StarRatingDisplay(starDifficulty.Value ?? new StarDifficulty()) { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -311,19 +311,21 @@ namespace osu.Game.Screens.Select titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); - starDifficulty.BindValueChanged(_ => setStarRatingDisplayVisibility(), true); + starDifficulty.BindValueChanged(updateStarRatingDisplay, true); // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); } - private void setStarRatingDisplayVisibility() + private void updateStarRatingDisplay(ValueChangedEvent valueChanged) { - if (starDifficulty.Value.HasValue && starDifficulty.Value.Value.Stars > 0) + if (valueChanged.NewValue.HasValue && valueChanged.NewValue.Value.Stars > 0) starRatingDisplay.Show(); else starRatingDisplay.Hide(); + + starRatingDisplay.StarDifficulty = valueChanged.NewValue ?? new StarDifficulty(); } private InfoLabel[] getRulesetInfoLabels() From 0dfd0bb59d731a46419f4840883ff433b88ce5d1 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 21 Apr 2021 14:16:16 +0200 Subject: [PATCH 034/108] Refactored background of BeatmapInfoWedge --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 2 - osu.Game/Screens/Select/BeatmapInfoWedge.cs | 57 +--------------- .../Select/BeatmapInfoWedgeBackground.cs | 66 +++++++++++++++++++ 3 files changed, 68 insertions(+), 57 deletions(-) create mode 100644 osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 90bd4ceb88..688cc9a035 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -193,8 +193,6 @@ namespace osu.Game.Tests.Visual.SongSelect private class TestBeatmapInfoWedge : BeatmapInfoWedge { - public new BufferedWedgeBackground Background => base.Background; - public new WedgeInfoText Info => base.Info; public new BeatmapInfoWedgeContainer Container => base.Container; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 7803b190e6..b499423412 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -11,7 +11,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -43,7 +42,6 @@ namespace osu.Game.Screens.Select protected BeatmapInfoWedgeContainer Container; protected WedgeInfoText Info => Container.Info; - protected BufferedWedgeBackground Background => Container.Background; public BeatmapInfoWedge() { @@ -123,6 +121,7 @@ namespace osu.Game.Screens.Select LoadComponentAsync(loadingInfo = new BeatmapInfoWedgeContainer(beatmap, ruleset.Value) { Shear = -Shear, + Depth = Container?.Depth + 1 ?? 0, }, loaded => { // ensure we are the most recent loaded wedge. @@ -139,7 +138,6 @@ namespace osu.Game.Screens.Select private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; - internal BufferedWedgeBackground Background; internal WedgeInfoText Info; public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset) @@ -155,10 +153,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - Background = new BufferedWedgeBackground(beatmap) - { - Depth = Background?.Depth + 1 ?? 0, - }, + new BeatmapInfoWedgeBackground(beatmap), Info = new WedgeInfoText(beatmap, ruleset), }; } @@ -572,53 +567,5 @@ namespace osu.Game.Screens.Select settingChangeTracker?.Dispose(); } } - - public class BufferedWedgeBackground : BufferedContainer - { - private readonly WorkingBeatmap beatmap; - - public BufferedWedgeBackground(WorkingBeatmap beatmap) - : base(pixelSnapping: true) - { - this.beatmap = beatmap; - } - - [BackgroundDependencyLoader] - private void load(LocalisationManager localisation) - { - CacheDrawnFrameBuffer = true; - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - // We will create the white-to-black gradient by modulating transparency and having - // a black backdrop. This results in an sRGB-space gradient and not linear space, - // transitioning from white to black more perceptually uniformly. - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - // We use a container, such that we can set the colour gradient to go across the - // vertices of the masked container instead of the vertices of the (larger) sprite. - new Container - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)), - Children = new[] - { - // Zoomed-in and cropped beatmap background - new BeatmapBackgroundSprite(beatmap) - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fill, - }, - }, - }, - }; - } - } } } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs new file mode 100644 index 0000000000..566f49a799 --- /dev/null +++ b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs @@ -0,0 +1,66 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osuTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Screens.Select +{ + internal class BeatmapInfoWedgeBackground : CompositeDrawable + { + private readonly WorkingBeatmap beatmap; + + public BeatmapInfoWedgeBackground(WorkingBeatmap beatmap) + { + this.beatmap = beatmap; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + InternalChild = new BufferedContainer(pixelSnapping: true) + { + CacheDrawnFrameBuffer = true, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + // We will create the white-to-black gradient by modulating transparency and having + // a black backdrop. This results in an sRGB-space gradient and not linear space, + // transitioning from white to black more perceptually uniformly. + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + // We use a container, such that we can set the colour gradient to go across the + // vertices of the masked container instead of the vertices of the (larger) sprite. + new Container + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)), + Children = new[] + { + // Zoomed-in and cropped beatmap background + new BeatmapBackgroundSprite(beatmap) + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fill, + }, + }, + }, + } + }; + } + } +} From 56a69ed95682e9096f2b8e4b518346b7a66b9308 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 21 Apr 2021 15:53:28 +0200 Subject: [PATCH 035/108] Codestyle fixes --- osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index ea4936bd82..bfc336a677 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -2,9 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Globalization; -using System.Threading; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -67,8 +65,6 @@ namespace osu.Game.Screens.Ranking.Expanded wholePartText.Text = $"{wholePart}"; fractionPartText.Text = $"{separator}{fractionPart}"; - - } [BackgroundDependencyLoader] @@ -118,7 +114,7 @@ namespace osu.Game.Screens.Ranking.Expanded t.AddText(wholePartText = new OsuSpriteText(), s => { s.Colour = Color4.Black; - s.Font = s.Font.With(size:14); + s.Font = s.Font.With(size: 14); s.UseFullGlyphHeight = false; }); t.AddText(fractionPartText = new OsuSpriteText(), s => From 713344ebadc752b16c83bab92331af3c5850be25 Mon Sep 17 00:00:00 2001 From: Denrage Date: Fri, 23 Apr 2021 10:31:49 +0200 Subject: [PATCH 036/108] Reorganize methods --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 214 ++++++++++---------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index b499423412..cb5a276a5d 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -323,6 +323,48 @@ namespace osu.Game.Screens.Select starRatingDisplay.StarDifficulty = valueChanged.NewValue ?? new StarDifficulty(); } + private void refreshModInformation(ValueChangedEvent> modsChangedEvent) + { + settingChangeTracker?.Dispose(); + settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); + refreshBPMLabel(modsChangedEvent.NewValue); + } + + private void setMetadata(string source) + { + ArtistLabel.Text = artistBinding.Value; + TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value; + } + + private void addInfoLabels() + { + if (beatmap.Beatmap?.HitObjects?.Any() != true) + return; + + infoLabelContainer.Children = new Drawable[] + { + new InfoLabel(new BeatmapStatistic + { + Name = "Length", + CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), + Content = TimeSpan.FromMilliseconds(beatmap.BeatmapInfo.Length).ToString(@"m\:ss"), + }), + bpmLabelContainer = new Container + { + AutoSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(20, 0), + Children = getRulesetInfoLabels() + } + }; + + mods.BindValueChanged(refreshModInformation, true); + } + private InfoLabel[] getRulesetInfoLabels() { try @@ -377,48 +419,6 @@ namespace osu.Game.Screens.Select }); } - private void setMetadata(string source) - { - ArtistLabel.Text = artistBinding.Value; - TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value; - } - - private void addInfoLabels() - { - if (beatmap.Beatmap?.HitObjects?.Any() != true) - return; - - infoLabelContainer.Children = new Drawable[] - { - new InfoLabel(new BeatmapStatistic - { - Name = "Length", - CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), - Content = TimeSpan.FromMilliseconds(beatmap.BeatmapInfo.Length).ToString(@"m\:ss"), - }), - bpmLabelContainer = new Container - { - AutoSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(20, 0), - Children = getRulesetInfoLabels() - } - }; - - mods.BindValueChanged(refreshModInformation, true); - } - - private void refreshModInformation(ValueChangedEvent> modsChangedEvent) - { - settingChangeTracker?.Dispose(); - settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); - refreshBPMLabel(modsChangedEvent.NewValue); - } - private OsuSpriteText[] getMapper(BeatmapMetadata metadata) { if (string.IsNullOrEmpty(metadata.Author?.Username)) @@ -439,6 +439,71 @@ namespace osu.Game.Screens.Select }; } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + settingChangeTracker?.Dispose(); + } + + public class InfoLabel : Container, IHasTooltip + { + public string TooltipText { get; } + + public InfoLabel(BeatmapStatistic statistic) + { + TooltipText = statistic.Name; + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20), + Children = new[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"441288"), + Icon = FontAwesome.Solid.Square, + Rotation = 45, + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"f7dd55"), + Icon = FontAwesome.Regular.Circle, + Size = new Vector2(0.8f) + }, + statistic.CreateIcon().With(i => + { + i.Anchor = Anchor.Centre; + i.Origin = Anchor.Centre; + i.RelativeSizeAxes = Axes.Both; + i.Colour = Color4Extensions.FromHex(@"f7dd55"); + i.Size = new Vector2(0.64f); + }), + } + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = new Color4(255, 221, 85, 255), + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17), + Margin = new MarginPadding { Left = 30 }, + Text = statistic.Content, + } + }; + } + } + private class DifficultyColourBar : Container { [Resolved] @@ -501,71 +566,6 @@ namespace osu.Game.Screens.Select cancellationTokenSource?.Cancel(); } } - - public class InfoLabel : Container, IHasTooltip - { - public string TooltipText { get; } - - public InfoLabel(BeatmapStatistic statistic) - { - TooltipText = statistic.Name; - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new Container - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(20), - Children = new[] - { - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"441288"), - Icon = FontAwesome.Solid.Square, - Rotation = 45, - }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"f7dd55"), - Icon = FontAwesome.Regular.Circle, - Size = new Vector2(0.8f) - }, - statistic.CreateIcon().With(i => - { - i.Anchor = Anchor.Centre; - i.Origin = Anchor.Centre; - i.RelativeSizeAxes = Axes.Both; - i.Colour = Color4Extensions.FromHex(@"f7dd55"); - i.Size = new Vector2(0.64f); - }), - } - }, - new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Colour = new Color4(255, 221, 85, 255), - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17), - Margin = new MarginPadding { Left = 30 }, - Text = statistic.Content, - } - }; - } - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - settingChangeTracker?.Dispose(); - } } } } From 59ae5ab913f74d75677576e10d3b3a9484a713a7 Mon Sep 17 00:00:00 2001 From: Denrage Date: Sat, 24 Apr 2021 13:25:29 +0200 Subject: [PATCH 037/108] Added transition in StarRatingDisplay --- .../Ranking/Expanded/StarRatingDisplay.cs | 112 +++++++++--------- 1 file changed, 59 insertions(+), 53 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index bfc336a677..748f58e430 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -27,9 +26,8 @@ namespace osu.Game.Screens.Ranking.Expanded private OsuColour colours { get; set; } private CircularContainer colorContainer; - private OsuSpriteText wholePartText; - private OsuSpriteText fractionPartText; private StarDifficulty starDifficulty; + private FillFlowContainer foregroundContainer; public StarDifficulty StarDifficulty { @@ -52,23 +50,15 @@ namespace osu.Game.Screens.Ranking.Expanded private void setDifficulty(StarDifficulty difficulty) { - var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - string wholePart = starRatingParts[0]; - string fractionPart = starRatingParts[1]; - string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + colorContainer.FadeColour(getDifficultyColour(difficulty), 250); - ColourInfo backgroundColour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); - - colorContainer.Colour = backgroundColour; - - wholePartText.Text = $"{wholePart}"; - fractionPartText.Text = $"{separator}{fractionPart}"; + foregroundContainer.Expire(); + foregroundContainer = null; + AddInternal(foregroundContainer = createForegroundContainer(difficulty)); } [BackgroundDependencyLoader] - private void load(BeatmapDifficultyCache difficultyCache) + private void load() { AutoSizeAxes = Axes.Both; @@ -78,6 +68,7 @@ namespace osu.Game.Screens.Ranking.Expanded { RelativeSizeAxes = Axes.Both, Masking = true, + Colour = getDifficultyColour(starDifficulty), Children = new Drawable[] { new Box @@ -86,49 +77,64 @@ namespace osu.Game.Screens.Ranking.Expanded }, } }, - new FillFlowContainer + foregroundContainer = createForegroundContainer(starDifficulty), + }; + } + + private ColourInfo getDifficultyColour(StarDifficulty difficulty) + { + return difficulty.DifficultyRating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + } + + private FillFlowContainer createForegroundContainer(StarDifficulty difficulty) + { + var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); + string wholePart = starRatingParts[0]; + string fractionPart = starRatingParts[1]; + string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + + return new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 8, Vertical = 4 }, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(2, 0), + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 8, Vertical = 4 }, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(2, 0), - Children = new Drawable[] + new SpriteIcon { - new SpriteIcon + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(7), + Icon = FontAwesome.Solid.Star, + Colour = Color4.Black + }, + new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + TextAnchor = Anchor.BottomLeft, + }.With(t => + { + t.AddText($"{wholePart}", s => { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(7), - Icon = FontAwesome.Solid.Star, - Colour = Color4.Black - }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + t.AddText($"{separator}{fractionPart}", s => { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - TextAnchor = Anchor.BottomLeft, - }.With(t => - { - t.AddText(wholePartText = new OsuSpriteText(), s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - t.AddText(fractionPartText = new OsuSpriteText(), s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - }), - } + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + }), } }; - - setDifficulty(starDifficulty); } } } From 2d17219c8fa8dd28de771604a12fd618e3cab6e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Apr 2021 19:56:49 +0900 Subject: [PATCH 038/108] Setup basic test and classes for scale adjustment --- .../Gameplay/TestSceneSkinnableHUDOverlay.cs | 42 +++++++++++++++++++ .../Play/HUD/SkinnableAccuracyCounter.cs | 2 +- .../Screens/Play/HUD/SkinnableHUDComponent.cs | 32 ++++++++++++++ .../Screens/Play/HUD/SkinnableScoreCounter.cs | 2 +- 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index fec1610160..029bf129c8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -4,17 +4,21 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; +using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -32,6 +36,32 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private OsuConfigManager config { get; set; } + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(new Container + { + RelativeSizeAxes = Axes.Both, + Width = 0.3f, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + Alpha = 0.7f, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = createSkinSourceComponents(), + }, + } + }); + } + [Test] public void TestComboCounterIncrementing() { @@ -74,6 +104,18 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("key counter flow not affected", () => keyCounterFlow.IsPresent); } + private IReadOnlyList createSkinSourceComponents() + { + var hudComponents = typeof(SkinnableHUDComponent).Assembly.GetTypes().Where(t => typeof(SkinnableHUDComponent).IsAssignableFrom(t)).ToArray(); + + List drawables = new List(); + + foreach (var component in hudComponents) + drawables.AddRange(component.CreateSettingsControls()); + + return drawables; + } + private void createNew(Action action = null) { AddStep("create overlay", () => diff --git a/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs index 76c9c30813..ebb3bcd148 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs @@ -6,7 +6,7 @@ using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class SkinnableAccuracyCounter : SkinnableDrawable, IAccuracyCounter + public class SkinnableAccuracyCounter : SkinnableHUDComponent, IAccuracyCounter { public Bindable Current { get; } = new Bindable(); diff --git a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs new file mode 100644 index 0000000000..ea14a15348 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.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.Bindables; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Skinning; + +namespace osu.Game.Screens.Play.HUD +{ + /// + /// A skinnable HUD component which can be scaled and repositioned at a skinner/user's will. + /// + public abstract class SkinnableHUDComponent : SkinnableDrawable + { + [SettingSource("Scale", "The scale at which this component should be displayed.")] + public BindableNumber SkinScale { get; } = new BindableFloat(1) + { + Precision = 0.1f, + MinValue = 0.1f, + MaxValue = 10, + Default = 1, + Value = 1, + }; + + protected SkinnableHUDComponent(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) + : base(component, defaultImplementation, allowFallback, confineMode) + { + } + } +} diff --git a/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs index b46f5684b1..9165b2c7f0 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs @@ -10,7 +10,7 @@ using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class SkinnableScoreCounter : SkinnableDrawable, IScoreCounter + public class SkinnableScoreCounter : SkinnableHUDComponent, IScoreCounter { public Bindable Current { get; } = new Bindable(); From fca173225a2a8e8bf032f2e1019d197a64af1023 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Apr 2021 15:40:35 +0900 Subject: [PATCH 039/108] Refactor editor selection/blueprint components to be generic --- .../Gameplay/TestSceneSkinnableHUDOverlay.cs | 17 ++++++++++++++--- .../Screens/Play/HUD/SkinnableComboCounter.cs | 2 +- .../Screens/Play/HUD/SkinnableHealthDisplay.cs | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 029bf129c8..4f8a78368e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; @@ -13,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; using osu.Game.Configuration; +using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Gameplay { Colour = Color4.Black, RelativeSizeAxes = Axes.Both, - Alpha = 0.7f, + Alpha = 0.9f, }, new FillFlowContainer { @@ -106,12 +106,23 @@ namespace osu.Game.Tests.Visual.Gameplay private IReadOnlyList createSkinSourceComponents() { - var hudComponents = typeof(SkinnableHUDComponent).Assembly.GetTypes().Where(t => typeof(SkinnableHUDComponent).IsAssignableFrom(t)).ToArray(); + Drawable[] hudComponents = typeof(SkinnableHUDComponent).Assembly + .GetTypes() + .Where(t => typeof(SkinnableHUDComponent).IsAssignableFrom(t)) + .Where(t => !t.IsAbstract) + .Select(t => Activator.CreateInstance(t) as Drawable) + .ToArray(); List drawables = new List(); foreach (var component in hudComponents) + { + drawables.Add(new OsuSpriteText + { + Text = component.GetType().Name + }); drawables.AddRange(component.CreateSettingsControls()); + } return drawables; } diff --git a/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs index c04c50141a..629bb467bc 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs @@ -6,7 +6,7 @@ using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class SkinnableComboCounter : SkinnableDrawable, IComboCounter + public class SkinnableComboCounter : SkinnableHUDComponent, IComboCounter { public Bindable Current { get; } = new Bindable(); diff --git a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs index 1f91f5e50f..f8a8696dae 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class SkinnableHealthDisplay : SkinnableDrawable, IHealthDisplay + public class SkinnableHealthDisplay : SkinnableHUDComponent, IHealthDisplay { public Bindable Current { get; } = new BindableDouble(1) { From 99b428ee4b853033abfac1e0ee08a57c185d7a91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Apr 2021 18:03:49 +0900 Subject: [PATCH 040/108] Add very basic skin editor test --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 222 ++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs new file mode 100644 index 0000000000..2391945b91 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -0,0 +1,222 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; +using osu.Framework.Testing; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinEditor : OsuTestScene + { + private HUDOverlay hudOverlay; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create hud", () => + { + hudOverlay = new HUDOverlay(null, null, null, Array.Empty()); + + // Add any key just to display the key counter visually. + hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); + + hudOverlay.ComboCounter.Current.Value = 1; + }); + + AddStep("create editor overlay", () => Add(new SkinEditor(hudOverlay))); + } + + public class SkinEditor : CompositeDrawable + { + private readonly Drawable target; + + public SkinEditor(Drawable target) + { + this.target = target; + + RelativeSizeAxes = Axes.Both; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + InternalChildren = new[] + { + target, + new SkinBlueprintContainer(target), + }; + } + + public class SkinBlueprintContainer : BlueprintContainer + { + private readonly Drawable target; + + public SkinBlueprintContainer(Drawable target) + { + this.target = target; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + SkinnableHUDComponent[] components = target.ChildrenOfType().ToArray(); + + foreach (var c in components) + { + Logger.Log($"Adding blueprint for {c.GetType()}"); + AddBlueprintFor(c); + } + } + + protected override SelectionHandler CreateSelectionHandler() => new SkinSelectionHandler(); + + public class SkinSelectionHandler : SelectionHandler + { + protected override void DeleteItems(IEnumerable items) + { + foreach (var i in items) + i.Drawable.Expire(); + } + + protected override void OnSelectionChanged() + { + base.OnSelectionChanged(); + + SelectionBox.CanRotate = true; + SelectionBox.CanScaleX = true; + SelectionBox.CanScaleY = true; + SelectionBox.CanReverse = false; + } + + public override bool HandleRotation(float angle) + { + foreach (var c in SelectedBlueprints) + c.Item.Rotation += angle; + + return base.HandleRotation(angle); + } + + public override bool HandleScale(Vector2 scale, Anchor anchor) + { + adjustScaleFromAnchor(ref scale, anchor); + + foreach (var c in SelectedBlueprints) + c.Item.Scale += scale * 0.01f; + + return true; + } + + private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) + { + // cancel out scale in axes we don't care about (based on which drag handle was used). + if ((reference & Anchor.x1) > 0) scale.X = 0; + if ((reference & Anchor.y1) > 0) scale.Y = 0; + + // reverse the scale direction if dragging from top or left. + if ((reference & Anchor.x0) > 0) scale.X = -scale.X; + if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y; + } + + public override bool HandleMovement(MoveSelectionEvent moveEvent) + { + foreach (var c in SelectedBlueprints) + c.Item.Position += moveEvent.InstantDelta; + return true; + } + } + + protected override SelectionBlueprint CreateBlueprintFor(SkinnableHUDComponent component) + => new SkinBlueprint(component); + + public class SkinBlueprint : SelectionBlueprint + { + /// + /// The which this applies to. + /// + public readonly SkinnableHUDComponent Component; + + private Container box; + private Drawable drawable => Component.Drawable; + + /// + /// Whether the blueprint should be shown even when the is not alive. + /// + protected virtual bool AlwaysShowWhenSelected => false; + + protected override bool ShouldBeAlive => (Component.IsAlive && Component.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); + + public SkinBlueprint(SkinnableHUDComponent component) + : base(component) + { + Component = component; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChildren = new Drawable[] + { + box = new Container + { + Colour = colours.Yellow, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.2f, + AlwaysPresent = true, + }, + } + }, + }; + } + + private Quad drawableQuad; + + public override Quad ScreenSpaceDrawQuad => drawableQuad; + + protected override void Update() + { + base.Update(); + + drawableQuad = drawable.ScreenSpaceDrawQuad; + var quad = ToLocalSpace(drawable.ScreenSpaceDrawQuad); + + box.Position = quad.TopLeft; + box.Size = quad.Size; + box.Rotation = Component.Rotation; + } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => drawable.ReceivePositionalInputAt(screenSpacePos); + + public override Vector2 ScreenSpaceSelectionPoint => Component.ToScreenSpace(Vector2.Zero); + + public override Quad SelectionQuad => drawable.ScreenSpaceDrawQuad; + + public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Component.Parent.ToLocalSpace(screenSpacePosition) - Component.Position; + } + } + } + } +} From 74fb7cd180051a928cb714e291de09188beec55c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 14:21:07 +0900 Subject: [PATCH 041/108] Extract storable attributes to bindables --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 18 +++++----- .../Screens/Play/HUD/SkinnableHUDComponent.cs | 33 ++++++++++++++----- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 2391945b91..4594db36ae 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -111,7 +111,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override bool HandleRotation(float angle) { foreach (var c in SelectedBlueprints) - c.Item.Rotation += angle; + c.Item.SkinRotation.Value += angle; return base.HandleRotation(angle); } @@ -121,11 +121,18 @@ namespace osu.Game.Tests.Visual.Gameplay adjustScaleFromAnchor(ref scale, anchor); foreach (var c in SelectedBlueprints) - c.Item.Scale += scale * 0.01f; + c.Item.SkinScale.Value += scale.X * 0.01f; return true; } + public override bool HandleMovement(MoveSelectionEvent moveEvent) + { + foreach (var c in SelectedBlueprints) + c.Item.SkinPosition.Value += moveEvent.InstantDelta.X; + return true; + } + private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) { // cancel out scale in axes we don't care about (based on which drag handle was used). @@ -136,13 +143,6 @@ namespace osu.Game.Tests.Visual.Gameplay if ((reference & Anchor.x0) > 0) scale.X = -scale.X; if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y; } - - public override bool HandleMovement(MoveSelectionEvent moveEvent) - { - foreach (var c in SelectedBlueprints) - c.Item.Position += moveEvent.InstantDelta; - return true; - } } protected override SelectionBlueprint CreateBlueprintFor(SkinnableHUDComponent component) diff --git a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs index ea14a15348..5b8d313400 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs @@ -4,8 +4,10 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Layout; using osu.Game.Configuration; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Screens.Play.HUD { @@ -15,18 +17,33 @@ namespace osu.Game.Screens.Play.HUD public abstract class SkinnableHUDComponent : SkinnableDrawable { [SettingSource("Scale", "The scale at which this component should be displayed.")] - public BindableNumber SkinScale { get; } = new BindableFloat(1) - { - Precision = 0.1f, - MinValue = 0.1f, - MaxValue = 10, - Default = 1, - Value = 1, - }; + public BindableNumber SkinScale { get; } = new BindableFloat(1); + + [SettingSource("Position", "The position at which this component should be displayed.")] + public BindableNumber SkinPosition { get; } = new BindableFloat(); + + [SettingSource("Rotation", "The rotation at which this component should be displayed.")] + public BindableNumber SkinRotation { get; } = new BindableFloat(); + + [SettingSource("Anchor", "The screen edge this component should align to.")] + public Bindable SkinAnchor { get; } = new Bindable(); protected SkinnableHUDComponent(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : base(component, defaultImplementation, allowFallback, confineMode) { + SkinScale.BindValueChanged(scale => Drawable.Scale = new Vector2(scale.NewValue)); + SkinPosition.BindValueChanged(position => Position = new Vector2(position.NewValue)); + SkinRotation.BindValueChanged(rotation => Drawable.Rotation = rotation.NewValue); + SkinAnchor.BindValueChanged(anchor => Anchor = anchor.NewValue); + } + + protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) + { + SkinScale.Value = Drawable.Scale.X; + SkinPosition.Value = Position.X; + SkinRotation.Value = Drawable.Rotation; + SkinAnchor.Value = Anchor; + return base.OnInvalidate(invalidation, source); } } } From 1cb8fc9a241a2d07d43019c67f2b7312cc3aa69b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 15:14:48 +0900 Subject: [PATCH 042/108] Extract editor classes out of test namespace and add anchor support --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 213 ++---------------- .../Screens/Play/HUD/SkinnableHUDComponent.cs | 5 +- osu.Game/Skinning/Editor/SkinBlueprint.cs | 86 +++++++ .../Skinning/Editor/SkinBlueprintContainer.cs | 36 +++ osu.Game/Skinning/Editor/SkinEditor.cs | 36 +++ .../Skinning/Editor/SkinSelectionHandler.cs | 139 ++++++++++++ 6 files changed, 315 insertions(+), 200 deletions(-) create mode 100644 osu.Game/Skinning/Editor/SkinBlueprint.cs create mode 100644 osu.Game/Skinning/Editor/SkinBlueprintContainer.cs create mode 100644 osu.Game/Skinning/Editor/SkinEditor.cs create mode 100644 osu.Game/Skinning/Editor/SkinSelectionHandler.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 4594db36ae..e46cd70667 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -2,221 +2,36 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Logging; using osu.Framework.Testing; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Play; -using osu.Game.Screens.Play.HUD; -using osuTK; +using osu.Game.Skinning.Editor; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSkinEditor : OsuTestScene + public class TestSceneSkinEditor : SkinnableTestScene { - private HUDOverlay hudOverlay; - [SetUpSteps] public void SetUpSteps() { - AddStep("create hud", () => + AddStep("create editor overlay", () => { - hudOverlay = new HUDOverlay(null, null, null, Array.Empty()); + SetContents(() => + { + var hudOverlay = new HUDOverlay(null, null, null, Array.Empty()); - // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); + // Add any key just to display the key counter visually. + hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); + hudOverlay.ComboCounter.Current.Value = 1; - hudOverlay.ComboCounter.Current.Value = 1; + return new SkinEditor(hudOverlay); + }); }); - - AddStep("create editor overlay", () => Add(new SkinEditor(hudOverlay))); } - public class SkinEditor : CompositeDrawable - { - private readonly Drawable target; - - public SkinEditor(Drawable target) - { - this.target = target; - - RelativeSizeAxes = Axes.Both; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - InternalChildren = new[] - { - target, - new SkinBlueprintContainer(target), - }; - } - - public class SkinBlueprintContainer : BlueprintContainer - { - private readonly Drawable target; - - public SkinBlueprintContainer(Drawable target) - { - this.target = target; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - SkinnableHUDComponent[] components = target.ChildrenOfType().ToArray(); - - foreach (var c in components) - { - Logger.Log($"Adding blueprint for {c.GetType()}"); - AddBlueprintFor(c); - } - } - - protected override SelectionHandler CreateSelectionHandler() => new SkinSelectionHandler(); - - public class SkinSelectionHandler : SelectionHandler - { - protected override void DeleteItems(IEnumerable items) - { - foreach (var i in items) - i.Drawable.Expire(); - } - - protected override void OnSelectionChanged() - { - base.OnSelectionChanged(); - - SelectionBox.CanRotate = true; - SelectionBox.CanScaleX = true; - SelectionBox.CanScaleY = true; - SelectionBox.CanReverse = false; - } - - public override bool HandleRotation(float angle) - { - foreach (var c in SelectedBlueprints) - c.Item.SkinRotation.Value += angle; - - return base.HandleRotation(angle); - } - - public override bool HandleScale(Vector2 scale, Anchor anchor) - { - adjustScaleFromAnchor(ref scale, anchor); - - foreach (var c in SelectedBlueprints) - c.Item.SkinScale.Value += scale.X * 0.01f; - - return true; - } - - public override bool HandleMovement(MoveSelectionEvent moveEvent) - { - foreach (var c in SelectedBlueprints) - c.Item.SkinPosition.Value += moveEvent.InstantDelta.X; - return true; - } - - private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) - { - // cancel out scale in axes we don't care about (based on which drag handle was used). - if ((reference & Anchor.x1) > 0) scale.X = 0; - if ((reference & Anchor.y1) > 0) scale.Y = 0; - - // reverse the scale direction if dragging from top or left. - if ((reference & Anchor.x0) > 0) scale.X = -scale.X; - if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y; - } - } - - protected override SelectionBlueprint CreateBlueprintFor(SkinnableHUDComponent component) - => new SkinBlueprint(component); - - public class SkinBlueprint : SelectionBlueprint - { - /// - /// The which this applies to. - /// - public readonly SkinnableHUDComponent Component; - - private Container box; - private Drawable drawable => Component.Drawable; - - /// - /// Whether the blueprint should be shown even when the is not alive. - /// - protected virtual bool AlwaysShowWhenSelected => false; - - protected override bool ShouldBeAlive => (Component.IsAlive && Component.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); - - public SkinBlueprint(SkinnableHUDComponent component) - : base(component) - { - Component = component; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - InternalChildren = new Drawable[] - { - box = new Container - { - Colour = colours.Yellow, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0.2f, - AlwaysPresent = true, - }, - } - }, - }; - } - - private Quad drawableQuad; - - public override Quad ScreenSpaceDrawQuad => drawableQuad; - - protected override void Update() - { - base.Update(); - - drawableQuad = drawable.ScreenSpaceDrawQuad; - var quad = ToLocalSpace(drawable.ScreenSpaceDrawQuad); - - box.Position = quad.TopLeft; - box.Size = quad.Size; - box.Rotation = Component.Rotation; - } - - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => drawable.ReceivePositionalInputAt(screenSpacePos); - - public override Vector2 ScreenSpaceSelectionPoint => Component.ToScreenSpace(Vector2.Zero); - - public override Quad SelectionQuad => drawable.ScreenSpaceDrawQuad; - - public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Component.Parent.ToLocalSpace(screenSpacePosition) - Component.Position; - } - } - } + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); } } diff --git a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs index 5b8d313400..2064f8589d 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs @@ -34,7 +34,10 @@ namespace osu.Game.Screens.Play.HUD SkinScale.BindValueChanged(scale => Drawable.Scale = new Vector2(scale.NewValue)); SkinPosition.BindValueChanged(position => Position = new Vector2(position.NewValue)); SkinRotation.BindValueChanged(rotation => Drawable.Rotation = rotation.NewValue); - SkinAnchor.BindValueChanged(anchor => Anchor = anchor.NewValue); + SkinAnchor.BindValueChanged(anchor => + { + Drawable.Anchor = anchor.NewValue; + }); } protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs new file mode 100644 index 0000000000..f9fffab579 --- /dev/null +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -0,0 +1,86 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Screens.Play.HUD; +using osuTK; + +namespace osu.Game.Skinning.Editor +{ + public class SkinBlueprint : SelectionBlueprint + { + /// + /// The which this applies to. + /// + public readonly SkinnableHUDComponent Component; + + private Container box; + private Drawable drawable => Component.Drawable; + + /// + /// Whether the blueprint should be shown even when the is not alive. + /// + protected virtual bool AlwaysShowWhenSelected => false; + + protected override bool ShouldBeAlive => (Component.IsAlive && Component.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); + + public SkinBlueprint(SkinnableHUDComponent component) + : base(component) + { + Component = component; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChildren = new Drawable[] + { + box = new Container + { + Colour = colours.Yellow, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.2f, + AlwaysPresent = true, + }, + } + }, + }; + } + + private Quad drawableQuad; + + public override Quad ScreenSpaceDrawQuad => drawableQuad; + + protected override void Update() + { + base.Update(); + + drawableQuad = drawable.ScreenSpaceDrawQuad; + var quad = ToLocalSpace(drawable.ScreenSpaceDrawQuad); + + box.Position = quad.TopLeft; + box.Size = quad.Size; + box.Rotation = Component.Rotation; + } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => drawable.ReceivePositionalInputAt(screenSpacePos); + + public override Vector2 ScreenSpaceSelectionPoint => Component.ToScreenSpace(Vector2.Zero); + + public override Quad SelectionQuad => drawable.ScreenSpaceDrawQuad; + + public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Component.Parent.ToLocalSpace(screenSpacePosition) - Component.Position; + } +} diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs new file mode 100644 index 0000000000..ddf3fd28a7 --- /dev/null +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Rulesets.Edit; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Screens.Play.HUD; + +namespace osu.Game.Skinning.Editor +{ + public class SkinBlueprintContainer : BlueprintContainer + { + private readonly Drawable target; + + public SkinBlueprintContainer(Drawable target) + { + this.target = target; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + SkinnableHUDComponent[] components = target.ChildrenOfType().ToArray(); + + foreach (var c in components) AddBlueprintFor(c); + } + + protected override SelectionHandler CreateSelectionHandler() => new SkinSelectionHandler(); + + protected override SelectionBlueprint CreateBlueprintFor(SkinnableHUDComponent component) + => new SkinBlueprint(component); + } +} diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs new file mode 100644 index 0000000000..74d32bcb9f --- /dev/null +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Cursor; + +namespace osu.Game.Skinning.Editor +{ + public class SkinEditor : CompositeDrawable + { + private readonly Drawable target; + + public SkinEditor(Drawable target) + { + this.target = target; + + RelativeSizeAxes = Axes.Both; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + InternalChild = new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + target, + new SkinBlueprintContainer(target), + } + }; + } + } +} diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs new file mode 100644 index 0000000000..4f28d4e0d2 --- /dev/null +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -0,0 +1,139 @@ +// 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 osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Edit; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Screens.Play.HUD; +using osuTK; + +namespace osu.Game.Skinning.Editor +{ + public class SkinSelectionHandler : SelectionHandler + { + protected override void DeleteItems(IEnumerable items) + { + foreach (var i in items) + i.Drawable.Expire(); + } + + protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable> selection) + { + yield return new OsuMenuItem("Anchor") + { + Items = createAnchorItems().ToArray() + }; + + foreach (var item in base.GetContextMenuItemsForSelection(selection)) + yield return item; + + IEnumerable createAnchorItems() + { + var displayableAnchors = new[] + { + Anchor.TopLeft, + Anchor.TopCentre, + Anchor.TopRight, + Anchor.CentreLeft, + Anchor.Centre, + Anchor.CentreRight, + Anchor.BottomLeft, + Anchor.BottomCentre, + Anchor.BottomRight, + }; + + return displayableAnchors.Select(a => + { + var countExisting = selection.Count(b => b.Item.SkinAnchor.Value == a); + var countTotal = selection.Count(); + + TernaryState state; + + if (countExisting == countTotal) + state = TernaryState.True; + else if (countExisting > 0) + state = TernaryState.Indeterminate; + else + state = TernaryState.False; + + return new AnchorMenuItem(a, selection, _ => applyAnchor(a)) + { + State = { Value = state } + }; + }); + } + } + + private void applyAnchor(Anchor anchor) + { + foreach (var item in SelectedItems) + item.SkinAnchor.Value = anchor; + } + + protected override void OnSelectionChanged() + { + base.OnSelectionChanged(); + + SelectionBox.CanRotate = true; + SelectionBox.CanScaleX = true; + SelectionBox.CanScaleY = true; + SelectionBox.CanReverse = false; + } + + public override bool HandleRotation(float angle) + { + foreach (var c in SelectedBlueprints) + c.Item.SkinRotation.Value += angle; + + return base.HandleRotation(angle); + } + + public override bool HandleScale(Vector2 scale, Anchor anchor) + { + adjustScaleFromAnchor(ref scale, anchor); + + foreach (var c in SelectedBlueprints) + c.Item.SkinScale.Value += scale.X * 0.01f; + + return true; + } + + public override bool HandleMovement(MoveSelectionEvent moveEvent) + { + foreach (var c in SelectedBlueprints) + c.Item.SkinPosition.Value += moveEvent.InstantDelta.X; + return true; + } + + private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) + { + // cancel out scale in axes we don't care about (based on which drag handle was used). + if ((reference & Anchor.x1) > 0) scale.X = 0; + if ((reference & Anchor.y1) > 0) scale.Y = 0; + + // reverse the scale direction if dragging from top or left. + if ((reference & Anchor.x0) > 0) scale.X = -scale.X; + if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y; + } + + public class AnchorMenuItem : TernaryStateMenuItem + { + public AnchorMenuItem(Anchor anchor, IEnumerable> selection, Action action) + : base(anchor.ToString(), getNextState, MenuItemType.Standard, action) + { + } + + private void updateState(TernaryState obj) + { + throw new NotImplementedException(); + } + + private static TernaryState getNextState(TernaryState state) => TernaryState.True; + } + } +} From 59339aa4fd3e46485c83c0a1c73eec18e45bddf9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 15:28:32 +0900 Subject: [PATCH 043/108] Add support for x/y position and scale back in --- .../Screens/Play/HUD/SkinnableHUDComponent.cs | 28 +++++++++++++------ .../Skinning/Editor/SkinSelectionHandler.cs | 11 ++++++-- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs index 2064f8589d..533573efd2 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs @@ -16,11 +16,17 @@ namespace osu.Game.Screens.Play.HUD /// public abstract class SkinnableHUDComponent : SkinnableDrawable { - [SettingSource("Scale", "The scale at which this component should be displayed.")] - public BindableNumber SkinScale { get; } = new BindableFloat(1); + [SettingSource("ScaleX", "The horizontal scale at which this component should be displayed.")] + public BindableNumber SkinScaleX { get; } = new BindableFloat(1); - [SettingSource("Position", "The position at which this component should be displayed.")] - public BindableNumber SkinPosition { get; } = new BindableFloat(); + [SettingSource("ScaleY", "The vertical scale at which this component should be displayed.")] + public BindableNumber SkinScaleY { get; } = new BindableFloat(1); + + [SettingSource("PositionX", "The horizontal position at which this component should be displayed.")] + public BindableNumber SkinPositionX { get; } = new BindableFloat(); + + [SettingSource("PositionY", "The vertical position at which this component should be displayed.")] + public BindableNumber SkinPositionY { get; } = new BindableFloat(); [SettingSource("Rotation", "The rotation at which this component should be displayed.")] public BindableNumber SkinRotation { get; } = new BindableFloat(); @@ -31,8 +37,12 @@ namespace osu.Game.Screens.Play.HUD protected SkinnableHUDComponent(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : base(component, defaultImplementation, allowFallback, confineMode) { - SkinScale.BindValueChanged(scale => Drawable.Scale = new Vector2(scale.NewValue)); - SkinPosition.BindValueChanged(position => Position = new Vector2(position.NewValue)); + SkinScaleX.BindValueChanged(x => Drawable.Scale = new Vector2(x.NewValue, Drawable.Scale.Y)); + SkinScaleY.BindValueChanged(y => Drawable.Scale = new Vector2(Drawable.Scale.X, y.NewValue)); + + SkinPositionX.BindValueChanged(x => Position = new Vector2(x.NewValue, Position.Y)); + SkinPositionY.BindValueChanged(y => Position = new Vector2(Position.X, y.NewValue)); + SkinRotation.BindValueChanged(rotation => Drawable.Rotation = rotation.NewValue); SkinAnchor.BindValueChanged(anchor => { @@ -42,8 +52,10 @@ namespace osu.Game.Screens.Play.HUD protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) { - SkinScale.Value = Drawable.Scale.X; - SkinPosition.Value = Position.X; + SkinScaleX.Value = Drawable.Scale.X; + SkinScaleY.Value = Drawable.Scale.Y; + SkinPositionX.Value = Position.X; + SkinPositionY.Value = Position.Y; SkinRotation.Value = Drawable.Rotation; SkinAnchor.Value = Anchor; return base.OnInvalidate(invalidation, source); diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 4f28d4e0d2..55f36c73d6 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -98,7 +98,10 @@ namespace osu.Game.Skinning.Editor adjustScaleFromAnchor(ref scale, anchor); foreach (var c in SelectedBlueprints) - c.Item.SkinScale.Value += scale.X * 0.01f; + { + c.Item.SkinScaleX.Value += scale.X * 0.01f; + c.Item.SkinScaleY.Value += scale.Y * 0.01f; + } return true; } @@ -106,7 +109,11 @@ namespace osu.Game.Skinning.Editor public override bool HandleMovement(MoveSelectionEvent moveEvent) { foreach (var c in SelectedBlueprints) - c.Item.SkinPosition.Value += moveEvent.InstantDelta.X; + { + c.Item.SkinPositionX.Value += moveEvent.InstantDelta.X; + c.Item.SkinPositionY.Value += moveEvent.InstantDelta.Y; + } + return true; } From 2540d6029c4d804dd2e0378c35b58319349c8ebb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 17:47:28 +0900 Subject: [PATCH 044/108] Use AutoSize for SkinnableHudComponents --- .../Screens/Play/HUD/SkinnableHUDComponent.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs index 533573efd2..f5333c940f 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs @@ -37,26 +37,26 @@ namespace osu.Game.Screens.Play.HUD protected SkinnableHUDComponent(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : base(component, defaultImplementation, allowFallback, confineMode) { - SkinScaleX.BindValueChanged(x => Drawable.Scale = new Vector2(x.NewValue, Drawable.Scale.Y)); - SkinScaleY.BindValueChanged(y => Drawable.Scale = new Vector2(Drawable.Scale.X, y.NewValue)); + SkinScaleX.BindValueChanged(x => Scale = new Vector2(x.NewValue, Scale.Y)); + SkinScaleY.BindValueChanged(y => Scale = new Vector2(Scale.X, y.NewValue)); SkinPositionX.BindValueChanged(x => Position = new Vector2(x.NewValue, Position.Y)); SkinPositionY.BindValueChanged(y => Position = new Vector2(Position.X, y.NewValue)); - SkinRotation.BindValueChanged(rotation => Drawable.Rotation = rotation.NewValue); - SkinAnchor.BindValueChanged(anchor => - { - Drawable.Anchor = anchor.NewValue; - }); + SkinRotation.BindValueChanged(rotation => Rotation = rotation.NewValue); + SkinAnchor.BindValueChanged(anchor => { Anchor = anchor.NewValue; }); + + RelativeSizeAxes = Axes.None; + AutoSizeAxes = Axes.Both; } protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) { - SkinScaleX.Value = Drawable.Scale.X; - SkinScaleY.Value = Drawable.Scale.Y; + SkinScaleX.Value = Scale.X; + SkinScaleY.Value = Scale.Y; SkinPositionX.Value = Position.X; SkinPositionY.Value = Position.Y; - SkinRotation.Value = Drawable.Rotation; + SkinRotation.Value = Rotation; SkinAnchor.Value = Anchor; return base.OnInvalidate(invalidation, source); } From defa350aa71cf07ffcee310fbfbde9dbc1c1c187 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 18:06:59 +0900 Subject: [PATCH 045/108] Set defaults on SkinnableHUDComponent to cancel out relative size default Specifying locally on each HUD component looks to make more sense. --- .../Gameplay/TestSceneSkinnableHUDOverlay.cs | 26 ------------------- .../Play/HUD/SkinnableAccuracyCounter.cs | 2 ++ .../Screens/Play/HUD/SkinnableComboCounter.cs | 2 ++ .../Screens/Play/HUD/SkinnableHUDComponent.cs | 6 ++++- .../Play/HUD/SkinnableHealthDisplay.cs | 3 +++ .../Screens/Play/HUD/SkinnableScoreCounter.cs | 2 ++ 6 files changed, 14 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 4f8a78368e..5c3d3c03bf 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -36,32 +36,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private OsuConfigManager config { get; set; } - protected override void LoadComplete() - { - base.LoadComplete(); - - Add(new Container - { - RelativeSizeAxes = Axes.Both, - Width = 0.3f, - Children = new Drawable[] - { - new Box - { - Colour = Color4.Black, - RelativeSizeAxes = Axes.Both, - Alpha = 0.9f, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = createSkinSourceComponents(), - }, - } - }); - } - [Test] public void TestComboCounterIncrementing() { diff --git a/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs index ebb3bcd148..33132adf23 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD @@ -14,6 +15,7 @@ namespace osu.Game.Screens.Play.HUD : base(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter), _ => new DefaultAccuracyCounter()) { CentreComponent = false; + AutoSizeAxes = Axes.Both; } private IAccuracyCounter skinnedCounter; diff --git a/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs index 629bb467bc..8785eccfc3 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD @@ -14,6 +15,7 @@ namespace osu.Game.Screens.Play.HUD : base(new HUDSkinComponent(HUDSkinComponents.ComboCounter), skinComponent => new DefaultComboCounter()) { CentreComponent = false; + AutoSizeAxes = Axes.Both; } private IComboCounter skinnedCounter; diff --git a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs index f5333c940f..7d23611718 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs @@ -46,8 +46,11 @@ namespace osu.Game.Screens.Play.HUD SkinRotation.BindValueChanged(rotation => Rotation = rotation.NewValue); SkinAnchor.BindValueChanged(anchor => { Anchor = anchor.NewValue; }); + // reset everything and require each component to specify what they want, + // as if they were just drawables. maybe we want to change SkinnableDrawable to not default + // to RelativeSizeAxes.Both... RelativeSizeAxes = Axes.None; - AutoSizeAxes = Axes.Both; + AutoSizeAxes = Axes.None; } protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) @@ -58,6 +61,7 @@ namespace osu.Game.Screens.Play.HUD SkinPositionY.Value = Position.Y; SkinRotation.Value = Rotation; SkinAnchor.Value = Anchor; + return base.OnInvalidate(invalidation, source); } } diff --git a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs index f8a8696dae..ceac147b63 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; @@ -35,6 +36,8 @@ namespace osu.Game.Screens.Play.HUD : base(new HUDSkinComponent(HUDSkinComponents.HealthDisplay), _ => new DefaultHealthDisplay()) { CentreComponent = false; + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; } private IHealthDisplay skinnedCounter; diff --git a/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs index 9165b2c7f0..c47baf95ff 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; @@ -22,6 +23,7 @@ namespace osu.Game.Screens.Play.HUD : base(new HUDSkinComponent(HUDSkinComponents.ScoreCounter), _ => new DefaultScoreCounter()) { CentreComponent = false; + AutoSizeAxes = Axes.Both; } [BackgroundDependencyLoader] From fd587a82ffe2f73ac5302b528222b0d8f42ad78b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 18:34:34 +0900 Subject: [PATCH 046/108] Replace abstract class with interface, attached to the actual components (not skinnable wrapper) --- .../Gameplay/TestSceneSkinnableHUDOverlay.cs | 27 -------- .../Play/HUD/DefaultAccuracyCounter.cs | 2 +- .../Screens/Play/HUD/DefaultComboCounter.cs | 2 +- .../Screens/Play/HUD/DefaultHealthDisplay.cs | 2 +- .../Screens/Play/HUD/DefaultScoreCounter.cs | 2 +- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 2 +- .../Screens/Play/HUD/ISkinnableComponent.cs | 14 ++++ .../Screens/Play/HUD/LegacyComboCounter.cs | 2 +- .../Play/HUD/SkinnableAccuracyCounter.cs | 4 +- .../Screens/Play/HUD/SkinnableComboCounter.cs | 4 +- .../Screens/Play/HUD/SkinnableHUDComponent.cs | 68 ------------------- .../Play/HUD/SkinnableHealthDisplay.cs | 5 +- .../Screens/Play/HUD/SkinnableScoreCounter.cs | 4 +- osu.Game/Skinning/Editor/SkinBlueprint.cs | 17 ++--- .../Skinning/Editor/SkinBlueprintContainer.cs | 8 +-- .../Skinning/Editor/SkinSelectionHandler.cs | 26 +++---- osu.Game/Skinning/LegacyAccuracyCounter.cs | 2 +- osu.Game/Skinning/LegacyHealthDisplay.cs | 2 +- osu.Game/Skinning/LegacyScoreCounter.cs | 3 +- 19 files changed, 52 insertions(+), 144 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ISkinnableComponent.cs delete mode 100644 osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index 5c3d3c03bf..fec1610160 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -9,16 +9,12 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; using osu.Game.Configuration; -using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Play; -using osu.Game.Screens.Play.HUD; -using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -78,29 +74,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("key counter flow not affected", () => keyCounterFlow.IsPresent); } - private IReadOnlyList createSkinSourceComponents() - { - Drawable[] hudComponents = typeof(SkinnableHUDComponent).Assembly - .GetTypes() - .Where(t => typeof(SkinnableHUDComponent).IsAssignableFrom(t)) - .Where(t => !t.IsAbstract) - .Select(t => Activator.CreateInstance(t) as Drawable) - .ToArray(); - - List drawables = new List(); - - foreach (var component in hudComponents) - { - drawables.Add(new OsuSpriteText - { - Text = component.GetType().Name - }); - drawables.AddRange(component.CreateSettingsControls()); - } - - return drawables; - } - private void createNew(Action action = null) { AddStep("create overlay", () => diff --git a/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs index d5d8ec570a..b8a43708b4 100644 --- a/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { - public class DefaultAccuracyCounter : PercentageCounter, IAccuracyCounter + public class DefaultAccuracyCounter : PercentageCounter, IAccuracyCounter, ISkinnableComponent { private readonly Vector2 offset = new Vector2(-20, 5); diff --git a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs index 63e7a88550..959766ecd1 100644 --- a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs @@ -11,7 +11,7 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { - public class DefaultComboCounter : RollingCounter, IComboCounter + public class DefaultComboCounter : RollingCounter, IComboCounter, ISkinnableComponent { private readonly Vector2 offset = new Vector2(20, 5); diff --git a/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs index b550b469e9..e3cd71691d 100644 --- a/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs @@ -16,7 +16,7 @@ using osu.Framework.Utils; namespace osu.Game.Screens.Play.HUD { - public class DefaultHealthDisplay : HealthDisplay, IHasAccentColour + public class DefaultHealthDisplay : HealthDisplay, IHasAccentColour, ISkinnableComponent { /// /// The base opacity of the glow. diff --git a/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs b/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs index 1dcfe2e067..dde5c18b38 100644 --- a/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Play.HUD { - public class DefaultScoreCounter : ScoreCounter + public class DefaultScoreCounter : ScoreCounter, ISkinnableComponent { public DefaultScoreCounter() : base(6) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 89f135de7f..3b24c8cc9e 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -18,7 +18,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD.HitErrorMeters { - public class BarHitErrorMeter : HitErrorMeter + public class BarHitErrorMeter : HitErrorMeter, ISkinnableComponent { private readonly Anchor alignment; diff --git a/osu.Game/Screens/Play/HUD/ISkinnableComponent.cs b/osu.Game/Screens/Play/HUD/ISkinnableComponent.cs new file mode 100644 index 0000000000..6d4558443f --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ISkinnableComponent.cs @@ -0,0 +1,14 @@ +// 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; + +namespace osu.Game.Screens.Play.HUD +{ + /// + /// Denotes a drawable which, as a drawable, can be adjusted via skinning specifications. + /// + public interface ISkinnableComponent : IDrawable + { + } +} diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index b4604c0d01..4ae722e44c 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Play.HUD /// /// Uses the 'x' symbol and has a pop-out effect while rolling over. /// - public class LegacyComboCounter : CompositeDrawable, IComboCounter + public class LegacyComboCounter : CompositeDrawable, IComboCounter, ISkinnableComponent { public Bindable Current { get; } = new BindableInt { MinValue = 0, }; diff --git a/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs index 33132adf23..76c9c30813 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs @@ -2,12 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class SkinnableAccuracyCounter : SkinnableHUDComponent, IAccuracyCounter + public class SkinnableAccuracyCounter : SkinnableDrawable, IAccuracyCounter { public Bindable Current { get; } = new Bindable(); @@ -15,7 +14,6 @@ namespace osu.Game.Screens.Play.HUD : base(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter), _ => new DefaultAccuracyCounter()) { CentreComponent = false; - AutoSizeAxes = Axes.Both; } private IAccuracyCounter skinnedCounter; diff --git a/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs index 8785eccfc3..c04c50141a 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs @@ -2,12 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class SkinnableComboCounter : SkinnableHUDComponent, IComboCounter + public class SkinnableComboCounter : SkinnableDrawable, IComboCounter { public Bindable Current { get; } = new Bindable(); @@ -15,7 +14,6 @@ namespace osu.Game.Screens.Play.HUD : base(new HUDSkinComponent(HUDSkinComponents.ComboCounter), skinComponent => new DefaultComboCounter()) { CentreComponent = false; - AutoSizeAxes = Axes.Both; } private IComboCounter skinnedCounter; diff --git a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs b/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs deleted file mode 100644 index 7d23611718..0000000000 --- a/osu.Game/Screens/Play/HUD/SkinnableHUDComponent.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Layout; -using osu.Game.Configuration; -using osu.Game.Skinning; -using osuTK; - -namespace osu.Game.Screens.Play.HUD -{ - /// - /// A skinnable HUD component which can be scaled and repositioned at a skinner/user's will. - /// - public abstract class SkinnableHUDComponent : SkinnableDrawable - { - [SettingSource("ScaleX", "The horizontal scale at which this component should be displayed.")] - public BindableNumber SkinScaleX { get; } = new BindableFloat(1); - - [SettingSource("ScaleY", "The vertical scale at which this component should be displayed.")] - public BindableNumber SkinScaleY { get; } = new BindableFloat(1); - - [SettingSource("PositionX", "The horizontal position at which this component should be displayed.")] - public BindableNumber SkinPositionX { get; } = new BindableFloat(); - - [SettingSource("PositionY", "The vertical position at which this component should be displayed.")] - public BindableNumber SkinPositionY { get; } = new BindableFloat(); - - [SettingSource("Rotation", "The rotation at which this component should be displayed.")] - public BindableNumber SkinRotation { get; } = new BindableFloat(); - - [SettingSource("Anchor", "The screen edge this component should align to.")] - public Bindable SkinAnchor { get; } = new Bindable(); - - protected SkinnableHUDComponent(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) - : base(component, defaultImplementation, allowFallback, confineMode) - { - SkinScaleX.BindValueChanged(x => Scale = new Vector2(x.NewValue, Scale.Y)); - SkinScaleY.BindValueChanged(y => Scale = new Vector2(Scale.X, y.NewValue)); - - SkinPositionX.BindValueChanged(x => Position = new Vector2(x.NewValue, Position.Y)); - SkinPositionY.BindValueChanged(y => Position = new Vector2(Position.X, y.NewValue)); - - SkinRotation.BindValueChanged(rotation => Rotation = rotation.NewValue); - SkinAnchor.BindValueChanged(anchor => { Anchor = anchor.NewValue; }); - - // reset everything and require each component to specify what they want, - // as if they were just drawables. maybe we want to change SkinnableDrawable to not default - // to RelativeSizeAxes.Both... - RelativeSizeAxes = Axes.None; - AutoSizeAxes = Axes.None; - } - - protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) - { - SkinScaleX.Value = Scale.X; - SkinScaleY.Value = Scale.Y; - SkinPositionX.Value = Position.X; - SkinPositionY.Value = Position.Y; - SkinRotation.Value = Rotation; - SkinAnchor.Value = Anchor; - - return base.OnInvalidate(invalidation, source); - } - } -} diff --git a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs index ceac147b63..1f91f5e50f 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs @@ -3,14 +3,13 @@ using System; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class SkinnableHealthDisplay : SkinnableHUDComponent, IHealthDisplay + public class SkinnableHealthDisplay : SkinnableDrawable, IHealthDisplay { public Bindable Current { get; } = new BindableDouble(1) { @@ -36,8 +35,6 @@ namespace osu.Game.Screens.Play.HUD : base(new HUDSkinComponent(HUDSkinComponents.HealthDisplay), _ => new DefaultHealthDisplay()) { CentreComponent = false; - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; } private IHealthDisplay skinnedCounter; diff --git a/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs index c47baf95ff..b46f5684b1 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs @@ -4,14 +4,13 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class SkinnableScoreCounter : SkinnableHUDComponent, IScoreCounter + public class SkinnableScoreCounter : SkinnableDrawable, IScoreCounter { public Bindable Current { get; } = new Bindable(); @@ -23,7 +22,6 @@ namespace osu.Game.Screens.Play.HUD : base(new HUDSkinComponent(HUDSkinComponents.ScoreCounter), _ => new DefaultScoreCounter()) { CentreComponent = false; - AutoSizeAxes = Axes.Both; } [BackgroundDependencyLoader] diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index f9fffab579..674153cb26 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -15,24 +15,25 @@ using osuTK; namespace osu.Game.Skinning.Editor { - public class SkinBlueprint : SelectionBlueprint + public class SkinBlueprint : SelectionBlueprint { /// /// The which this applies to. /// - public readonly SkinnableHUDComponent Component; + public readonly ISkinnableComponent Component; private Container box; - private Drawable drawable => Component.Drawable; + + private Drawable drawable => (Drawable)Component; /// /// Whether the blueprint should be shown even when the is not alive. /// protected virtual bool AlwaysShowWhenSelected => false; - protected override bool ShouldBeAlive => (Component.IsAlive && Component.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); + protected override bool ShouldBeAlive => (drawable.IsAlive && Component.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); - public SkinBlueprint(SkinnableHUDComponent component) + public SkinBlueprint(ISkinnableComponent component) : base(component) { Component = component; @@ -72,15 +73,15 @@ namespace osu.Game.Skinning.Editor box.Position = quad.TopLeft; box.Size = quad.Size; - box.Rotation = Component.Rotation; + box.Rotation = drawable.Rotation; } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => drawable.ReceivePositionalInputAt(screenSpacePos); - public override Vector2 ScreenSpaceSelectionPoint => Component.ToScreenSpace(Vector2.Zero); + public override Vector2 ScreenSpaceSelectionPoint => drawable.ToScreenSpace(Vector2.Zero); public override Quad SelectionQuad => drawable.ScreenSpaceDrawQuad; - public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Component.Parent.ToLocalSpace(screenSpacePosition) - Component.Position; + public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Component.Parent.ToLocalSpace(screenSpacePosition) - drawable.Position; } } diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index ddf3fd28a7..f997e84355 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -10,7 +10,7 @@ using osu.Game.Screens.Play.HUD; namespace osu.Game.Skinning.Editor { - public class SkinBlueprintContainer : BlueprintContainer + public class SkinBlueprintContainer : BlueprintContainer { private readonly Drawable target; @@ -23,14 +23,14 @@ namespace osu.Game.Skinning.Editor { base.LoadComplete(); - SkinnableHUDComponent[] components = target.ChildrenOfType().ToArray(); + ISkinnableComponent[] components = target.ChildrenOfType().ToArray(); foreach (var c in components) AddBlueprintFor(c); } - protected override SelectionHandler CreateSelectionHandler() => new SkinSelectionHandler(); + protected override SelectionHandler CreateSelectionHandler() => new SkinSelectionHandler(); - protected override SelectionBlueprint CreateBlueprintFor(SkinnableHUDComponent component) + protected override SelectionBlueprint CreateBlueprintFor(ISkinnableComponent component) => new SkinBlueprint(component); } } diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 55f36c73d6..38404efd0c 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -14,15 +14,15 @@ using osuTK; namespace osu.Game.Skinning.Editor { - public class SkinSelectionHandler : SelectionHandler + public class SkinSelectionHandler : SelectionHandler { - protected override void DeleteItems(IEnumerable items) + protected override void DeleteItems(IEnumerable items) { foreach (var i in items) - i.Drawable.Expire(); + i.Hide(); } - protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable> selection) + protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable> selection) { yield return new OsuMenuItem("Anchor") { @@ -49,7 +49,7 @@ namespace osu.Game.Skinning.Editor return displayableAnchors.Select(a => { - var countExisting = selection.Count(b => b.Item.SkinAnchor.Value == a); + var countExisting = selection.Count(b => ((Drawable)b.Item).Anchor == a); var countTotal = selection.Count(); TernaryState state; @@ -72,7 +72,7 @@ namespace osu.Game.Skinning.Editor private void applyAnchor(Anchor anchor) { foreach (var item in SelectedItems) - item.SkinAnchor.Value = anchor; + ((Drawable)item).Anchor = anchor; } protected override void OnSelectionChanged() @@ -88,7 +88,7 @@ namespace osu.Game.Skinning.Editor public override bool HandleRotation(float angle) { foreach (var c in SelectedBlueprints) - c.Item.SkinRotation.Value += angle; + ((Drawable)c.Item).Rotation += angle; return base.HandleRotation(angle); } @@ -98,20 +98,16 @@ namespace osu.Game.Skinning.Editor adjustScaleFromAnchor(ref scale, anchor); foreach (var c in SelectedBlueprints) - { - c.Item.SkinScaleX.Value += scale.X * 0.01f; - c.Item.SkinScaleY.Value += scale.Y * 0.01f; - } + ((Drawable)c.Item).Scale += scale * 0.01f; return true; } - public override bool HandleMovement(MoveSelectionEvent moveEvent) + public override bool HandleMovement(MoveSelectionEvent moveEvent) { foreach (var c in SelectedBlueprints) { - c.Item.SkinPositionX.Value += moveEvent.InstantDelta.X; - c.Item.SkinPositionY.Value += moveEvent.InstantDelta.Y; + ((Drawable)c.Item).Position += moveEvent.InstantDelta; } return true; @@ -130,7 +126,7 @@ namespace osu.Game.Skinning.Editor public class AnchorMenuItem : TernaryStateMenuItem { - public AnchorMenuItem(Anchor anchor, IEnumerable> selection, Action action) + public AnchorMenuItem(Anchor anchor, IEnumerable> selection, Action action) : base(anchor.ToString(), getNextState, MenuItemType.Standard, action) { } diff --git a/osu.Game/Skinning/LegacyAccuracyCounter.cs b/osu.Game/Skinning/LegacyAccuracyCounter.cs index 7d6f1dc916..c592e06924 100644 --- a/osu.Game/Skinning/LegacyAccuracyCounter.cs +++ b/osu.Game/Skinning/LegacyAccuracyCounter.cs @@ -11,7 +11,7 @@ using osuTK; namespace osu.Game.Skinning { - public class LegacyAccuracyCounter : PercentageCounter, IAccuracyCounter + public class LegacyAccuracyCounter : PercentageCounter, IAccuracyCounter, ISkinnableComponent { private readonly ISkin skin; diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index 2921d46467..2e29abf453 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -16,7 +16,7 @@ using osuTK.Graphics; namespace osu.Game.Skinning { - public class LegacyHealthDisplay : CompositeDrawable, IHealthDisplay + public class LegacyHealthDisplay : CompositeDrawable, IHealthDisplay, ISkinnableComponent { private const double epic_cutoff = 0.5; diff --git a/osu.Game/Skinning/LegacyScoreCounter.cs b/osu.Game/Skinning/LegacyScoreCounter.cs index 1d330ef495..cae8044242 100644 --- a/osu.Game/Skinning/LegacyScoreCounter.cs +++ b/osu.Game/Skinning/LegacyScoreCounter.cs @@ -5,11 +5,12 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Play.HUD; using osuTK; namespace osu.Game.Skinning { - public class LegacyScoreCounter : ScoreCounter + public class LegacyScoreCounter : ScoreCounter, ISkinnableComponent { private readonly ISkin skin; From 4f9e1e4945d002ea20a35855bd2bcdd160aa50eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 18:59:55 +0900 Subject: [PATCH 047/108] Check for new components every one second to handle late loaders --- osu.Game/Skinning/Editor/SkinBlueprintContainer.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index f997e84355..f2bc8ecddf 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -23,9 +23,14 @@ namespace osu.Game.Skinning.Editor { base.LoadComplete(); - ISkinnableComponent[] components = target.ChildrenOfType().ToArray(); + checkForComponents(); + } - foreach (var c in components) AddBlueprintFor(c); + private void checkForComponents() + { + foreach (var c in target.ChildrenOfType().ToArray()) AddBlueprintFor(c); + + Scheduler.AddDelayed(checkForComponents, 1000); } protected override SelectionHandler CreateSelectionHandler() => new SkinSelectionHandler(); From 74c6fdc4b8e52611b8e51645e19f27b9e042e5e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Apr 2021 19:00:16 +0900 Subject: [PATCH 048/108] Add `DrawableRuleset` to the skin editor test to get a hit error meter to display --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index e46cd70667..eaa0c29616 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -2,10 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; using osu.Game.Skinning.Editor; using osuTK.Input; @@ -21,13 +26,32 @@ namespace osu.Game.Tests.Visual.Gameplay { SetContents(() => { - var hudOverlay = new HUDOverlay(null, null, null, Array.Empty()); + var ruleset = new OsuRuleset(); + var working = CreateWorkingBeatmap(ruleset.RulesetInfo); + var beatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo); + + ScoreProcessor scoreProcessor = new ScoreProcessor(); + + var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap); + + var hudOverlay = new HUDOverlay(scoreProcessor, null, drawableRuleset, Array.Empty()); // Add any key just to display the key counter visually. hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); hudOverlay.ComboCounter.Current.Value = 1; - return new SkinEditor(hudOverlay); + // Apply a miss judgement + scoreProcessor.ApplyResult(new JudgementResult(new HitObject(), new Judgement()) { Type = HitResult.Good }); + + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + drawableRuleset, + new SkinEditor(hudOverlay), + } + }; }); }); } From de73ac7ceccfa3d518baa6ec93190efda3ea2d24 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 13:53:01 +0900 Subject: [PATCH 049/108] Allow skin editor to be invoked from any context This is kind of how I see things working going forward, where the editor can be applied to anything in the game which supports it (ie. a results screen, gameplay screen, etc.) and it will immediately allow changing the interface. This adds a test scene which shows this working with gameplay. --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 59 +++++------------ .../TestSceneSkinEditorMultipleSkins.cs | 61 +++++++++++++++++ osu.Game/Skinning/Editor/SkinEditor.cs | 66 +++++++++++++++++-- 3 files changed, 138 insertions(+), 48 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index eaa0c29616..0c2c6ed454 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -1,61 +1,36 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Rulesets; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play; using osu.Game.Skinning.Editor; -using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSkinEditor : SkinnableTestScene + public class TestSceneSkinEditor : PlayerTestScene { + private SkinEditor skinEditor; + [SetUpSteps] - public void SetUpSteps() + public override void SetUpSteps() { - AddStep("create editor overlay", () => + base.SetUpSteps(); + + AddStep("add editor overlay", () => { - SetContents(() => - { - var ruleset = new OsuRuleset(); - var working = CreateWorkingBeatmap(ruleset.RulesetInfo); - var beatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo); - - ScoreProcessor scoreProcessor = new ScoreProcessor(); - - var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap); - - var hudOverlay = new HUDOverlay(scoreProcessor, null, drawableRuleset, Array.Empty()); - - // Add any key just to display the key counter visually. - hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); - hudOverlay.ComboCounter.Current.Value = 1; - - // Apply a miss judgement - scoreProcessor.ApplyResult(new JudgementResult(new HitObject(), new Judgement()) { Type = HitResult.Good }); - - return new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - drawableRuleset, - new SkinEditor(hudOverlay), - } - }; - }); + skinEditor?.Expire(); + LoadComponentAsync(skinEditor = new SkinEditor(Player), Add); }); } - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + [Test] + public void TestToggleEditor() + { + AddToggleStep("toggle editor visibility", visible => skinEditor.ToggleVisibility()); + } + + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs new file mode 100644 index 0000000000..086bcb19c3 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -0,0 +1,61 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play; +using osu.Game.Skinning.Editor; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinEditorMultipleSkins : SkinnableTestScene + { + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create editor overlay", () => + { + SetContents(() => + { + var ruleset = new OsuRuleset(); + var working = CreateWorkingBeatmap(ruleset.RulesetInfo); + var beatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo); + + ScoreProcessor scoreProcessor = new ScoreProcessor(); + + var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap); + + var hudOverlay = new HUDOverlay(scoreProcessor, null, drawableRuleset, Array.Empty()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + // Add any key just to display the key counter visually. + hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); + hudOverlay.ComboCounter.Current.Value = 1; + + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + drawableRuleset, + hudOverlay, + new SkinEditor(hudOverlay), + } + }; + }); + }); + } + + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + } +} diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 74d32bcb9f..4f81fad4ba 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -1,15 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osu.Game.Graphics.Cursor; namespace osu.Game.Skinning.Editor { - public class SkinEditor : CompositeDrawable + public class SkinEditor : VisibilityContainer { private readonly Drawable target; + private Container border; + + protected override bool StartHidden => true; public SkinEditor(Drawable target) { @@ -18,19 +24,67 @@ namespace osu.Game.Skinning.Editor RelativeSizeAxes = Axes.Both; } - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load(OsuColour colours) { - base.LoadComplete(); - InternalChild = new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Children = new[] + Children = new Drawable[] { - target, + border = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderColour = colours.Yellow, + BorderThickness = 5, + CornerRadius = 5, + Children = new Drawable[] + { + new Box + { + AlwaysPresent = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both, + }, + } + }, new SkinBlueprintContainer(target), } }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + Show(); + } + + private const double transition_duration = 500; + private const float visible_target_scale = 0.8f; + + protected override void PopIn() + { + if (IsLoaded) + { + target.ScaleTo(visible_target_scale, transition_duration, Easing.OutQuint); + border.ScaleTo(visible_target_scale, transition_duration, Easing.OutQuint); + } + + this.FadeIn(transition_duration); + } + + protected override void PopOut() + { + if (IsLoaded) + { + target.ScaleTo(1, transition_duration, Easing.OutQuint); + border.ScaleTo(1, transition_duration, Easing.OutQuint); + } + + this.FadeOut(transition_duration, Easing.OutQuint); + } } } From 3c84b0d8c624db113873e8b53a4f1f6cf491ba0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 14:07:15 +0900 Subject: [PATCH 050/108] Fix selection screen point being wrong since recent refactors --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index 674153cb26..2cee94b2c6 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -78,10 +78,10 @@ namespace osu.Game.Skinning.Editor public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => drawable.ReceivePositionalInputAt(screenSpacePos); - public override Vector2 ScreenSpaceSelectionPoint => drawable.ToScreenSpace(Vector2.Zero); + public override Vector2 ScreenSpaceSelectionPoint => drawable.Parent.ToScreenSpace(drawable.DrawPosition); public override Quad SelectionQuad => drawable.ScreenSpaceDrawQuad; - public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Component.Parent.ToLocalSpace(screenSpacePosition) - drawable.Position; + public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Component.Parent.ToLocalSpace(screenSpacePosition) - drawable.DrawPosition; } } From 434e63d6297c9e6db9ed3a70562faa519c67ac8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 14:14:04 +0900 Subject: [PATCH 051/108] Add skin customisation support to song progress display --- osu.Game/Screens/Play/SongProgress.cs | 57 +++++++++++++++++---------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 6c7cb9376c..db81633aea 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -14,6 +14,7 @@ using osu.Framework.Timing; using osu.Game.Configuration; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Screens.Play { @@ -71,30 +72,38 @@ namespace osu.Game.Screens.Play public SongProgress() { - Masking = true; - Children = new Drawable[] { - info = new SongProgressInfo + new SongProgressDisplay { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = info_height, - }, - graph = new SongProgressGraph - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Height = graph_height, - Margin = new MarginPadding { Bottom = bottom_bar_height }, - }, - bar = new SongProgressBar(bottom_bar_height, graph_height, handle_size) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - OnSeek = time => RequestSeek?.Invoke(time), + Masking = true, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Children = new Drawable[] + { + info = new SongProgressInfo + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = info_height, + }, + graph = new SongProgressGraph + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Height = graph_height, + Margin = new MarginPadding { Bottom = bottom_bar_height }, + }, + bar = new SongProgressBar(bottom_bar_height, graph_height, handle_size) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + OnSeek = time => RequestSeek?.Invoke(time), + }, + } }, }; } @@ -175,5 +184,11 @@ namespace osu.Game.Screens.Play float finalMargin = bottom_bar_height + (AllowSeeking.Value ? handle_size.Y : 0) + (ShowGraph.Value ? graph_height : 0); info.TransformTo(nameof(info.Margin), new MarginPadding { Bottom = finalMargin }, transition_duration, Easing.In); } + + public class SongProgressDisplay : Container, ISkinnableComponent + { + // TODO: move actual implementation into this. + // exists for skin customisation purposes. + } } } From 1516e2ffef7228cff1f7d3c70ba7224dd9345a47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 15:29:25 +0900 Subject: [PATCH 052/108] Update blueprint implementation in line with #12625. --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 4 +--- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index 2cee94b2c6..491a403325 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -78,10 +78,8 @@ namespace osu.Game.Skinning.Editor public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => drawable.ReceivePositionalInputAt(screenSpacePos); - public override Vector2 ScreenSpaceSelectionPoint => drawable.Parent.ToScreenSpace(drawable.DrawPosition); + public override Vector2 ScreenSpaceSelectionPoint => drawable.ScreenSpaceDrawQuad.Centre; public override Quad SelectionQuad => drawable.ScreenSpaceDrawQuad; - - public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Component.Parent.ToLocalSpace(screenSpacePosition) - drawable.DrawPosition; } } diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 38404efd0c..466136f0a8 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; +using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Compose.Components; @@ -107,7 +108,8 @@ namespace osu.Game.Skinning.Editor { foreach (var c in SelectedBlueprints) { - ((Drawable)c.Item).Position += moveEvent.InstantDelta; + Drawable drawable = (Drawable)c.Item; + drawable.Position += drawable.ScreenSpaceDeltaToParentSpace(moveEvent.ScreenSpaceDelta); } return true; From b460181f15ea13569fdce9a557cda060b6775634 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 16:16:52 +0900 Subject: [PATCH 053/108] Add note about rotation not working as expected --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 466136f0a8..17b459a916 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -88,6 +88,7 @@ namespace osu.Game.Skinning.Editor public override bool HandleRotation(float angle) { + // TODO: this doesn't correctly account for origin/anchor specs being different in a multi-selection. foreach (var c in SelectedBlueprints) ((Drawable)c.Item).Rotation += angle; From 141d3af302e1443af684b8658e8eca180d9e746a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 17:19:47 +0900 Subject: [PATCH 054/108] Add the ability to temporarily disable user scaling on a `ScalingContainer` --- .../Graphics/Containers/ScalingContainer.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index 8f07c3a656..b691e372c5 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -36,6 +36,24 @@ namespace osu.Game.Graphics.Containers private BackgroundScreenStack backgroundStack; + private bool allowScaling = true; + + /// + /// Whether user scaling preferences should be applied. Enabled by default. + /// + public bool AllowScaling + { + get => allowScaling; + set + { + if (value == allowScaling) + return; + + allowScaling = value; + updateSize(); + } + } + /// /// Create a new instance. /// @@ -139,7 +157,7 @@ namespace osu.Game.Graphics.Containers backgroundStack?.FadeOut(fade_time); } - bool scaling = targetMode == null || scalingMode.Value == targetMode; + bool scaling = AllowScaling && (targetMode == null || scalingMode.Value == targetMode); var targetSize = scaling ? new Vector2(sizeX.Value, sizeY.Value) : Vector2.One; var targetPosition = scaling ? new Vector2(posX.Value, posY.Value) * (Vector2.One - targetSize) : Vector2.Zero; From b93604395673af6dc74631f6601a241d06aac53f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 17:20:22 +0900 Subject: [PATCH 055/108] Add the skin editor to the game --- .../Input/Bindings/GlobalActionContainer.cs | 6 +- osu.Game/OsuGame.cs | 8 ++ osu.Game/Skinning/Editor/SkinEditor.cs | 50 +++-------- .../Skinning/Editor/SkinEditorContainer.cs | 87 +++++++++++++++++++ 4 files changed, 110 insertions(+), 41 deletions(-) create mode 100644 osu.Game/Skinning/Editor/SkinEditorContainer.cs diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 6717de5658..ce945f3bf8 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -48,6 +48,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleBeatmapListing), new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications), + new KeyBinding(new[] { InputKey.Shift, InputKey.F2 }, GlobalAction.ToggleSkinEditor), new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back), @@ -258,6 +259,9 @@ namespace osu.Game.Input.Bindings EditorNudgeLeft, [Description("Nudge selection right")] - EditorNudgeRight + EditorNudgeRight, + + [Description("Toggle skin editor")] + ToggleSkinEditor, } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 28f32ba455..67e0e6a81c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -51,6 +51,7 @@ using osu.Game.Utils; using LogLevel = osu.Framework.Logging.LogLevel; using osu.Game.Database; using osu.Game.IO; +using osu.Game.Skinning.Editor; namespace osu.Game { @@ -597,6 +598,8 @@ namespace osu.Game screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays) { RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Children = new Drawable[] { receptor = new BackButton.Receptor(), @@ -616,6 +619,7 @@ namespace osu.Game logoContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, + skinEditor = new SkinEditorContainer(screenContainer), overlayContent = new Container { RelativeSizeAxes = Axes.Both }, rightFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, @@ -942,6 +946,8 @@ namespace osu.Game private ScalingContainer screenContainer; + private SkinEditorContainer skinEditor; + protected override bool OnExiting() { if (ScreenStack.CurrentScreen is Loader) @@ -968,6 +974,8 @@ namespace osu.Game protected virtual void ScreenChanged(IScreen current, IScreen newScreen) { + skinEditor.Reset(); + switch (newScreen) { case IntroScreen intro: diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 4f81fad4ba..fdef4d0fce 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -4,16 +4,16 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; +using osu.Framework.Input.Events; using osu.Game.Graphics.Cursor; namespace osu.Game.Skinning.Editor { - public class SkinEditor : VisibilityContainer + public class SkinEditor : FocusedOverlayContainer { + public const double TRANSITION_DURATION = 500; + private readonly Drawable target; - private Container border; protected override bool StartHidden => true; @@ -25,32 +25,13 @@ namespace osu.Game.Skinning.Editor } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { InternalChild = new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - border = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderColour = colours.Yellow, - BorderThickness = 5, - CornerRadius = 5, - Children = new Drawable[] - { - new Box - { - AlwaysPresent = true, - Alpha = 0, - RelativeSizeAxes = Axes.Both, - }, - } - }, new SkinBlueprintContainer(target), } }; @@ -62,29 +43,18 @@ namespace osu.Game.Skinning.Editor Show(); } - private const double transition_duration = 500; - private const float visible_target_scale = 0.8f; + protected override bool OnHover(HoverEvent e) => true; + + protected override bool OnMouseDown(MouseDownEvent e) => true; protected override void PopIn() { - if (IsLoaded) - { - target.ScaleTo(visible_target_scale, transition_duration, Easing.OutQuint); - border.ScaleTo(visible_target_scale, transition_duration, Easing.OutQuint); - } - - this.FadeIn(transition_duration); + this.FadeIn(TRANSITION_DURATION, Easing.OutQuint); } protected override void PopOut() { - if (IsLoaded) - { - target.ScaleTo(1, transition_duration, Easing.OutQuint); - border.ScaleTo(1, transition_duration, Easing.OutQuint); - } - - this.FadeOut(transition_duration, Easing.OutQuint); + this.FadeOut(TRANSITION_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Skinning/Editor/SkinEditorContainer.cs b/osu.Game/Skinning/Editor/SkinEditorContainer.cs new file mode 100644 index 0000000000..f84b70d67a --- /dev/null +++ b/osu.Game/Skinning/Editor/SkinEditorContainer.cs @@ -0,0 +1,87 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Input.Bindings; + +namespace osu.Game.Skinning.Editor +{ + /// + /// A container which handles loading a skin editor on user request. + /// + public class SkinEditorContainer : CompositeDrawable, IKeyBindingHandler + { + private readonly ScalingContainer target; + private SkinEditor skinEditor; + + private const float visible_target_scale = 0.8f; + + [Resolved] + private OsuColour colours { get; set; } + + public SkinEditorContainer(ScalingContainer target) + { + this.target = target; + RelativeSizeAxes = Axes.Both; + } + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.ToggleSkinEditor: + if (skinEditor == null) + { + LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); + skinEditor.State.BindValueChanged(editorVisibilityChanged); + } + else + skinEditor.ToggleVisibility(); + + return true; + } + + return false; + } + + private void editorVisibilityChanged(ValueChangedEvent visibility) + { + if (visibility.NewValue == Visibility.Visible) + { + target.ScaleTo(visible_target_scale, SkinEditor.TRANSITION_DURATION, Easing.OutQuint); + + target.Masking = true; + target.BorderThickness = 5; + target.BorderColour = colours.Yellow; + target.AllowScaling = false; + } + else + { + target.BorderThickness = 0; + target.AllowScaling = true; + + target.ScaleTo(1, SkinEditor.TRANSITION_DURATION, Easing.OutQuint).OnComplete(_ => target.Masking = false); + } + } + + public void OnReleased(GlobalAction action) + { + } + + /// + /// Exit any existing skin editor due to the game state changing. + /// + public void Reset() + { + skinEditor?.Hide(); + skinEditor?.Expire(); + skinEditor = null; + } + } +} From a7982787d446198a2aa95f18eab524fcd3a59d46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 17:26:55 +0900 Subject: [PATCH 056/108] Add basic header text --- osu.Game/Skinning/Editor/SkinEditor.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index fdef4d0fce..532c3de7fb 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -5,6 +5,8 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; namespace osu.Game.Skinning.Editor @@ -15,6 +17,8 @@ namespace osu.Game.Skinning.Editor private readonly Drawable target; + private OsuTextFlowContainer headerText; + protected override bool StartHidden => true; public SkinEditor(Drawable target) @@ -25,16 +29,31 @@ namespace osu.Game.Skinning.Editor } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { InternalChild = new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + headerText = new OsuTextFlowContainer() + { + TextAnchor = Anchor.TopCentre, + Padding = new MarginPadding(20), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X + }, new SkinBlueprintContainer(target), } }; + + headerText.AddParagraph("Skin editor (preview)", cp => cp.Font = OsuFont.Default.With(size: 24)); + headerText.AddParagraph("This is a preview of what is to come. Changes are lost on changing screens.", cp => + { + cp.Font = OsuFont.Default.With(size: 12); + cp.Colour = colours.Yellow; + }); } protected override void LoadComplete() From fb64f6faf205f8c3c5d46f52fabe016ac0782ea9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 17:40:58 +0900 Subject: [PATCH 057/108] Add ability to exit using game "back" binding --- osu.Game/Skinning/Editor/SkinEditorContainer.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Skinning/Editor/SkinEditorContainer.cs b/osu.Game/Skinning/Editor/SkinEditorContainer.cs index f84b70d67a..adb1abefd1 100644 --- a/osu.Game/Skinning/Editor/SkinEditorContainer.cs +++ b/osu.Game/Skinning/Editor/SkinEditorContainer.cs @@ -35,6 +35,15 @@ namespace osu.Game.Skinning.Editor { switch (action) { + case GlobalAction.Back: + if (skinEditor?.State.Value == Visibility.Visible) + { + skinEditor.ToggleVisibility(); + return true; + } + + break; + case GlobalAction.ToggleSkinEditor: if (skinEditor == null) { From 63435ba5482b8c832ea5f1eab612054c5b9b1e6f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 17:41:07 +0900 Subject: [PATCH 058/108] Ensure toolbar doesn't overlap editor content --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 67e0e6a81c..77fb9c3b63 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -619,7 +619,6 @@ namespace osu.Game logoContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, - skinEditor = new SkinEditorContainer(screenContainer), overlayContent = new Container { RelativeSizeAxes = Axes.Both }, rightFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, @@ -689,6 +688,7 @@ namespace osu.Game var changelogOverlay = loadComponentSingleFile(new ChangelogOverlay(), overlayContent.Add, true); loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); + loadComponentSingleFile(skinEditor = new SkinEditorContainer(screenContainer), overlayContent.Add); loadComponentSingleFile(new LoginOverlay { From 8d056ff38f6685ada5be49654838be7be4ad49d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 29 Apr 2021 18:23:22 +0900 Subject: [PATCH 059/108] Remove redundant parenthesis --- osu.Game/Skinning/Editor/SkinEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 532c3de7fb..562dd23224 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -36,7 +36,7 @@ namespace osu.Game.Skinning.Editor RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - headerText = new OsuTextFlowContainer() + headerText = new OsuTextFlowContainer { TextAnchor = Anchor.TopCentre, Padding = new MarginPadding(20), From cdef07b2eee4cb587fcda722febe81cd9b10366e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Apr 2021 13:09:57 +0900 Subject: [PATCH 060/108] Fix blueprints not hiding when deleting elements --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 17b459a916..0408ce74a6 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -20,7 +20,10 @@ namespace osu.Game.Skinning.Editor protected override void DeleteItems(IEnumerable items) { foreach (var i in items) - i.Hide(); + { + ((Drawable)i).Expire(); + SelectedItems.Remove(i); + } } protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable> selection) From b2130fc600d8cff184ba6e51bc3ef7dbeed2f44c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 3 May 2021 01:56:32 +0300 Subject: [PATCH 061/108] Fix replay frames sort instability --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 0f25a45177..d6c9b9c6d9 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -4,7 +4,7 @@ #nullable enable using System; -using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using osu.Game.Input.Handlers; using osu.Game.Replays; @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Replays { // TODO: This replay frame ordering should be enforced on the Replay type. // Currently, the ordering can be broken if the frames are added after this construction. - replay.Frames.Sort((x, y) => x.Time.CompareTo(y.Time)); + replay.Frames = replay.Frames.OrderBy(f => f.Time).ToList(); this.replay = replay; currentFrameIndex = -1; From 943c497397245fdf1924860b951c89940e3b235e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 3 May 2021 02:02:14 +0300 Subject: [PATCH 062/108] Return back removed using --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index d6c9b9c6d9..bc8994bbe5 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using osu.Game.Input.Handlers; From f3b305bbe62f4501579cd36ca6e77c188fb344ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 14:58:23 +0900 Subject: [PATCH 063/108] Rename and improve xmldoc of `SkinEditorOverlay` --- osu.Game/OsuGame.cs | 4 ++-- .../{SkinEditorContainer.cs => SkinEditorOverlay.cs} | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) rename osu.Game/Skinning/Editor/{SkinEditorContainer.cs => SkinEditorOverlay.cs} (91%) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 77fb9c3b63..d031a8e7fa 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -688,7 +688,7 @@ namespace osu.Game var changelogOverlay = loadComponentSingleFile(new ChangelogOverlay(), overlayContent.Add, true); loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); - loadComponentSingleFile(skinEditor = new SkinEditorContainer(screenContainer), overlayContent.Add); + loadComponentSingleFile(skinEditor = new SkinEditorOverlay(screenContainer), overlayContent.Add); loadComponentSingleFile(new LoginOverlay { @@ -946,7 +946,7 @@ namespace osu.Game private ScalingContainer screenContainer; - private SkinEditorContainer skinEditor; + private SkinEditorOverlay skinEditor; protected override bool OnExiting() { diff --git a/osu.Game/Skinning/Editor/SkinEditorContainer.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs similarity index 91% rename from osu.Game/Skinning/Editor/SkinEditorContainer.cs rename to osu.Game/Skinning/Editor/SkinEditorOverlay.cs index adb1abefd1..06c6dffb60 100644 --- a/osu.Game/Skinning/Editor/SkinEditorContainer.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -13,9 +13,10 @@ using osu.Game.Input.Bindings; namespace osu.Game.Skinning.Editor { /// - /// A container which handles loading a skin editor on user request. + /// A container which handles loading a skin editor on user request for a specified target. + /// This also handles the scaling / positioning adjustment of the target. /// - public class SkinEditorContainer : CompositeDrawable, IKeyBindingHandler + public class SkinEditorOverlay : CompositeDrawable, IKeyBindingHandler { private readonly ScalingContainer target; private SkinEditor skinEditor; @@ -25,7 +26,7 @@ namespace osu.Game.Skinning.Editor [Resolved] private OsuColour colours { get; set; } - public SkinEditorContainer(ScalingContainer target) + public SkinEditorOverlay(ScalingContainer target) { this.target = target; RelativeSizeAxes = Axes.Both; From 01984de9c7ce603e063f914942775d55fe8793e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 15:13:32 +0900 Subject: [PATCH 064/108] Use existing `GetStateFromSelection` helper function --- .../Compose/Components/EditorSelectionHandler.cs | 11 ----------- .../Edit/Compose/Components/SelectionHandler.cs | 11 +++++++++++ osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 14 +------------- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 0fc305dcc4..6ab4ca8267 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -108,17 +108,6 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - /// - /// Given a selection target and a function of truth, retrieve the correct ternary state for display. - /// - protected TernaryState GetStateFromSelection(IEnumerable selection, Func func) - { - if (selection.Any(func)) - return selection.All(func) ? TernaryState.True : TernaryState.Indeterminate; - - return TernaryState.False; - } - #endregion #region Ternary state changes diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index cb3424a250..c0dbc5e7db 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -268,6 +268,17 @@ namespace osu.Game.Screens.Edit.Compose.Components DeleteSelected(); } + /// + /// Given a selection target and a function of truth, retrieve the correct ternary state for display. + /// + protected static TernaryState GetStateFromSelection(IEnumerable selection, Func func) + { + if (selection.Any(func)) + return selection.All(func) ? TernaryState.True : TernaryState.Indeterminate; + + return TernaryState.False; + } + /// /// Called whenever the deletion of items has been requested. /// diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 0408ce74a6..044ad88333 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -53,21 +53,9 @@ namespace osu.Game.Skinning.Editor return displayableAnchors.Select(a => { - var countExisting = selection.Count(b => ((Drawable)b.Item).Anchor == a); - var countTotal = selection.Count(); - - TernaryState state; - - if (countExisting == countTotal) - state = TernaryState.True; - else if (countExisting > 0) - state = TernaryState.Indeterminate; - else - state = TernaryState.False; - return new AnchorMenuItem(a, selection, _ => applyAnchor(a)) { - State = { Value = state } + State = { Value = GetStateFromSelection(selection, c => ((Drawable)c.Item).Anchor == a) } }; }); } From a2faa0b74c5c10b89d5f06ded55abe6d68c9bfbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 15:13:53 +0900 Subject: [PATCH 065/108] Remove dead code --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 044ad88333..8792daef19 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -125,11 +125,6 @@ namespace osu.Game.Skinning.Editor { } - private void updateState(TernaryState obj) - { - throw new NotImplementedException(); - } - private static TernaryState getNextState(TernaryState state) => TernaryState.True; } } From 51f4077b2714b61db68db48706546c86a1125238 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 15:15:00 +0900 Subject: [PATCH 066/108] Reorder methods in `SkinSelectionHandler` to follow standards --- .../Skinning/Editor/SkinSelectionHandler.cs | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 8792daef19..c7c0f45cc0 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -17,6 +17,46 @@ namespace osu.Game.Skinning.Editor { public class SkinSelectionHandler : SelectionHandler { + public override bool HandleRotation(float angle) + { + // TODO: this doesn't correctly account for origin/anchor specs being different in a multi-selection. + foreach (var c in SelectedBlueprints) + ((Drawable)c.Item).Rotation += angle; + + return base.HandleRotation(angle); + } + + public override bool HandleScale(Vector2 scale, Anchor anchor) + { + adjustScaleFromAnchor(ref scale, anchor); + + foreach (var c in SelectedBlueprints) + ((Drawable)c.Item).Scale += scale * 0.01f; + + return true; + } + + public override bool HandleMovement(MoveSelectionEvent moveEvent) + { + foreach (var c in SelectedBlueprints) + { + Drawable drawable = (Drawable)c.Item; + drawable.Position += drawable.ScreenSpaceDeltaToParentSpace(moveEvent.ScreenSpaceDelta); + } + + return true; + } + + protected override void OnSelectionChanged() + { + base.OnSelectionChanged(); + + SelectionBox.CanRotate = true; + SelectionBox.CanScaleX = true; + SelectionBox.CanScaleY = true; + SelectionBox.CanReverse = false; + } + protected override void DeleteItems(IEnumerable items) { foreach (var i in items) @@ -67,46 +107,6 @@ namespace osu.Game.Skinning.Editor ((Drawable)item).Anchor = anchor; } - protected override void OnSelectionChanged() - { - base.OnSelectionChanged(); - - SelectionBox.CanRotate = true; - SelectionBox.CanScaleX = true; - SelectionBox.CanScaleY = true; - SelectionBox.CanReverse = false; - } - - public override bool HandleRotation(float angle) - { - // TODO: this doesn't correctly account for origin/anchor specs being different in a multi-selection. - foreach (var c in SelectedBlueprints) - ((Drawable)c.Item).Rotation += angle; - - return base.HandleRotation(angle); - } - - public override bool HandleScale(Vector2 scale, Anchor anchor) - { - adjustScaleFromAnchor(ref scale, anchor); - - foreach (var c in SelectedBlueprints) - ((Drawable)c.Item).Scale += scale * 0.01f; - - return true; - } - - public override bool HandleMovement(MoveSelectionEvent moveEvent) - { - foreach (var c in SelectedBlueprints) - { - Drawable drawable = (Drawable)c.Item; - drawable.Position += drawable.ScreenSpaceDeltaToParentSpace(moveEvent.ScreenSpaceDelta); - } - - return true; - } - private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) { // cancel out scale in axes we don't care about (based on which drag handle was used). From f36684a070308f6068a709b68dba2d67906bb632 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 15:17:01 +0900 Subject: [PATCH 067/108] Guard against non-threadsafe transformation logic in `ScalingContainer`- --- osu.Game/Graphics/Containers/ScalingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index b691e372c5..2488fd14d0 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -50,7 +50,7 @@ namespace osu.Game.Graphics.Containers return; allowScaling = value; - updateSize(); + if (IsLoaded) updateSize(); } } From df8609b3dc6307a7d9e91b5f6a2df13a2b5d824d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 15:15:50 +0900 Subject: [PATCH 068/108] Move private field for skin editor overlay to where others exist --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d031a8e7fa..b1173784b5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -80,6 +80,8 @@ namespace osu.Game private BeatmapSetOverlay beatmapSetOverlay; + private SkinEditorOverlay skinEditor; + [Cached] private readonly DifficultyRecommender difficultyRecommender = new DifficultyRecommender(); @@ -946,8 +948,6 @@ namespace osu.Game private ScalingContainer screenContainer; - private SkinEditorOverlay skinEditor; - protected override bool OnExiting() { if (ScreenStack.CurrentScreen is Loader) From a298a930701b337248823aa901364786c4ef19d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 15:18:18 +0900 Subject: [PATCH 069/108] Remove redundant storage of blueprint's related item --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index 491a403325..11409c46ab 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Screens.Play.HUD; using osuTK; @@ -17,26 +16,20 @@ namespace osu.Game.Skinning.Editor { public class SkinBlueprint : SelectionBlueprint { - /// - /// The which this applies to. - /// - public readonly ISkinnableComponent Component; - private Container box; - private Drawable drawable => (Drawable)Component; + private Drawable drawable => (Drawable)Item; /// - /// Whether the blueprint should be shown even when the is not alive. + /// Whether the blueprint should be shown even when the is not alive. /// protected virtual bool AlwaysShowWhenSelected => false; - protected override bool ShouldBeAlive => (drawable.IsAlive && Component.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); + protected override bool ShouldBeAlive => (drawable.IsAlive && Item.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); public SkinBlueprint(ISkinnableComponent component) : base(component) { - Component = component; } [BackgroundDependencyLoader] From 7d8be8cd836dda2a401f4de40d70a680f7e6e958 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 15:20:00 +0900 Subject: [PATCH 070/108] Add comment about why we are running `checkForComponents` on a timer --- osu.Game/Skinning/Editor/SkinBlueprintContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index f2bc8ecddf..d9bfefe5f2 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -30,6 +30,8 @@ namespace osu.Game.Skinning.Editor { foreach (var c in target.ChildrenOfType().ToArray()) AddBlueprintFor(c); + // We'd hope to eventually be running this in a more sensible way, but this handles situations where new drawables become present (ie. during ongoing gameplay) + // or when drawables in the target are loaded asynchronously and may not be immediately available when this BlueprintContainer is loaded. Scheduler.AddDelayed(checkForComponents, 1000); } From 15603de6e946c4570cb3c9befc68114e162a0c11 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 15:24:51 +0900 Subject: [PATCH 071/108] Change scale multiplier to be closer to expectations --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index c7c0f45cc0..d09ba8af0e 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -31,7 +31,8 @@ namespace osu.Game.Skinning.Editor adjustScaleFromAnchor(ref scale, anchor); foreach (var c in SelectedBlueprints) - ((Drawable)c.Item).Scale += scale * 0.01f; + // TODO: this is temporary and will be fixed with a separate refactor of selection transform logic. + ((Drawable)c.Item).Scale += scale * 0.02f; return true; } From 4cfa858dc4cd02cbf438ea9863d57a1c26568c63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 May 2021 15:37:15 +0900 Subject: [PATCH 072/108] Fix tooltips displaying for hidden `SelectionHandler` content --- osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index c0dbc5e7db..053e532137 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -57,7 +57,6 @@ namespace osu.Game.Screens.Edit.Compose.Components RelativeSizeAxes = Axes.Both; AlwaysPresent = true; - Alpha = 0; } [BackgroundDependencyLoader] @@ -318,7 +317,7 @@ namespace osu.Game.Screens.Edit.Compose.Components selectionDetailsText.Text = count > 0 ? count.ToString() : string.Empty; - this.FadeTo(count > 0 ? 1 : 0); + content.FadeTo(count > 0 ? 1 : 0); OnSelectionChanged(); } From e00af3e71d2f2f754e641698dede05e3ac089b5b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 4 May 2021 09:45:58 +0300 Subject: [PATCH 073/108] Add test coverage --- .../NonVisual/FramedReplayInputHandlerTest.cs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index a42b7d54ee..2062c4b820 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; +using osu.Framework.Utils; using osu.Game.Replays; using osu.Game.Rulesets.Replays; @@ -278,6 +280,38 @@ namespace osu.Game.Tests.NonVisual setTime(-100, -100); } + [Test] + public void TestReplayFrameSortStability() + { + const double repeating_time = 5000; + + int data = 0; + + // 1. add a range of frames in which some of them have the constant time 5000, all without any "data". + // 2. randomize the frames. + // 3. assign "data" to each frame in ascending order. + replay.Frames.AddRange(Enumerable.Range(1, 250).Select(i => + { + if (RNG.NextBool()) + return new TestReplayFrame(repeating_time, true); + else + return new TestReplayFrame(i * 1000, true); + }).OrderBy(_ => RNG.Next()).Select(f => new TestReplayFrame(f.Time, true, ++data))); + + replay.HasReceivedAllFrames = true; + + // create a new handler with the replay for the frames to be sorted. + handler = new TestInputHandler(replay); + + // ensure sort stability by checking whether the "data" assigned to each time-repeated frame is in ascending order, as it was before sort. + var repeatingTimeFramesData = replay.Frames + .Cast() + .Where(f => f.Time == repeating_time) + .Select(f => f.Data); + + Assert.That(repeatingTimeFramesData, Is.Ordered.Ascending); + } + private void setReplayFrames() { replay.Frames = new List @@ -324,11 +358,13 @@ namespace osu.Game.Tests.NonVisual private class TestReplayFrame : ReplayFrame { public readonly bool IsImportant; + public readonly int Data; - public TestReplayFrame(double time, bool isImportant = false) + public TestReplayFrame(double time, bool isImportant = false, int data = 0) : base(time) { IsImportant = isImportant; + Data = data; } } From ffc88db47aa7377e78a6db933723a314a937e933 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 May 2021 16:04:59 +0900 Subject: [PATCH 074/108] Implement Duration via the interface --- osu.Game/Storyboards/IStoryboardElementWithDuration.cs | 2 +- osu.Game/Storyboards/StoryboardSprite.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Storyboards/IStoryboardElementWithDuration.cs b/osu.Game/Storyboards/IStoryboardElementWithDuration.cs index 02b438cb76..55f163ee07 100644 --- a/osu.Game/Storyboards/IStoryboardElementWithDuration.cs +++ b/osu.Game/Storyboards/IStoryboardElementWithDuration.cs @@ -16,6 +16,6 @@ namespace osu.Game.Storyboards /// /// The duration of the StoryboardElement. /// - double Duration { get; } + double Duration => EndTime - StartTime; } } diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 0aaf264341..bf87e7d10e 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -65,8 +65,6 @@ namespace osu.Game.Storyboards } } - public double Duration => EndTime - StartTime; - public bool HasCommands => TimelineGroup.HasCommands || loops.Any(l => l.HasCommands); private delegate void DrawablePropertyInitializer(Drawable drawable, T value); From b30145de40646e893d3123133fd46d67e52c23b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 May 2021 16:35:50 +0900 Subject: [PATCH 075/108] Specify explicit primitive type --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 1a11adad30..d424f90079 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -640,7 +640,7 @@ namespace osu.Game.Screens.Play return; } - var storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; + bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; if (storyboardHasOutro) { From 4c7a4239f818662055b08f610b7ab588a3520042 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 May 2021 16:36:05 +0900 Subject: [PATCH 076/108] Fix `AllowGameplayOverlays` potentially not working for outro skip overlay --- osu.Game/Screens/Play/Player.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d424f90079..6ed1a87d8a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -245,8 +245,6 @@ namespace osu.Game.Screens.Play HUDOverlay.ShowHud.Value = false; HUDOverlay.ShowHud.Disabled = true; BreakOverlay.Hide(); - skipIntroOverlay.Hide(); - skipOutroOverlay.Hide(); } DrawableRuleset.FrameStableClock.WaitingOnFrames.BindValueChanged(waiting => @@ -398,7 +396,7 @@ namespace osu.Game.Screens.Play } }; - if (!Configuration.AllowSkipping) + if (!Configuration.AllowSkipping || !DrawableRuleset.AllowGameplayOverlays) { skipIntroOverlay.Expire(); skipOutroOverlay.Expire(); From b380be7169de5b4e2ca066d27fd5970a624e5cfb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 May 2021 16:43:51 +0900 Subject: [PATCH 077/108] Add xmldoc for `updateCompletionState` --- osu.Game/Screens/Play/Player.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 6ed1a87d8a..cf26bc479a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -582,6 +582,11 @@ namespace osu.Game.Screens.Play private ScheduledDelegate completionProgressDelegate; private Task prepareScoreForDisplayTask; + /// + /// Handles changes in player state which may progress the completion of gameplay / this screen's lifetime. + /// + /// If in a state where a storyboard outro is to be played, offers the choice of skipping beyond it. + /// Thrown if this method is called more than once without changing state. private void updateCompletionState(bool skipStoryboardOutro = false) { // screen may be in the exiting transition phase. From 18779b1d1e8fa6d6b9fdb4e0d389b8b7f0d08d54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 May 2021 16:48:13 +0900 Subject: [PATCH 078/108] Cache last event time value to avoid super expensive LINQ --- .../Drawables/DrawableStoryboard.cs | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index a9a8b8a4ac..4c42823779 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -20,6 +20,13 @@ namespace osu.Game.Storyboards.Drawables [Cached] public Storyboard Storyboard { get; } + /// + /// Whether the storyboard is considered finished. + /// + public IBindable HasStoryboardEnded => hasStoryboardEnded; + + private readonly BindableBool hasStoryboardEnded = new BindableBool(); + protected override Container Content { get; } protected override Vector2 DrawScale => new Vector2(Parent.DrawHeight / 480); @@ -40,6 +47,8 @@ namespace osu.Game.Storyboards.Drawables public override bool RemoveCompletedTransforms => false; + private double? lastEventEndTime; + private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => @@ -74,6 +83,14 @@ namespace osu.Game.Storyboards.Drawables Add(layer.CreateDrawable()); } + + lastEventEndTime = Storyboard.LatestEventTime; + } + + protected override void Update() + { + base.Update(); + hasStoryboardEnded.Value = lastEventEndTime == null || Time.Current >= lastEventEndTime; } public DrawableStoryboardLayer OverlayLayer => Children.Single(layer => layer.Name == "Overlay"); @@ -83,25 +100,5 @@ namespace osu.Game.Storyboards.Drawables foreach (var layer in Children) layer.Enabled = passing ? layer.Layer.VisibleWhenPassing : layer.Layer.VisibleWhenFailing; } - - protected override void Update() - { - base.Update(); - updateHasStoryboardEnded(); - } - - /// - /// Whether the storyboard is considered finished. - /// - public IBindable HasStoryboardEnded => hasStoryboardEnded; - - private readonly BindableBool hasStoryboardEnded = new BindableBool(); - - private void updateHasStoryboardEnded() - { - hasStoryboardEnded.Value = - Storyboard.LatestEventTime == null || - Time.Current >= Storyboard.LatestEventTime; - } } } From acc9258eb217dfd04dfdc71f32498df3884caba6 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 4 May 2021 00:59:22 -0700 Subject: [PATCH 079/108] Implement notes for settings --- osu.Game/Overlays/Settings/SettingsItem.cs | 33 +++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 0bd9750b0b..1d6535c289 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -19,6 +19,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Settings { @@ -36,6 +37,8 @@ namespace osu.Game.Overlays.Settings private SpriteText labelText; + private readonly OsuTextFlowContainer noteText; + public bool ShowsDefaultIndicator = true; public string TooltipText { get; set; } @@ -57,6 +60,19 @@ namespace osu.Game.Overlays.Settings } } + /// + /// Text to be displayed at the bottom of this . + /// Used for further explanation or indicating drawbacks of the current setting. + /// + public string NoteText + { + set + { + noteText.Alpha = 1; + noteText.Text = value; + } + } + public virtual Bindable Current { get => controlWithCurrent.Current; @@ -92,7 +108,16 @@ namespace osu.Game.Overlays.Settings RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS }, - Child = Control = CreateControl() + Children = new[] + { + Control = CreateControl(), + noteText = new OsuTextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Alpha = 0, + }, + }, }, }; @@ -108,6 +133,12 @@ namespace osu.Game.Overlays.Settings } } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + noteText.Colour = colours.Yellow; + } + private void updateDisabled() { if (labelText != null) From 796bd8e47eab8b8268bcbecae9349539b99d85a3 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 4 May 2021 00:59:48 -0700 Subject: [PATCH 080/108] Add existing setting notes from stable --- .../Settings/Sections/Graphics/LayoutSettings.cs | 9 ++++++++- .../Sections/Graphics/RendererSettings.cs | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 4d5c2e06eb..a24dd1f64b 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -141,7 +141,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); - windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); + windowModeDropdown.Current.BindValueChanged(mode => + { + updateResolutionDropdown(); + + const string not_fullscreen_note = "Running without fullscreen mode will increase your input latency!"; + + windowModeDropdown.NoteText = mode.NewValue != WindowMode.Fullscreen ? not_fullscreen_note : string.Empty; + }, true); windowModes.BindCollectionChanged((sender, args) => { diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 8773e6763c..3ad201640d 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -13,6 +13,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { protected override string Header => "Renderer"; + private SettingsEnumDropdown frameLimiterDropdown; + [BackgroundDependencyLoader] private void load(FrameworkConfigManager config, OsuConfigManager osuConfig) { @@ -20,7 +22,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Children = new Drawable[] { // TODO: this needs to be a custom dropdown at some point - new SettingsEnumDropdown + frameLimiterDropdown = new SettingsEnumDropdown { LabelText = "Frame limiter", Current = config.GetBindable(FrameworkSetting.FrameSync) @@ -37,5 +39,17 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + frameLimiterDropdown.Current.BindValueChanged(limit => + { + const string unlimited_frames_note = "Using unlimited frame limiter can lead to stutters. \"2x refresh rate\" is recommended."; + + frameLimiterDropdown.NoteText = limit.NewValue == FrameSync.Unlimited ? unlimited_frames_note : string.Empty; + }, true); + } } } From 0a649227385b82fb95787e2fd8989fae1fca258a Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 4 May 2021 01:00:35 -0700 Subject: [PATCH 081/108] Add supporter note to background source setting --- .../UserInterface/MainMenuSettings.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 95e2e9da30..707f8cd314 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -2,8 +2,11 @@ // 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.Configuration; +using osu.Game.Online.API; +using osu.Game.Users; namespace osu.Game.Overlays.Settings.Sections.UserInterface { @@ -11,9 +14,15 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { protected override string Header => "Main Menu"; + private IBindable user; + + private SettingsEnumDropdown backgroundSourceDropdown; + [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, IAPIProvider api) { + user = api.LocalUser.GetBoundCopy(); + Children = new Drawable[] { new SettingsCheckbox @@ -31,7 +40,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface LabelText = "Intro sequence", Current = config.GetBindable(OsuSetting.IntroSequence), }, - new SettingsEnumDropdown + backgroundSourceDropdown = new SettingsEnumDropdown { LabelText = "Background source", Current = config.GetBindable(OsuSetting.MenuBackgroundSource), @@ -43,5 +52,17 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface } }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + backgroundSourceDropdown.Current.BindValueChanged(source => + { + const string not_supporter_note = "Changes to this setting will only apply with an active osu!supporter tag."; + + backgroundSourceDropdown.NoteText = user.Value?.IsSupporter == false ? not_supporter_note : string.Empty; + }, true); + } } } From a5842130027d8e9560c9311e997c4916ebc35ba7 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 4 May 2021 09:11:33 -0700 Subject: [PATCH 082/108] Use vertical padding instead of relative height for default button --- osu.Game/Overlays/Settings/SettingsItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 1d6535c289..6405431f6b 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -18,7 +18,6 @@ using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osuTK; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Settings @@ -172,6 +171,7 @@ namespace osu.Game.Overlays.Settings { RelativeSizeAxes = Axes.Y; Width = SettingsPanel.CONTENT_MARGINS; + Padding = new MarginPadding { Vertical = 1.5f }; Alpha = 0f; } @@ -194,7 +194,7 @@ namespace osu.Game.Overlays.Settings Type = EdgeEffectType.Glow, Radius = 2, }, - Size = new Vector2(0.33f, 0.8f), + Width = 0.33f, Child = new Box { RelativeSizeAxes = Axes.Both }, }; } From 4ceb9b1562e52475ba8da9652bd2d31501983ef3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 4 May 2021 23:36:14 +0300 Subject: [PATCH 083/108] Avoid randomizing and overestimating logic with simple hardcoding Not sure what was in my mind while I was pushing that.. --- .../NonVisual/FramedReplayInputHandlerTest.cs | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 2062c4b820..fe1186bf95 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Utils; using osu.Game.Replays; using osu.Game.Rulesets.Replays; @@ -281,26 +280,39 @@ namespace osu.Game.Tests.NonVisual } [Test] - public void TestReplayFrameSortStability() + public void TestReplayFramesSortStability() { const double repeating_time = 5000; - int data = 0; - - // 1. add a range of frames in which some of them have the constant time 5000, all without any "data". - // 2. randomize the frames. - // 3. assign "data" to each frame in ascending order. - replay.Frames.AddRange(Enumerable.Range(1, 250).Select(i => + // add a range of frames randomized in time but have a "data" assigned to them in ascending order. + replay.Frames.AddRange(new[] { - if (RNG.NextBool()) - return new TestReplayFrame(repeating_time, true); - else - return new TestReplayFrame(i * 1000, true); - }).OrderBy(_ => RNG.Next()).Select(f => new TestReplayFrame(f.Time, true, ++data))); + new TestReplayFrame(repeating_time, true, 0), + new TestReplayFrame(0, true, 1), + new TestReplayFrame(3000, true, 2), + new TestReplayFrame(repeating_time, true, 3), + new TestReplayFrame(repeating_time, true, 4), + new TestReplayFrame(6000, true, 5), + new TestReplayFrame(9000, true, 6), + new TestReplayFrame(repeating_time, true, 7), + new TestReplayFrame(repeating_time, true, 8), + new TestReplayFrame(1000, true, 9), + new TestReplayFrame(11000, true, 10), + new TestReplayFrame(21000, true, 11), + new TestReplayFrame(4000, true, 12), + new TestReplayFrame(repeating_time, true, 13), + new TestReplayFrame(repeating_time, true, 14), + new TestReplayFrame(8000, true, 15), + new TestReplayFrame(2000, true, 16), + new TestReplayFrame(7000, true, 17), + new TestReplayFrame(repeating_time, true, 18), + new TestReplayFrame(repeating_time, true, 19), + new TestReplayFrame(10000, true, 20), + }); replay.HasReceivedAllFrames = true; - // create a new handler with the replay for the frames to be sorted. + // create a new handler with the replay for the sort to be performed. handler = new TestInputHandler(replay); // ensure sort stability by checking whether the "data" assigned to each time-repeated frame is in ascending order, as it was before sort. From 45c0b74151c7f547838a98786c5859194ad3f442 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 4 May 2021 23:41:46 +0300 Subject: [PATCH 084/108] Use LINQ select for data assigning for simplicity To avoid having to read through all of frames and ensure nothing is failing there --- .../NonVisual/FramedReplayInputHandlerTest.cs | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index fe1186bf95..a9f9dfdc83 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -284,31 +284,33 @@ namespace osu.Game.Tests.NonVisual { const double repeating_time = 5000; + int incrementingData = 0; + // add a range of frames randomized in time but have a "data" assigned to them in ascending order. replay.Frames.AddRange(new[] { - new TestReplayFrame(repeating_time, true, 0), - new TestReplayFrame(0, true, 1), - new TestReplayFrame(3000, true, 2), - new TestReplayFrame(repeating_time, true, 3), - new TestReplayFrame(repeating_time, true, 4), - new TestReplayFrame(6000, true, 5), - new TestReplayFrame(9000, true, 6), - new TestReplayFrame(repeating_time, true, 7), - new TestReplayFrame(repeating_time, true, 8), - new TestReplayFrame(1000, true, 9), - new TestReplayFrame(11000, true, 10), - new TestReplayFrame(21000, true, 11), - new TestReplayFrame(4000, true, 12), - new TestReplayFrame(repeating_time, true, 13), - new TestReplayFrame(repeating_time, true, 14), - new TestReplayFrame(8000, true, 15), - new TestReplayFrame(2000, true, 16), - new TestReplayFrame(7000, true, 17), - new TestReplayFrame(repeating_time, true, 18), - new TestReplayFrame(repeating_time, true, 19), - new TestReplayFrame(10000, true, 20), - }); + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(0, true), + new TestReplayFrame(3000, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(6000, true), + new TestReplayFrame(9000, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(1000, true), + new TestReplayFrame(11000, true), + new TestReplayFrame(21000, true), + new TestReplayFrame(4000, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(8000, true), + new TestReplayFrame(2000, true), + new TestReplayFrame(7000, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(repeating_time, true), + new TestReplayFrame(10000, true), + }.Select(f => new TestReplayFrame(f.Time, true, incrementingData++))); replay.HasReceivedAllFrames = true; From 973475823735a94dce077f2b5e9005df9e689a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 4 May 2021 22:48:57 +0200 Subject: [PATCH 085/108] Simplify test case further --- .../NonVisual/FramedReplayInputHandlerTest.cs | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index a9f9dfdc83..a4fe2172e1 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -284,33 +284,31 @@ namespace osu.Game.Tests.NonVisual { const double repeating_time = 5000; - int incrementingData = 0; - // add a range of frames randomized in time but have a "data" assigned to them in ascending order. replay.Frames.AddRange(new[] { - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(0, true), - new TestReplayFrame(3000, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(6000, true), - new TestReplayFrame(9000, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(1000, true), - new TestReplayFrame(11000, true), - new TestReplayFrame(21000, true), - new TestReplayFrame(4000, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(8000, true), - new TestReplayFrame(2000, true), - new TestReplayFrame(7000, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(repeating_time, true), - new TestReplayFrame(10000, true), - }.Select(f => new TestReplayFrame(f.Time, true, incrementingData++))); + repeating_time, + 0, + 3000, + repeating_time, + repeating_time, + 6000, + 9000, + repeating_time, + repeating_time, + 1000, + 11000, + 21000, + 4000, + repeating_time, + repeating_time, + 8000, + 2000, + 7000, + repeating_time, + repeating_time, + 10000 + }.Select((time, index) => new TestReplayFrame(time, true, index))); replay.HasReceivedAllFrames = true; @@ -321,7 +319,7 @@ namespace osu.Game.Tests.NonVisual var repeatingTimeFramesData = replay.Frames .Cast() .Where(f => f.Time == repeating_time) - .Select(f => f.Data); + .Select(f => f.FrameIndex); Assert.That(repeatingTimeFramesData, Is.Ordered.Ascending); } @@ -372,13 +370,13 @@ namespace osu.Game.Tests.NonVisual private class TestReplayFrame : ReplayFrame { public readonly bool IsImportant; - public readonly int Data; + public readonly int FrameIndex; - public TestReplayFrame(double time, bool isImportant = false, int data = 0) + public TestReplayFrame(double time, bool isImportant = false, int frameIndex = 0) : base(time) { IsImportant = isImportant; - Data = data; + FrameIndex = frameIndex; } } From f7d9fb094e8d764e01948a6a8104e6a5f8adc2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 4 May 2021 22:59:10 +0200 Subject: [PATCH 086/108] Reword & clarify comments --- osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index a4fe2172e1..407dec936b 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -284,7 +284,10 @@ namespace osu.Game.Tests.NonVisual { const double repeating_time = 5000; - // add a range of frames randomized in time but have a "data" assigned to them in ascending order. + // add a collection of frames in shuffled order time-wise; each frame also stores its original index to check stability later. + // data is hand-picked and breaks if the unstable List.Sort() is used. + // in theory this can still return a false-positive with another unstable algorithm if extremely unlucky, + // but there is no conceivable fool-proof way to prevent that anyways. replay.Frames.AddRange(new[] { repeating_time, @@ -315,7 +318,7 @@ namespace osu.Game.Tests.NonVisual // create a new handler with the replay for the sort to be performed. handler = new TestInputHandler(replay); - // ensure sort stability by checking whether the "data" assigned to each time-repeated frame is in ascending order, as it was before sort. + // ensure sort stability by checking that the frames with time == repeating_time are sorted in ascending frame index order themselves. var repeatingTimeFramesData = replay.Frames .Cast() .Where(f => f.Time == repeating_time) From 23b9d8c260058df9da31790b981ba50504b8aa5a Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 4 May 2021 14:02:12 -0700 Subject: [PATCH 087/108] Fix alpha not being zero when string is set to empty and use inequality on supporter condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Settings/Sections/UserInterface/MainMenuSettings.cs | 2 +- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 707f8cd314..b5ab6d2f60 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { const string not_supporter_note = "Changes to this setting will only apply with an active osu!supporter tag."; - backgroundSourceDropdown.NoteText = user.Value?.IsSupporter == false ? not_supporter_note : string.Empty; + backgroundSourceDropdown.NoteText = user.Value?.IsSupporter != true ? not_supporter_note : string.Empty; }, true); } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 6405431f6b..c6f17cfc23 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Settings { set { - noteText.Alpha = 1; + noteText.Alpha = string.IsNullOrWhiteSpace(value) ? 0 : 1; noteText.Text = value; } } From 1472960319ecdd6f4627bdfd7aff9896b6bdcfe1 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Tue, 4 May 2021 21:35:36 -0400 Subject: [PATCH 088/108] Hide and disable skip outro overlay on rewind --- osu.Game/Screens/Play/Player.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cf26bc479a..88e617245b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -598,6 +598,7 @@ namespace osu.Game.Screens.Play completionProgressDelegate?.Cancel(); completionProgressDelegate = null; ValidForResume = true; + skipOutroOverlay.Hide(); return; } From 1d4a8bc0ae9df6ea3b796974953c6b903ddb1f5c Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Tue, 4 May 2021 22:23:36 -0400 Subject: [PATCH 089/108] Add visual test for rewinding --- .../Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 8326063f81..3229716b7e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -116,6 +116,15 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for score shown", () => Player.IsScoreShown); } + [Test] + public void TestStoryboardRewind() + { + CreateTest(null); + AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddStep("rewind", () => Player.GameplayClockContainer.Seek(-1000)); + AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); + } + protected override bool AllowFail => true; protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); From 6178f38c95277c91e2a07cb0766d9a33318d24aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 16:15:06 +0900 Subject: [PATCH 090/108] Reword unlimited frame rate warning a bit --- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 3ad201640d..c42db1706f 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics frameLimiterDropdown.Current.BindValueChanged(limit => { - const string unlimited_frames_note = "Using unlimited frame limiter can lead to stutters. \"2x refresh rate\" is recommended."; + const string unlimited_frames_note = "Using unlimited frame limiter can lead to stutters, bad performance and overheating. It will not improve perceived latency. \"2x refresh rate\" is recommended."; frameLimiterDropdown.NoteText = limit.NewValue == FrameSync.Unlimited ? unlimited_frames_note : string.Empty; }, true); From 1288f69fada2d8457b2e21bd9341ff517ab72c58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 16:16:02 +0900 Subject: [PATCH 091/108] Rename to `WarningText` --- .../Settings/Sections/Graphics/LayoutSettings.cs | 2 +- .../Settings/Sections/Graphics/RendererSettings.cs | 2 +- .../Sections/UserInterface/MainMenuSettings.cs | 2 +- osu.Game/Overlays/Settings/SettingsItem.cs | 14 +++++++------- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index a24dd1f64b..937bcc8abf 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -147,7 +147,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics const string not_fullscreen_note = "Running without fullscreen mode will increase your input latency!"; - windowModeDropdown.NoteText = mode.NewValue != WindowMode.Fullscreen ? not_fullscreen_note : string.Empty; + windowModeDropdown.WarningText = mode.NewValue != WindowMode.Fullscreen ? not_fullscreen_note : string.Empty; }, true); windowModes.BindCollectionChanged((sender, args) => diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index c42db1706f..70225ff6b8 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { const string unlimited_frames_note = "Using unlimited frame limiter can lead to stutters, bad performance and overheating. It will not improve perceived latency. \"2x refresh rate\" is recommended."; - frameLimiterDropdown.NoteText = limit.NewValue == FrameSync.Unlimited ? unlimited_frames_note : string.Empty; + frameLimiterDropdown.WarningText = limit.NewValue == FrameSync.Unlimited ? unlimited_frames_note : string.Empty; }, true); } } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index b5ab6d2f60..7c4c88f344 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { const string not_supporter_note = "Changes to this setting will only apply with an active osu!supporter tag."; - backgroundSourceDropdown.NoteText = user.Value?.IsSupporter != true ? not_supporter_note : string.Empty; + backgroundSourceDropdown.WarningText = user.Value?.IsSupporter != true ? not_supporter_note : string.Empty; }, true); } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index c6f17cfc23..f4d7c72b7f 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Settings private SpriteText labelText; - private readonly OsuTextFlowContainer noteText; + private readonly OsuTextFlowContainer warningText; public bool ShowsDefaultIndicator = true; @@ -61,14 +61,14 @@ namespace osu.Game.Overlays.Settings /// /// Text to be displayed at the bottom of this . - /// Used for further explanation or indicating drawbacks of the current setting. + /// Generally used to recommend the user change their setting as the current one is considered sub-optimal. /// - public string NoteText + public string WarningText { set { - noteText.Alpha = string.IsNullOrWhiteSpace(value) ? 0 : 1; - noteText.Text = value; + warningText.Alpha = string.IsNullOrWhiteSpace(value) ? 0 : 1; + warningText.Text = value; } } @@ -110,7 +110,7 @@ namespace osu.Game.Overlays.Settings Children = new[] { Control = CreateControl(), - noteText = new OsuTextFlowContainer + warningText = new OsuTextFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -135,7 +135,7 @@ namespace osu.Game.Overlays.Settings [BackgroundDependencyLoader] private void load(OsuColour colours) { - noteText.Colour = colours.Yellow; + warningText.Colour = colours.Yellow; } private void updateDisabled() From 19ffcd00c27d04ce184f16cd55f141c803dfd011 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 16:19:06 +0900 Subject: [PATCH 092/108] Initialise warning text flow lazily as most items will not use it --- osu.Game/Overlays/Settings/SettingsItem.cs | 29 ++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index f4d7c72b7f..09e458ad7e 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -36,12 +36,15 @@ namespace osu.Game.Overlays.Settings private SpriteText labelText; - private readonly OsuTextFlowContainer warningText; + private OsuTextFlowContainer warningText; public bool ShowsDefaultIndicator = true; public string TooltipText { get; set; } + [Resolved] + private OsuColour colours { get; set; } + public virtual LocalisableString LabelText { get => labelText?.Text ?? string.Empty; @@ -67,6 +70,18 @@ namespace osu.Game.Overlays.Settings { set { + if (warningText == null) + { + // construct lazily for cases where the label is not needed (may be provided by the Control). + FlowContent.Add(warningText = new OsuTextFlowContainer + { + Colour = colours.Yellow, + Margin = new MarginPadding { Bottom = 5 }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }); + } + warningText.Alpha = string.IsNullOrWhiteSpace(value) ? 0 : 1; warningText.Text = value; } @@ -110,12 +125,6 @@ namespace osu.Game.Overlays.Settings Children = new[] { Control = CreateControl(), - warningText = new OsuTextFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Alpha = 0, - }, }, }, }; @@ -132,12 +141,6 @@ namespace osu.Game.Overlays.Settings } } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - warningText.Colour = colours.Yellow; - } - private void updateDisabled() { if (labelText != null) From 08a45e9fc29d37b686a8430db7c37aab39894c00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 16:26:34 +0900 Subject: [PATCH 093/108] Remove dead code --- osu.Game/Overlays/Settings/SettingsItem.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 09e458ad7e..86a836d29b 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -230,12 +230,6 @@ namespace osu.Game.Overlays.Settings UpdateState(); } - public void SetButtonColour(Color4 buttonColour) - { - this.buttonColour = buttonColour; - UpdateState(); - } - public void UpdateState() => Scheduler.AddOnce(updateState); private void updateState() From 3cc9bad97968f220fc475f657f0189eb524b3f8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 17:49:33 +0900 Subject: [PATCH 094/108] Actually check for correct state of fade content in rewind test --- .../Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 3229716b7e..4138a81ebd 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Graphics; @@ -119,9 +120,16 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestStoryboardRewind() { + SkipOverlay.FadeContainer fadeContainer() => Player.ChildrenOfType().First(); + CreateTest(null); AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value); + AddUntilStep("skip overlay content becomes visible", () => fadeContainer().State == Visibility.Visible); + AddStep("rewind", () => Player.GameplayClockContainer.Seek(-1000)); + AddUntilStep("skip overlay content not visible", () => fadeContainer().State == Visibility.Hidden); + + AddUntilStep("skip overlay content becomes visible", () => fadeContainer().State == Visibility.Visible); AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); } From 9ec3255c505e99a826b3eba40f86f80829502562 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 17:50:25 +0900 Subject: [PATCH 095/108] Fix `SkipOverlay`'s `FadeContent` not getting correct state from parent --- osu.Game/Screens/Play/SkipOverlay.cs | 30 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index ddb78dfb67..ed49fc40b2 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -8,19 +8,19 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Threading; +using osu.Framework.Utils; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; using osu.Game.Screens.Ranking; using osuTK; using osuTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics.Containers; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; -using osu.Framework.Utils; -using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play { @@ -92,6 +92,18 @@ namespace osu.Game.Screens.Play private double fadeOutBeginTime => startTime - MasterGameplayClockContainer.MINIMUM_SKIP_TIME; + public override void Hide() + { + base.Hide(); + fadeContainer.Hide(); + } + + public override void Show() + { + base.Show(); + fadeContainer.Show(); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -147,7 +159,7 @@ namespace osu.Game.Screens.Play { } - private class FadeContainer : Container, IStateful + public class FadeContainer : Container, IStateful { public event Action StateChanged; @@ -170,7 +182,7 @@ namespace osu.Game.Screens.Play switch (state) { case Visibility.Visible: - // we may be triggered to become visible mnultiple times but we only want to transform once. + // we may be triggered to become visible multiple times but we only want to transform once. if (stateChanged) this.FadeIn(500, Easing.OutExpo); From 377af38d94541c48cab0d7083cae089b28916f20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 17:59:49 +0900 Subject: [PATCH 096/108] Remove unnecessary pixelSnapping parameter This was only required because there was text being rendered to the `BufferedContainer` content until now. Removing this should allow for better resolution in the background display (due to using a better minify scale mode). --- osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs index 566f49a799..0233112c69 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both; - InternalChild = new BufferedContainer(pixelSnapping: true) + InternalChild = new BufferedContainer() { CacheDrawnFrameBuffer = true, RelativeSizeAxes = Axes.Both, From 1410b8f36dce587d91b030bfe5b4736828bb074b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 18:30:57 +0900 Subject: [PATCH 097/108] Fix follow points displaying at incorrect locations when dragging a slider out-of-bounds --- .../Objects/Drawables/Connections/FollowPointConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 5541d0e790..cda4715280 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections Entry = null; } - private void onEntryInvalidated() => refreshPoints(); + private void onEntryInvalidated() => Scheduler.AddOnce(refreshPoints); private void refreshPoints() { From cf6ed7a7cf5bac2d8d8d463a1672b7318bd0b3d1 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 13:13:37 +0200 Subject: [PATCH 098/108] Refactored out changes in StarRatingDisplay --- .../Ranking/Expanded/StarRatingDisplay.cs | 145 +++++++----------- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 68 ++++---- .../Select/BeatmapInfoWedgeBackground.cs | 2 +- 3 files changed, 97 insertions(+), 118 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 748f58e430..f7e50fdc8a 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -22,22 +22,7 @@ namespace osu.Game.Screens.Ranking.Expanded /// public class StarRatingDisplay : CompositeDrawable { - [Resolved] - private OsuColour colours { get; set; } - - private CircularContainer colorContainer; - private StarDifficulty starDifficulty; - private FillFlowContainer foregroundContainer; - - public StarDifficulty StarDifficulty - { - get => starDifficulty; - set - { - starDifficulty = value; - setDifficulty(starDifficulty); - } - } + private readonly StarDifficulty difficulty; /// /// Creates a new using an already computed . @@ -45,94 +30,78 @@ namespace osu.Game.Screens.Ranking.Expanded /// The already computed to display the star difficulty of. public StarRatingDisplay(StarDifficulty starDifficulty) { - this.starDifficulty = starDifficulty; - } - - private void setDifficulty(StarDifficulty difficulty) - { - colorContainer.FadeColour(getDifficultyColour(difficulty), 250); - - foregroundContainer.Expire(); - foregroundContainer = null; - AddInternal(foregroundContainer = createForegroundContainer(difficulty)); + difficulty = starDifficulty; } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) { AutoSizeAxes = Axes.Both; - InternalChildren = new Drawable[] - { - colorContainer = new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Colour = getDifficultyColour(starDifficulty), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - }, - } - }, - foregroundContainer = createForegroundContainer(starDifficulty), - }; - } - - private ColourInfo getDifficultyColour(StarDifficulty difficulty) - { - return difficulty.DifficultyRating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); - } - - private FillFlowContainer createForegroundContainer(StarDifficulty difficulty) - { var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); string wholePart = starRatingParts[0]; string fractionPart = starRatingParts[1]; string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; - return new FillFlowContainer + ColourInfo backgroundColour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + + InternalChildren = new Drawable[] { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 8, Vertical = 4 }, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(2, 0), - Children = new Drawable[] + new CircularContainer { - new SpriteIcon + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(7), - Icon = FontAwesome.Solid.Star, - Colour = Color4.Black - }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - TextAnchor = Anchor.BottomLeft, - }.With(t => - { - t.AddText($"{wholePart}", s => + new Box { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - t.AddText($"{separator}{fractionPart}", s => + RelativeSizeAxes = Axes.Both, + Colour = backgroundColour + }, + } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 8, Vertical = 4 }, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(2, 0), + Children = new Drawable[] + { + new SpriteIcon { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - }), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(7), + Icon = FontAwesome.Solid.Star, + Colour = Color4.Black + }, + new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + TextAnchor = Anchor.BottomLeft, + }.With(t => + { + t.AddText($"{wholePart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + + t.AddText($"{separator}{fractionPart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + }) + } } }; } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index cb5a276a5d..04063d5819 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -173,7 +173,7 @@ namespace osu.Game.Screens.Select private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; - private StarRatingDisplay starRatingDisplay; + private Container topRightMetadataContainer; private Container bpmLabelContainer; private ModSettingChangeTracker settingChangeTracker; private CancellationTokenSource cancellationTokenSource; @@ -232,34 +232,15 @@ namespace osu.Game.Screens.Select }, } }, - new FillFlowContainer + topRightMetadataContainer = new Container { Name = "Topright-aligned metadata", Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Direction = FillDirection.Vertical, Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Shear = wedged_container_shear, - Children = new Drawable[] - { - starRatingDisplay = new StarRatingDisplay(starDifficulty.Value ?? new StarDifficulty()) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - Margin = new MarginPadding { Bottom = 5 } - }, - StatusPill = new BeatmapSetOnlineStatusPill - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - TextSize = 11, - TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, - Status = beatmapInfo.Status, - } - } + Child = createTopRightMetadataContainer(beatmapInfo, starDifficulty.Value ?? new StarDifficulty()) }, new FillFlowContainer { @@ -306,21 +287,50 @@ namespace osu.Game.Screens.Select titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); - starDifficulty.BindValueChanged(updateStarRatingDisplay, true); + starDifficulty.BindValueChanged(updateTopRightMetadata, true); // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); } - private void updateStarRatingDisplay(ValueChangedEvent valueChanged) + private void updateTopRightMetadata(ValueChangedEvent valueChanged) { - if (valueChanged.NewValue.HasValue && valueChanged.NewValue.Value.Stars > 0) - starRatingDisplay.Show(); - else - starRatingDisplay.Hide(); + topRightMetadataContainer.Child.FadeOut(250); + topRightMetadataContainer.Child.Expire(); + topRightMetadataContainer.Child = createTopRightMetadataContainer(beatmap.BeatmapInfo, valueChanged.NewValue ?? new StarDifficulty()); + } - starRatingDisplay.StarDifficulty = valueChanged.NewValue ?? new StarDifficulty(); + private FillFlowContainer createTopRightMetadataContainer(BeatmapInfo beatmapInfo, StarDifficulty difficulty) + { + var container = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + }; + + if (difficulty.Stars > 0) + { + container.Add(new StarRatingDisplay(difficulty) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + Margin = new MarginPadding { Bottom = 5 } + }); + } + + container.Add(StatusPill = new BeatmapSetOnlineStatusPill + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + TextSize = 11, + TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, + Status = beatmapInfo.Status, + }); + + return container; } private void refreshModInformation(ValueChangedEvent> modsChangedEvent) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs index 0233112c69..f50fb4dc8a 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both; - InternalChild = new BufferedContainer() + InternalChild = new BufferedContainer { CacheDrawnFrameBuffer = true, RelativeSizeAxes = Axes.Both, From 4ef901d08d51e586e447f831821463150d14229c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 May 2021 21:07:49 +0900 Subject: [PATCH 099/108] Remove unnecessary redirection property to `Container.Info` --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs | 4 ++-- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 688cc9a035..ec19f00087 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -193,9 +193,9 @@ namespace osu.Game.Tests.Visual.SongSelect private class TestBeatmapInfoWedge : BeatmapInfoWedge { - public new WedgeInfoText Info => base.Info; - public new BeatmapInfoWedgeContainer Container => base.Container; + + public WedgeInfoText Info => base.Container.Info; } private class TestHitObject : ConvertHitObject, IHasPosition diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 04063d5819..c86bdc99ff 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -41,7 +41,6 @@ namespace osu.Game.Screens.Select private IBindable ruleset { get; set; } protected BeatmapInfoWedgeContainer Container; - protected WedgeInfoText Info => Container.Info; public BeatmapInfoWedge() { From 5049e2fbf9bf709df1f4b2614ef80c11e8f5d31e Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 15:11:38 +0200 Subject: [PATCH 100/108] Refactored out changes in DifficultyColourBar --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 66 +++++++++------------ 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index c86bdc99ff..448bf088dc 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -174,6 +174,7 @@ namespace osu.Game.Screens.Select private FillFlowContainer infoLabelContainer; private Container topRightMetadataContainer; private Container bpmLabelContainer; + private Container difficultyColourBarContainer; private ModSettingChangeTracker settingChangeTracker; private CancellationTokenSource cancellationTokenSource; private IBindable starDifficulty; @@ -206,10 +207,11 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - new DifficultyColourBar(beatmapInfo) + difficultyColourBarContainer = new Container { RelativeSizeAxes = Axes.Y, Width = 20, + Child = createDifficultyColourBar(starDifficulty.Value ?? new StarDifficulty()), }, new FillFlowContainer { @@ -286,20 +288,32 @@ namespace osu.Game.Screens.Select titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); - starDifficulty.BindValueChanged(updateTopRightMetadata, true); + starDifficulty.BindValueChanged(updateDifficulty, true); // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); } - private void updateTopRightMetadata(ValueChangedEvent valueChanged) + private void updateDifficulty(ValueChangedEvent valueChanged) { + var difficulty = valueChanged.NewValue ?? new StarDifficulty(); + topRightMetadataContainer.Child.FadeOut(250); topRightMetadataContainer.Child.Expire(); - topRightMetadataContainer.Child = createTopRightMetadataContainer(beatmap.BeatmapInfo, valueChanged.NewValue ?? new StarDifficulty()); + topRightMetadataContainer.Child = createTopRightMetadataContainer(beatmap.BeatmapInfo, difficulty); + + difficultyColourBarContainer.Child.Expire(); + difficultyColourBarContainer.Child = createDifficultyColourBar(difficulty); } + private DifficultyColourBar createDifficultyColourBar(StarDifficulty difficulty) + => new DifficultyColourBar(difficulty) + { + RelativeSizeAxes = Axes.Y, + Width = 20, + }; + private FillFlowContainer createTopRightMetadataContainer(BeatmapInfo beatmapInfo, StarDifficulty difficulty) { var container = new FillFlowContainer @@ -515,64 +529,38 @@ namespace osu.Game.Screens.Select private class DifficultyColourBar : Container { - [Resolved] - private OsuColour colours { get; set; } + private readonly StarDifficulty difficulty; - private Box solidDifficultyBox; - private Box transparentDifficultyBox; - private CancellationTokenSource cancellationTokenSource; - private IBindable starDifficulty; - - private readonly BeatmapInfo beatmapInfo; - - public DifficultyColourBar(BeatmapInfo beatmapInfo) + public DifficultyColourBar(StarDifficulty difficulty) { - this.beatmapInfo = beatmapInfo; + this.difficulty = difficulty; } [BackgroundDependencyLoader] - private void load(BeatmapDifficultyCache difficultyCache) + private void load(OsuColour colours) { const float full_opacity_ratio = 0.7f; - cancellationTokenSource?.Cancel(); - cancellationTokenSource = new CancellationTokenSource(); - - starDifficulty?.UnbindAll(); - starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); + var difficultyColour = colours.ForDifficultyRating(difficulty.DifficultyRating); Children = new Drawable[] { - solidDifficultyBox = new Box + new Box { RelativeSizeAxes = Axes.Both, + Colour = difficultyColour, Width = full_opacity_ratio, }, - transparentDifficultyBox = new Box + new Box { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, + Colour = difficultyColour, Alpha = 0.5f, X = full_opacity_ratio, Width = 1 - full_opacity_ratio, } }; - - starDifficulty.BindValueChanged(setColour, true); - } - - private void setColour(ValueChangedEvent valueChanged) - { - var difficultyColour = colours.ForDifficultyRating(valueChanged.NewValue?.DifficultyRating ?? (new StarDifficulty()).DifficultyRating); - - solidDifficultyBox.Colour = difficultyColour; - transparentDifficultyBox.Colour = difficultyColour; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - cancellationTokenSource?.Cancel(); } } } From 88506a51dd95ec070457889ad6c89b96ca777720 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 17:51:29 +0200 Subject: [PATCH 101/108] reduced complexity --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 75 ++++++++++----------- 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 448bf088dc..475a547c27 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -233,15 +233,35 @@ namespace osu.Game.Screens.Select }, } }, - topRightMetadataContainer = new Container + new FillFlowContainer { Name = "Topright-aligned metadata", Anchor = Anchor.TopRight, Origin = Anchor.TopRight, + Direction = FillDirection.Vertical, Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Shear = wedged_container_shear, - Child = createTopRightMetadataContainer(beatmapInfo, starDifficulty.Value ?? new StarDifficulty()) + Children = new Drawable[] + { + starRatingContainer = new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + Margin = new MarginPadding { Bottom = 5 } + }, + StatusPill = new BeatmapSetOnlineStatusPill + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + TextSize = 11, + TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, + Status = beatmapInfo.Status, + } + } }, new FillFlowContainer { @@ -299,51 +319,24 @@ namespace osu.Game.Screens.Select { var difficulty = valueChanged.NewValue ?? new StarDifficulty(); - topRightMetadataContainer.Child.FadeOut(250); - topRightMetadataContainer.Child.Expire(); - topRightMetadataContainer.Child = createTopRightMetadataContainer(beatmap.BeatmapInfo, difficulty); + if (starRatingContainer.Children.Count > 0) + { + starRatingContainer.Child.FadeOut(250); + starRatingContainer.Child.Expire(); + } - difficultyColourBarContainer.Child.Expire(); - difficultyColourBarContainer.Child = createDifficultyColourBar(difficulty); - } + starRatingContainer.Child = difficulty.Stars > 0 ? new StarRatingDisplay(difficulty) : Empty(); - private DifficultyColourBar createDifficultyColourBar(StarDifficulty difficulty) - => new DifficultyColourBar(difficulty) + if (difficultyColourBarContainer.Children.Count > 0) + { + difficultyColourBarContainer.Child.Expire(); + } + + difficultyColourBarContainer.Child = new DifficultyColourBar(difficulty) { RelativeSizeAxes = Axes.Y, Width = 20, }; - - private FillFlowContainer createTopRightMetadataContainer(BeatmapInfo beatmapInfo, StarDifficulty difficulty) - { - var container = new FillFlowContainer - { - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, - }; - - if (difficulty.Stars > 0) - { - container.Add(new StarRatingDisplay(difficulty) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - Margin = new MarginPadding { Bottom = 5 } - }); - } - - container.Add(StatusPill = new BeatmapSetOnlineStatusPill - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - TextSize = 11, - TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, - Status = beatmapInfo.Status, - }); - - return container; } private void refreshModInformation(ValueChangedEvent> modsChangedEvent) From 279750775848a7f6536ecbff54e69773d01c2464 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 17:56:07 +0200 Subject: [PATCH 102/108] Reorganized elements for readability --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 128 ++++++++++---------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 475a547c27..15e484e24c 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -132,39 +132,13 @@ namespace osu.Game.Screens.Select } } - public class BeatmapInfoWedgeContainer : Container - { - private readonly WorkingBeatmap beatmap; - private readonly RulesetInfo ruleset; - - internal WedgeInfoText Info; - - public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset) - { - this.beatmap = beatmap; - this.ruleset = ruleset; - } - - [BackgroundDependencyLoader] - private void load() - { - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new BeatmapInfoWedgeBackground(beatmap), - Info = new WedgeInfoText(beatmap, ruleset), - }; - } - } - public class WedgeInfoText : Container { - public FillFlowContainer MapperContainer { get; private set; } + public OsuSpriteText VersionLabel { get; private set; } public OsuSpriteText TitleLabel { get; private set; } public OsuSpriteText ArtistLabel { get; private set; } - public OsuSpriteText VersionLabel { get; private set; } public BeatmapSetOnlineStatusPill StatusPill { get; private set; } + public FillFlowContainer MapperContainer { get; private set; } [Resolved] private IBindable> mods { get; set; } @@ -172,16 +146,17 @@ namespace osu.Game.Screens.Select private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; - private Container topRightMetadataContainer; + private Container starRatingContainer; private Container bpmLabelContainer; private Container difficultyColourBarContainer; - private ModSettingChangeTracker settingChangeTracker; private CancellationTokenSource cancellationTokenSource; private IBindable starDifficulty; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; + private ModSettingChangeTracker settingChangeTracker; + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset) { this.beatmap = beatmap; @@ -211,7 +186,6 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Y, Width = 20, - Child = createDifficultyColourBar(starDifficulty.Value ?? new StarDifficulty()), }, new FillFlowContainer { @@ -304,8 +278,6 @@ namespace osu.Game.Screens.Select } }; - addInfoLabels(); - titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); starDifficulty.BindValueChanged(updateDifficulty, true); @@ -313,38 +285,8 @@ namespace osu.Game.Screens.Select // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); - } - private void updateDifficulty(ValueChangedEvent valueChanged) - { - var difficulty = valueChanged.NewValue ?? new StarDifficulty(); - - if (starRatingContainer.Children.Count > 0) - { - starRatingContainer.Child.FadeOut(250); - starRatingContainer.Child.Expire(); - } - - starRatingContainer.Child = difficulty.Stars > 0 ? new StarRatingDisplay(difficulty) : Empty(); - - if (difficultyColourBarContainer.Children.Count > 0) - { - difficultyColourBarContainer.Child.Expire(); - } - - difficultyColourBarContainer.Child = new DifficultyColourBar(difficulty) - { - RelativeSizeAxes = Axes.Y, - Width = 20, - }; - } - - private void refreshModInformation(ValueChangedEvent> modsChangedEvent) - { - settingChangeTracker?.Dispose(); - settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); - refreshBPMLabel(modsChangedEvent.NewValue); + addInfoLabels(); } private void setMetadata(string source) @@ -455,6 +397,38 @@ namespace osu.Game.Screens.Select }; } + private void updateDifficulty(ValueChangedEvent valueChanged) + { + var difficulty = valueChanged.NewValue ?? new StarDifficulty(); + + if (starRatingContainer.Children.Count > 0) + { + starRatingContainer.Child.FadeOut(250); + starRatingContainer.Child.Expire(); + } + + starRatingContainer.Child = difficulty.Stars > 0 ? new StarRatingDisplay(difficulty) : Empty(); + + if (difficultyColourBarContainer.Children.Count > 0) + { + difficultyColourBarContainer.Child.Expire(); + } + + difficultyColourBarContainer.Child = new DifficultyColourBar(difficulty) + { + RelativeSizeAxes = Axes.Y, + Width = 20, + }; + } + + private void refreshModInformation(ValueChangedEvent> modsChangedEvent) + { + settingChangeTracker?.Dispose(); + settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); + refreshBPMLabel(modsChangedEvent.NewValue); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -557,5 +531,31 @@ namespace osu.Game.Screens.Select } } } + + public class BeatmapInfoWedgeContainer : Container + { + private readonly WorkingBeatmap beatmap; + private readonly RulesetInfo ruleset; + + internal WedgeInfoText Info; + + public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset) + { + this.beatmap = beatmap; + this.ruleset = ruleset; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new BeatmapInfoWedgeBackground(beatmap), + Info = new WedgeInfoText(beatmap, ruleset), + }; + } + } } } From bb385f425531c1efdf37fd258df9057edaf10fc9 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 18:15:59 +0200 Subject: [PATCH 103/108] Reverted difficulty and mod updates --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 117 +++++++++----------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 15e484e24c..4be7d3b0f4 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -40,6 +40,14 @@ namespace osu.Game.Screens.Select [Resolved] private IBindable ruleset { get; set; } + [Resolved] + private IBindable> mods { get; set; } + + [Resolved] + private BeatmapDifficultyCache difficultyCache { get; set; } + + private IBindable beatmapDifficulty; + protected BeatmapInfoWedgeContainer Container; public BeatmapInfoWedge() @@ -80,6 +88,8 @@ namespace osu.Game.Screens.Select private WorkingBeatmap beatmap; + private CancellationTokenSource cancellationSource; + public WorkingBeatmap Beatmap { get => beatmap; @@ -88,6 +98,13 @@ namespace osu.Game.Screens.Select if (beatmap == value) return; beatmap = value; + cancellationSource?.Cancel(); + cancellationSource = new CancellationTokenSource(); + + beatmapDifficulty?.UnbindAll(); + beatmapDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token); + beatmapDifficulty.BindValueChanged(_ => updateDisplay()); + updateDisplay(); } } @@ -117,7 +134,7 @@ namespace osu.Game.Screens.Select return; } - LoadComponentAsync(loadingInfo = new BeatmapInfoWedgeContainer(beatmap, ruleset.Value) + LoadComponentAsync(loadingInfo = new BeatmapInfoWedgeContainer(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()) { Shear = -Shear, Depth = Container?.Depth + 1 ?? 0, @@ -132,6 +149,12 @@ namespace osu.Game.Screens.Select } } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + cancellationSource?.Cancel(); + } + public class WedgeInfoText : Container { public OsuSpriteText VersionLabel { get; private set; } @@ -140,41 +163,32 @@ namespace osu.Game.Screens.Select public BeatmapSetOnlineStatusPill StatusPill { get; private set; } public FillFlowContainer MapperContainer { get; private set; } - [Resolved] - private IBindable> mods { get; set; } - private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; - private Container starRatingContainer; private Container bpmLabelContainer; - private Container difficultyColourBarContainer; - private CancellationTokenSource cancellationTokenSource; - private IBindable starDifficulty; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; + private readonly IReadOnlyList mods; + private readonly StarDifficulty starDifficulty; private ModSettingChangeTracker settingChangeTracker; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, StarDifficulty difficulty) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; + this.mods = mods; + starDifficulty = difficulty; } [BackgroundDependencyLoader] - private void load(LocalisationManager localisation, BeatmapDifficultyCache difficultyCache) + private void load(LocalisationManager localisation) { var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - cancellationTokenSource?.Cancel(); - cancellationTokenSource = new CancellationTokenSource(); - - starDifficulty?.UnbindAll(); - starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, cancellationTokenSource.Token); - RelativeSizeAxes = Axes.Both; titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title)); @@ -182,7 +196,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - difficultyColourBarContainer = new Container + new DifficultyColourBar(starDifficulty) { RelativeSizeAxes = Axes.Y, Width = 20, @@ -216,16 +230,14 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Shear = wedged_container_shear, - Children = new Drawable[] + Children = new[] { - starRatingContainer = new Container + createStarRatingDisplay(starDifficulty).With(display => { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - Margin = new MarginPadding { Bottom = 5 } - }, + display.Anchor = Anchor.TopRight; + display.Origin = Anchor.TopRight; + display.Shear = -wedged_container_shear; + }), StatusPill = new BeatmapSetOnlineStatusPill { Anchor = Anchor.TopRight, @@ -280,7 +292,6 @@ namespace osu.Game.Screens.Select titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); - starDifficulty.BindValueChanged(updateDifficulty, true); // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) @@ -289,6 +300,13 @@ namespace osu.Game.Screens.Select addInfoLabels(); } + private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 + ? new StarRatingDisplay(difficulty) + { + Margin = new MarginPadding { Bottom = 5 } + } + : Empty(); + private void setMetadata(string source) { ArtistLabel.Text = artistBinding.Value; @@ -320,7 +338,10 @@ namespace osu.Game.Screens.Select } }; - mods.BindValueChanged(refreshModInformation, true); + settingChangeTracker = new ModSettingChangeTracker(mods); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(); + + refreshBPMLabel(); } private InfoLabel[] getRulesetInfoLabels() @@ -350,7 +371,7 @@ namespace osu.Game.Screens.Select return Array.Empty(); } - private void refreshBPMLabel(IReadOnlyList mods) + private void refreshBPMLabel() { var b = beatmap.Beatmap; if (b == null) @@ -397,38 +418,6 @@ namespace osu.Game.Screens.Select }; } - private void updateDifficulty(ValueChangedEvent valueChanged) - { - var difficulty = valueChanged.NewValue ?? new StarDifficulty(); - - if (starRatingContainer.Children.Count > 0) - { - starRatingContainer.Child.FadeOut(250); - starRatingContainer.Child.Expire(); - } - - starRatingContainer.Child = difficulty.Stars > 0 ? new StarRatingDisplay(difficulty) : Empty(); - - if (difficultyColourBarContainer.Children.Count > 0) - { - difficultyColourBarContainer.Child.Expire(); - } - - difficultyColourBarContainer.Child = new DifficultyColourBar(difficulty) - { - RelativeSizeAxes = Axes.Y, - Width = 20, - }; - } - - private void refreshModInformation(ValueChangedEvent> modsChangedEvent) - { - settingChangeTracker?.Dispose(); - settingChangeTracker = new ModSettingChangeTracker(modsChangedEvent.NewValue); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(modsChangedEvent.NewValue); - refreshBPMLabel(modsChangedEvent.NewValue); - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -536,13 +525,17 @@ namespace osu.Game.Screens.Select { private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; + private readonly StarDifficulty starDifficulty; + private readonly IReadOnlyList mods; internal WedgeInfoText Info; - public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset) + public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset, IReadOnlyList mods, StarDifficulty difficulty) { this.beatmap = beatmap; this.ruleset = ruleset; + this.mods = mods; + starDifficulty = difficulty; } [BackgroundDependencyLoader] @@ -553,7 +546,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { new BeatmapInfoWedgeBackground(beatmap), - Info = new WedgeInfoText(beatmap, ruleset), + Info = new WedgeInfoText(beatmap, ruleset, mods, starDifficulty), }; } } From b6b9a696017ca41dd9befb1ca5b63c2fa129f008 Mon Sep 17 00:00:00 2001 From: Denrage Date: Wed, 5 May 2021 18:50:49 +0200 Subject: [PATCH 104/108] Removed unnecessary class for wrapping --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 7 +-- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 43 +++++-------------- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index ec19f00087..b9e92cba62 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -7,6 +7,7 @@ using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; @@ -135,7 +136,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void selectBeatmap([CanBeNull] IBeatmap b) { - BeatmapInfoWedge.BeatmapInfoWedgeContainer containerBefore = null; + Container containerBefore = null; AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => { @@ -193,9 +194,9 @@ namespace osu.Game.Tests.Visual.SongSelect private class TestBeatmapInfoWedge : BeatmapInfoWedge { - public new BeatmapInfoWedgeContainer Container => base.Container; + public new Container Container => base.Container; - public WedgeInfoText Info => base.Container.Info; + public new WedgeInfoText Info => base.Info; } private class TestHitObject : ConvertHitObject, IHasPosition diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 4be7d3b0f4..53ac97cc7d 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -48,7 +48,8 @@ namespace osu.Game.Screens.Select private IBindable beatmapDifficulty; - protected BeatmapInfoWedgeContainer Container; + protected Container Container; + protected WedgeInfoText Info; public BeatmapInfoWedge() { @@ -111,7 +112,7 @@ namespace osu.Game.Screens.Select public override bool IsPresent => base.IsPresent || Container == null; // Visibility is updated in the LoadComponentAsync callback - private BeatmapInfoWedgeContainer loadingInfo; + private Container loadingInfo; private void updateDisplay() { @@ -134,10 +135,16 @@ namespace osu.Game.Screens.Select return; } - LoadComponentAsync(loadingInfo = new BeatmapInfoWedgeContainer(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()) + LoadComponentAsync(loadingInfo = new Container { + RelativeSizeAxes = Axes.Both, Shear = -Shear, Depth = Container?.Depth + 1 ?? 0, + Children = new Drawable[] + { + new BeatmapInfoWedgeBackground(beatmap), + Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()), + } }, loaded => { // ensure we are the most recent loaded wedge. @@ -520,35 +527,5 @@ namespace osu.Game.Screens.Select } } } - - public class BeatmapInfoWedgeContainer : Container - { - private readonly WorkingBeatmap beatmap; - private readonly RulesetInfo ruleset; - private readonly StarDifficulty starDifficulty; - private readonly IReadOnlyList mods; - - internal WedgeInfoText Info; - - public BeatmapInfoWedgeContainer(WorkingBeatmap beatmap, RulesetInfo ruleset, IReadOnlyList mods, StarDifficulty difficulty) - { - this.beatmap = beatmap; - this.ruleset = ruleset; - this.mods = mods; - starDifficulty = difficulty; - } - - [BackgroundDependencyLoader] - private void load() - { - RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - new BeatmapInfoWedgeBackground(beatmap), - Info = new WedgeInfoText(beatmap, ruleset, mods, starDifficulty), - }; - } - } } } From fe9ade6754360eb1e36c41f46c7d386d9b8565c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 May 2021 02:14:04 +0900 Subject: [PATCH 105/108] Rename Container to DisplayedContent --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 6 +++--- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 15 ++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index b9e92cba62..67c85a1120 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -140,11 +140,11 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => { - containerBefore = infoWedge.Container; + containerBefore = infoWedge.DisplayedContent; infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b); }); - AddUntilStep("wait for async load", () => infoWedge.Container != containerBefore); + AddUntilStep("wait for async load", () => infoWedge.DisplayedContent != containerBefore); } private IBeatmap createTestBeatmap(RulesetInfo ruleset) @@ -194,7 +194,7 @@ namespace osu.Game.Tests.Visual.SongSelect private class TestBeatmapInfoWedge : BeatmapInfoWedge { - public new Container Container => base.Container; + public new Container DisplayedContent => base.DisplayedContent; public new WedgeInfoText Info => base.Info; } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 53ac97cc7d..d84052b94d 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -48,7 +48,8 @@ namespace osu.Game.Screens.Select private IBindable beatmapDifficulty; - protected Container Container; + protected Container DisplayedContent; + protected WedgeInfoText Info; public BeatmapInfoWedge() @@ -110,7 +111,7 @@ namespace osu.Game.Screens.Select } } - public override bool IsPresent => base.IsPresent || Container == null; // Visibility is updated in the LoadComponentAsync callback + public override bool IsPresent => base.IsPresent || DisplayedContent == null; // Visibility is updated in the LoadComponentAsync callback private Container loadingInfo; @@ -124,9 +125,9 @@ namespace osu.Game.Screens.Select { State.Value = beatmap == null ? Visibility.Hidden : Visibility.Visible; - Container?.FadeOut(250); - Container?.Expire(); - Container = null; + DisplayedContent?.FadeOut(250); + DisplayedContent?.Expire(); + DisplayedContent = null; } if (beatmap == null) @@ -139,7 +140,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both, Shear = -Shear, - Depth = Container?.Depth + 1 ?? 0, + Depth = DisplayedContent?.Depth + 1 ?? 0, Children = new Drawable[] { new BeatmapInfoWedgeBackground(beatmap), @@ -151,7 +152,7 @@ namespace osu.Game.Screens.Select if (loaded != loadingInfo) return; removeOldInfo(); - Add(Container = loaded); + Add(DisplayedContent = loaded); }); } } From cffeb8641f484a860580bbcce38f977563e68674 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 May 2021 02:14:57 +0900 Subject: [PATCH 106/108] Make setters private for protected containers --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index d84052b94d..18615d9192 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -48,9 +48,9 @@ namespace osu.Game.Screens.Select private IBindable beatmapDifficulty; - protected Container DisplayedContent; + protected Container DisplayedContent { get; private set; } - protected WedgeInfoText Info; + protected WedgeInfoText Info { get; private set; } public BeatmapInfoWedge() { From d4658c609b61d3f15cccbe840023bb6003efc91c Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 5 May 2021 22:43:16 -0700 Subject: [PATCH 107/108] Fix warning text of bg source setting not being updated when user with supporter signs in/out --- .../Settings/Sections/UserInterface/MainMenuSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 7c4c88f344..5f703ed5a4 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -57,11 +57,11 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { base.LoadComplete(); - backgroundSourceDropdown.Current.BindValueChanged(source => + user.BindValueChanged(u => { const string not_supporter_note = "Changes to this setting will only apply with an active osu!supporter tag."; - backgroundSourceDropdown.WarningText = user.Value?.IsSupporter != true ? not_supporter_note : string.Empty; + backgroundSourceDropdown.WarningText = u.NewValue?.IsSupporter != true ? not_supporter_note : string.Empty; }, true); } } From af75c9ac82ab73f43188284eef35a67b7bf1dbfd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 May 2021 16:08:28 +0900 Subject: [PATCH 108/108] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 5aee9e15cc..99cda7693d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1e0eabfff7..e448972066 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index e26e727e69..43ed2d7dc8 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - +