From e3a5299b1acc6bba39f5b9de2726159add8ecb89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 19:23:43 +0900 Subject: [PATCH 001/134] Expose the loading player in `PlayerLoader` --- osu.Game/Screens/Play/PlayerLoader.cs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 41eb822e39..384561d616 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Play private bool readyForPush => !playerConsumed // don't push unless the player is completely loaded - && player?.LoadState == LoadState.Ready + && CurrentPlayer?.LoadState == LoadState.Ready // don't push if the user is hovering one of the panes, unless they are idle. && (IsHovered || idleTracker.IsIdle.Value) // don't push if the user is dragging a slider or otherwise. @@ -100,10 +100,14 @@ namespace osu.Game.Screens.Play private readonly Func createPlayer; - private Player player; + /// + /// The instance being loaded by this screen. + /// + [CanBeNull] + public Player CurrentPlayer { get; private set; } /// - /// Whether the curent player instance has been consumed via . + /// Whether the current player instance has been consumed via . /// private bool playerConsumed; @@ -237,12 +241,12 @@ namespace osu.Game.Screens.Play { base.OnResuming(last); - var lastScore = player.Score; + var lastScore = CurrentPlayer.Score; AudioSettings.ReferenceScore.Value = lastScore?.ScoreInfo; // prepare for a retry. - player = null; + CurrentPlayer = null; playerConsumed = false; cancelLoad(); @@ -346,7 +350,7 @@ namespace osu.Game.Screens.Play Debug.Assert(!playerConsumed); playerConsumed = true; - return player; + return CurrentPlayer; } private void prepareNewPlayer() @@ -354,11 +358,11 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - player = createPlayer(); - player.RestartCount = restartCount++; - player.RestartRequested = restartRequested; + CurrentPlayer = createPlayer(); + CurrentPlayer.RestartCount = restartCount++; + CurrentPlayer.RestartRequested = restartRequested; - LoadTask = LoadComponentAsync(player, _ => MetadataInfo.Loading = false); + LoadTask = LoadComponentAsync(CurrentPlayer, _ => MetadataInfo.Loading = false); } private void restartRequested() @@ -472,7 +476,7 @@ namespace osu.Game.Screens.Play if (isDisposing) { // if the player never got pushed, we should explicitly dispose it. - DisposalTask = LoadTask?.ContinueWith(_ => player?.Dispose()); + DisposalTask = LoadTask?.ContinueWith(_ => CurrentPlayer?.Dispose()); } } From 5164b4d6403bcb83fedbd8f83a35999eb1eeb58f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 19:36:33 +0900 Subject: [PATCH 002/134] Use `nullable` in `PlayerLoader` --- osu.Game/Screens/Play/PlayerLoader.cs | 54 ++++++++++++++------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 384561d616..ba720af2a1 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -1,10 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Diagnostics; using System.Threading.Tasks; -using JetBrains.Annotations; using ManagedBass.Fx; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -48,31 +49,31 @@ namespace osu.Game.Screens.Play public override bool HandlePositionalInput => true; // We show the previous screen status - protected override UserActivity InitialActivity => null; + protected override UserActivity? InitialActivity => null; protected override bool PlayResumeSound => false; - protected BeatmapMetadataDisplay MetadataInfo { get; private set; } + protected BeatmapMetadataDisplay MetadataInfo { get; private set; } = null!; /// /// A fill flow containing the player settings groups, exposed for the ability to hide it from inheritors of the player loader. /// - protected FillFlowContainer PlayerSettings { get; private set; } + protected FillFlowContainer PlayerSettings { get; private set; } = null!; - protected VisualSettings VisualSettings { get; private set; } + protected VisualSettings VisualSettings { get; private set; } = null!; - protected AudioSettings AudioSettings { get; private set; } + protected AudioSettings AudioSettings { get; private set; } = null!; - protected Task LoadTask { get; private set; } + protected Task? LoadTask { get; private set; } - protected Task DisposalTask { get; private set; } + protected Task? DisposalTask { get; private set; } private bool backgroundBrightnessReduction; private readonly BindableDouble volumeAdjustment = new BindableDouble(1); - private AudioFilter lowPassFilter; - private AudioFilter highPassFilter; + private AudioFilter lowPassFilter = null!; + private AudioFilter highPassFilter = null!; protected bool BackgroundBrightnessReduction { @@ -94,47 +95,45 @@ namespace osu.Game.Screens.Play // don't push if the user is hovering one of the panes, unless they are idle. && (IsHovered || idleTracker.IsIdle.Value) // don't push if the user is dragging a slider or otherwise. - && inputManager?.DraggedDrawable == null + && inputManager.DraggedDrawable == null // don't push if a focused overlay is visible, like settings. - && inputManager?.FocusedDrawable == null; + && inputManager.FocusedDrawable == null; private readonly Func createPlayer; /// /// The instance being loaded by this screen. /// - [CanBeNull] - public Player CurrentPlayer { get; private set; } + public Player? CurrentPlayer { get; private set; } /// /// Whether the current player instance has been consumed via . /// private bool playerConsumed; - private LogoTrackingContainer content; + private LogoTrackingContainer content = null!; private bool hideOverlays; - private InputManager inputManager; + private InputManager inputManager = null!; - private IdleTracker idleTracker; + private IdleTracker idleTracker = null!; - private ScheduledDelegate scheduledPushPlayer; + private ScheduledDelegate? scheduledPushPlayer; - [CanBeNull] - private EpilepsyWarning epilepsyWarning; + private EpilepsyWarning? epilepsyWarning; [Resolved(CanBeNull = true)] - private NotificationOverlay notificationOverlay { get; set; } + private NotificationOverlay? notificationOverlay { get; set; } [Resolved(CanBeNull = true)] - private VolumeOverlay volumeOverlay { get; set; } + private VolumeOverlay? volumeOverlay { get; set; } [Resolved] - private AudioManager audioManager { get; set; } + private AudioManager audioManager { get; set; } = null!; [Resolved(CanBeNull = true)] - private BatteryInfo batteryInfo { get; set; } + private BatteryInfo? batteryInfo { get; set; } public PlayerLoader(Func createPlayer) { @@ -241,6 +240,8 @@ namespace osu.Game.Screens.Play { base.OnResuming(last); + Debug.Assert(CurrentPlayer != null); + var lastScore = CurrentPlayer.Score; AudioSettings.ReferenceScore.Value = lastScore?.ScoreInfo; @@ -348,6 +349,7 @@ namespace osu.Game.Screens.Play private Player consumePlayer() { Debug.Assert(!playerConsumed); + Debug.Assert(CurrentPlayer != null); playerConsumed = true; return CurrentPlayer; @@ -484,7 +486,7 @@ namespace osu.Game.Screens.Play #region Mute warning - private Bindable muteWarningShownOnce; + private Bindable muteWarningShownOnce = null!; private int restartCount; @@ -539,7 +541,7 @@ namespace osu.Game.Screens.Play #region Low battery warning - private Bindable batteryWarningShownOnce; + private Bindable batteryWarningShownOnce = null!; private void showBatteryWarningIfNeeded() { From e3a8bb2d1c36c4995177a2826126e47dc2a0c6eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 19:23:49 +0900 Subject: [PATCH 003/134] Add test coverage of `SpectatorPlayer` failing to seek on inopportune frame arrival time --- .../Visual/Gameplay/TestSceneSpectator.cs | 54 ++++++++++++++++++- .../Visual/Spectator/TestSpectatorClient.cs | 5 +- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index d614815316..8b420cebc8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -70,6 +70,56 @@ namespace osu.Game.Tests.Visual.Gameplay }); } + [Test] + public void TestSeekToGameplayStartFramesArriveAfterPlayerLoad() + { + const double gameplay_start = 10000; + + loadSpectatingScreen(); + + start(); + + waitForPlayer(); + + sendFrames(startTime: gameplay_start); + + AddAssert("time is greater than seek target", () => currentFrameStableTime > gameplay_start); + } + + /// + /// Tests the same as but with the frames arriving just as is transitioning into existence. + /// + [Test] + public void TestSeekToGameplayStartFramesArriveAsPlayerLoaded() + { + const double gameplay_start = 10000; + + loadSpectatingScreen(); + + start(); + + AddUntilStep("wait for player loader", () => (Stack.CurrentScreen as PlayerLoader)?.IsLoaded == true); + + AddUntilStep("queue send frames on player load", () => + { + var loadingPlayer = (Stack.CurrentScreen as PlayerLoader)?.CurrentPlayer; + + if (loadingPlayer == null) + return false; + + loadingPlayer.OnLoadComplete += _ => + { + spectatorClient.SendFramesFromUser(streamingUser.Id, 10, gameplay_start); + }; + return true; + }); + + waitForPlayer(); + + AddUntilStep("state is playing", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Playing); + AddAssert("time is greater than seek target", () => currentFrameStableTime > gameplay_start); + } + [Test] public void TestFrameStarvationAndResume() { @@ -319,9 +369,9 @@ namespace osu.Game.Tests.Visual.Gameplay private void checkPaused(bool state) => AddUntilStep($"game is {(state ? "paused" : "playing")}", () => player.ChildrenOfType().First().IsPaused.Value == state); - private void sendFrames(int count = 10) + private void sendFrames(int count = 10, double startTime = 0) { - AddStep("send frames", () => spectatorClient.SendFramesFromUser(streamingUser.Id, count)); + AddStep("send frames", () => spectatorClient.SendFramesFromUser(streamingUser.Id, count, startTime)); } private void loadSpectatingScreen() diff --git a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs index f5da95bd7b..ac7cb43e02 100644 --- a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs +++ b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs @@ -88,7 +88,8 @@ namespace osu.Game.Tests.Visual.Spectator /// /// The user to send frames for. /// The total number of frames to send. - public void SendFramesFromUser(int userId, int count) + /// The time to start gameplay frames from. + public void SendFramesFromUser(int userId, int count, double startTime = 0) { var frames = new List(); @@ -102,7 +103,7 @@ namespace osu.Game.Tests.Visual.Spectator flush(); var buttonState = currentFrameIndex == lastFrameIndex ? ReplayButtonState.None : ReplayButtonState.Left1; - frames.Add(new LegacyReplayFrame(currentFrameIndex * 100, RNG.Next(0, 512), RNG.Next(0, 512), buttonState)); + frames.Add(new LegacyReplayFrame(currentFrameIndex * 100 + startTime, RNG.Next(0, 512), RNG.Next(0, 512), buttonState)); } flush(); From a4a0241800934b62349ac11b913e5e0a1c94c929 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 20:54:42 +0900 Subject: [PATCH 004/134] Use a more explicit flow to set and reset `GameplayClockContainer` start time --- .../Screens/Play/GameplayClockContainer.cs | 20 ++++++-- .../Play/MasterGameplayClockContainer.cs | 49 ++++++++++--------- osu.Game/Screens/Play/Player.cs | 10 ++-- osu.Game/Screens/Play/SpectatorPlayer.cs | 2 +- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 0fd524f976..b60a3e306e 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -41,6 +41,15 @@ namespace osu.Game.Screens.Play /// public event Action OnSeek; + /// + /// The time from which gameplay should start. Will be seeked to on calling . + /// + /// + /// If not set, a value of zero will be used. + /// Importantly, the value will be inferred from the current ruleset in unless specified. + /// + protected double? GameplayStartTime { get; private set; } + /// /// Creates a new . /// @@ -106,15 +115,20 @@ namespace osu.Game.Screens.Play /// /// Resets this and the source to an initial state ready for gameplay. /// - public virtual void Reset() + /// Whether to start the clock immediately. + /// A time to use for future calls as the definite start of gameplay. + public void Reset(bool startClock = false, double? gameplayStartTime = null) { + if (gameplayStartTime != null) + GameplayStartTime = gameplayStartTime; + ensureSourceClockSet(); - Seek(0); + Seek(GameplayStartTime ?? 0); // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); - if (!IsPaused.Value) + if (!IsPaused.Value && startClock) Start(); } diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index af58e9d910..ce7b6ef7f0 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -58,7 +58,6 @@ namespace osu.Game.Screens.Play private HardwareCorrectionOffsetClock platformOffsetClock; private MasterGameplayClock masterGameplayClock; private Bindable userAudioOffset; - private double startOffset; private IDisposable beatmapOffsetSubscription; @@ -90,26 +89,33 @@ namespace osu.Game.Screens.Play settings => settings.Offset, val => userBeatmapOffsetClock.Offset = val); - // sane default provided by ruleset. - startOffset = gameplayStartTime; - - if (!startAtGameplayStart) + if (GameplayStartTime == null) { - startOffset = Math.Min(0, startOffset); + // sane default provided by ruleset. + double offset = gameplayStartTime; - // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. - // this is commonly used to display an intro before the audio track start. - double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; - if (firstStoryboardEvent != null) - startOffset = Math.Min(startOffset, firstStoryboardEvent.Value); + if (!startAtGameplayStart) + { + offset = Math.Min(0, offset); - // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. - // this is not available as an option in the live editor but can still be applied via .osu editing. - if (beatmap.BeatmapInfo.AudioLeadIn > 0) - startOffset = Math.Min(startOffset, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn); + // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. + // this is commonly used to display an intro before the audio track start. + double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; + if (firstStoryboardEvent != null) + offset = Math.Min(offset, firstStoryboardEvent.Value); + + // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. + // this is not available as an option in the live editor but can still be applied via .osu editing. + if (beatmap.BeatmapInfo.AudioLeadIn > 0) + offset = Math.Min(offset, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn); + } + + // Reset may have been called externally before LoadComplete. + // If it was and the clock is in a playing state, we want to ensure that it isn't stopped here. + bool isStarted = !IsPaused.Value; + + Reset(startClock: isStarted, gameplayStartTime: offset); } - - Seek(startOffset); } protected override void OnIsPausedChanged(ValueChangedEvent isPaused) @@ -164,12 +170,6 @@ namespace osu.Game.Screens.Play Seek(skipTarget); } - public override void Reset() - { - base.Reset(); - Seek(startOffset); - } - protected override GameplayClock CreateGameplayClock(IFrameBasedClock source) { // Lazer's audio timings in general doesn't match stable. This is the result of user testing, albeit limited. @@ -231,6 +231,7 @@ namespace osu.Game.Screens.Play } private class HardwareCorrectionOffsetClock : FramedOffsetClock + { private readonly BindableDouble pauseRateAdjust; @@ -276,9 +277,9 @@ namespace osu.Game.Screens.Play } private class MasterGameplayClock : GameplayClock + { public readonly List> MutableNonGameplayAdjustments = new List>(); - public override IEnumerable> NonGameplayAdjustments => MutableNonGameplayAdjustments; public MasterGameplayClock(FramedOffsetClock underlyingClock) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cb8f4b6020..6ec21ef924 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -607,13 +607,13 @@ namespace osu.Game.Screens.Play private ScheduledDelegate frameStablePlaybackResetDelegate; /// - /// Seeks to a specific time in gameplay, bypassing frame stability. + /// Specify and seek to a custom start time from which gameplay should be observed. /// /// - /// Intermediate hitobject judgements may not be applied or reverted correctly during this seek. + /// This performance a non-frame-stable seek. Intermediate hitobject judgements may not be applied or reverted correctly during this seek. /// /// The destination time to seek to. - internal void NonFrameStableSeek(double time) + protected void SetGameplayStartTime(double time) { if (frameStablePlaybackResetDelegate?.Cancelled == false && !frameStablePlaybackResetDelegate.Completed) frameStablePlaybackResetDelegate.RunTask(); @@ -621,7 +621,7 @@ namespace osu.Game.Screens.Play bool wasFrameStable = DrawableRuleset.FrameStablePlayback; DrawableRuleset.FrameStablePlayback = false; - Seek(time); + GameplayClockContainer.Reset(gameplayStartTime: time); // Delay resetting frame-stable playback for one frame to give the FrameStabilityContainer a chance to seek. frameStablePlaybackResetDelegate = ScheduleAfterChildren(() => DrawableRuleset.FrameStablePlayback = wasFrameStable); @@ -981,7 +981,7 @@ namespace osu.Game.Screens.Play if (GameplayClockContainer.GameplayClock.IsRunning) throw new InvalidOperationException($"{nameof(StartGameplay)} should not be called when the gameplay clock is already running"); - GameplayClockContainer.Reset(); + GameplayClockContainer.Reset(true); } public override void OnSuspending(IScreen next) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index c415041081..c0682952c3 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Play } if (isFirstBundle && score.Replay.Frames.Count > 0) - NonFrameStableSeek(score.Replay.Frames[0].Time); + SetGameplayStartTime(score.Replay.Frames[0].Time); } protected override Score CreateScore(IBeatmap beatmap) => score; From e3ab5de8cd944a35228d47bba2623fbd5ee712ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Mar 2022 23:39:45 +0900 Subject: [PATCH 005/134] Tidy up constructor logic overlap with `gameplayStartTime` --- .../TestSceneMultiSpectatorScreen.cs | 2 +- .../Screens/Play/GameplayClockContainer.cs | 8 +-- .../Play/MasterGameplayClockContainer.cs | 69 ++++++++++--------- 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index e5e3fecd06..ed8b0bdd96 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -473,7 +473,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } protected override MasterGameplayClockContainer CreateMasterGameplayClockContainer(WorkingBeatmap beatmap) - => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0, gameplayStartTime.HasValue); + => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0); } } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index b60a3e306e..66f5cb0bd4 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -42,13 +42,13 @@ namespace osu.Game.Screens.Play public event Action OnSeek; /// - /// The time from which gameplay should start. Will be seeked to on calling . + /// The time from which the clock should start. Will be seeked to on calling . /// /// /// If not set, a value of zero will be used. /// Importantly, the value will be inferred from the current ruleset in unless specified. /// - protected double? GameplayStartTime { get; private set; } + protected double? StartTime { get; set; } /// /// Creates a new . @@ -120,10 +120,10 @@ namespace osu.Game.Screens.Play public void Reset(bool startClock = false, double? gameplayStartTime = null) { if (gameplayStartTime != null) - GameplayStartTime = gameplayStartTime; + StartTime = gameplayStartTime; ensureSourceClockSet(); - Seek(GameplayStartTime ?? 0); + Seek(StartTime ?? 0); // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index ce7b6ef7f0..58e8b5f1ad 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -49,9 +49,6 @@ namespace osu.Game.Screens.Play private readonly BindableDouble pauseFreqAdjust = new BindableDouble(1); private readonly WorkingBeatmap beatmap; - private readonly double gameplayStartTime; - private readonly bool startAtGameplayStart; - private readonly double firstHitObjectTime; private HardwareCorrectionOffsetClock userGlobalOffsetClock; private HardwareCorrectionOffsetClock userBeatmapOffsetClock; @@ -61,20 +58,29 @@ namespace osu.Game.Screens.Play private IDisposable beatmapOffsetSubscription; + private readonly double latestGameplayStartTime; + [Resolved] private RealmAccess realm { get; set; } [Resolved] private OsuConfigManager config { get; set; } - public MasterGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStartTime, bool startAtGameplayStart = false) + /// + /// Create a new master gameplay clock container. + /// + /// The beatmap to be used for time and metadata references. + /// The latest time which should be used when introducing gameplay. Will be used when skipping forward. + /// Whether to start from the provided latest start time rather than zero. + public MasterGameplayClockContainer(WorkingBeatmap beatmap, double latestGameplayStartTime, bool startFromLatestStartTime = false) : base(beatmap.Track) { this.beatmap = beatmap; - this.gameplayStartTime = gameplayStartTime; - this.startAtGameplayStart = startAtGameplayStart; - firstHitObjectTime = beatmap.Beatmap.HitObjects.First().StartTime; + this.latestGameplayStartTime = latestGameplayStartTime; + + if (startFromLatestStartTime) + StartTime = latestGameplayStartTime; } protected override void LoadComplete() @@ -89,33 +95,34 @@ namespace osu.Game.Screens.Play settings => settings.Offset, val => userBeatmapOffsetClock.Offset = val); - if (GameplayStartTime == null) - { - // sane default provided by ruleset. - double offset = gameplayStartTime; + // Reset may have been called externally before LoadComplete. + // If it was, and the clock is in a playing state, we want to ensure that it isn't stopped here. + bool isStarted = !IsPaused.Value; - if (!startAtGameplayStart) - { - offset = Math.Min(0, offset); + // If a custom start time was not specified, calculate the best value to use. + double gameplayStartTime = StartTime ?? findBeatmapStartTime(); - // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. - // this is commonly used to display an intro before the audio track start. - double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; - if (firstStoryboardEvent != null) - offset = Math.Min(offset, firstStoryboardEvent.Value); + Reset(startClock: isStarted, gameplayStartTime: gameplayStartTime); + } - // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. - // this is not available as an option in the live editor but can still be applied via .osu editing. - if (beatmap.BeatmapInfo.AudioLeadIn > 0) - offset = Math.Min(offset, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn); - } + private double findBeatmapStartTime() + { + // start with the originally provided latest time as a sane default. + double time = latestGameplayStartTime; - // Reset may have been called externally before LoadComplete. - // If it was and the clock is in a playing state, we want to ensure that it isn't stopped here. - bool isStarted = !IsPaused.Value; + // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. + // this is commonly used to display an intro before the audio track start. + double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; + if (firstStoryboardEvent != null) + time = Math.Min(time, firstStoryboardEvent.Value); - Reset(startClock: isStarted, gameplayStartTime: offset); - } + // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. + // this is not available as an option in the live editor but can still be applied via .osu editing. + double firstHitObjectTime = beatmap.Beatmap.HitObjects.First().StartTime; + if (beatmap.BeatmapInfo.AudioLeadIn > 0) + time = Math.Min(time, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn); + + return time; } protected override void OnIsPausedChanged(ValueChangedEvent isPaused) @@ -158,10 +165,10 @@ namespace osu.Game.Screens.Play /// public void Skip() { - if (GameplayClock.CurrentTime > gameplayStartTime - MINIMUM_SKIP_TIME) + if (GameplayClock.CurrentTime > latestGameplayStartTime - MINIMUM_SKIP_TIME) return; - double skipTarget = gameplayStartTime - MINIMUM_SKIP_TIME; + double skipTarget = latestGameplayStartTime - MINIMUM_SKIP_TIME; if (GameplayClock.CurrentTime < 0 && skipTarget > 6000) // double skip exception for storyboards with very long intros From c6be26eb018a4f26701f0b2bcbe58c742fa4a2e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 01:15:17 +0900 Subject: [PATCH 006/134] Rename start time calculation method and add more commenting to explain purpose better --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 58e8b5f1ad..529503e020 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -100,15 +100,18 @@ namespace osu.Game.Screens.Play bool isStarted = !IsPaused.Value; // If a custom start time was not specified, calculate the best value to use. - double gameplayStartTime = StartTime ?? findBeatmapStartTime(); + double gameplayStartTime = StartTime ?? findEarliestStartTime(); Reset(startClock: isStarted, gameplayStartTime: gameplayStartTime); } - private double findBeatmapStartTime() + private double findEarliestStartTime() { - // start with the originally provided latest time as a sane default. - double time = latestGameplayStartTime; + // here we are trying to find the time to start playback from the "zero" point. + // generally this is either zero, or some point earlier than zero in the case of storyboards, lead-ins etc. + + // start with the originally provided latest time (if before zero). + double time = Math.Min(0, latestGameplayStartTime); // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. // this is commonly used to display an intro before the audio track start. From b5ff9ed13a0d6e936d949606af3ca44fe5a00df0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 14:27:58 +0900 Subject: [PATCH 007/134] Add test coverage of multiplayer being paused when users are still loading --- .../Multiplayer/TestSceneMultiplayer.cs | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index e38da96bd5..2a3dbdaf95 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -494,17 +494,20 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true); - AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + AddAssert("Mods match current item", + () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); AddStep("Switch required mods", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Mods.Value = new Mod[] { new OsuModDoubleTime() }); - AddAssert("Mods don't match current item", () => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + AddAssert("Mods don't match current item", + () => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); AddStep("start match externally", () => multiplayerClient.StartMatch()); AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player); - AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + AddAssert("Mods match current item", + () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); } [Test] @@ -664,6 +667,41 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for results", () => multiplayerComponents.CurrentScreen is ResultsScreen); } + [Test] + public void TestGameplayDoesntStartWithNonLoadedUser() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, + } + } + }); + + pressReadyButton(); + + AddStep("join other user and ready", () => + { + multiplayerClient.AddUser(new APIUser { Id = 1234 }); + multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Ready); + }); + + AddStep("start match", () => + { + multiplayerClient.StartMatch(); + }); + + AddUntilStep("wait for player", () => multiplayerComponents.CurrentScreen is Player); + + AddWaitStep("wait some", 20); + + AddAssert("ensure gameplay hasn't started", () => this.ChildrenOfType().SingleOrDefault()?.IsRunning == false); + } + [Test] public void TestRoomSettingsReQueriedWhenJoiningRoom() { From 59aef88504a8408eb49b61d7362941ef177d7520 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 15:07:57 +0900 Subject: [PATCH 008/134] Simplify clock reset/start flow in `MultiSpectatorScreen` --- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 6747b8fc66..523301f4fd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -164,7 +164,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate base.LoadComplete(); masterClockContainer.Reset(); - masterClockContainer.Stop(); syncManager.ReadyToStart += onReadyToStart; syncManager.MasterState.BindValueChanged(onMasterStateChanged, true); @@ -198,8 +197,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate .DefaultIfEmpty(0) .Min(); - masterClockContainer.Seek(startTime); - masterClockContainer.Start(); + masterClockContainer.Reset(true, startTime); // Although the clock has been started, this flag is set to allow for later synchronisation state changes to also be able to start it. canStartMasterClock = true; From f09a9467226f335c95c823bb03b09fc99f13de4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 15:08:25 +0900 Subject: [PATCH 009/134] Start `GameplayClockContainer` paused for better state control --- .../Gameplay/TestSceneMasterGameplayClockContainer.cs | 5 +---- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs | 2 -- osu.Game/Screens/Play/GameplayClockContainer.cs | 6 +++--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs index 77b402ad3c..0ed432bbea 100644 --- a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs @@ -85,10 +85,7 @@ namespace osu.Game.Tests.Gameplay Add(gameplayClockContainer = new MasterGameplayClockContainer(working, 0)); - if (whileStopped) - gameplayClockContainer.Stop(); - - gameplayClockContainer.Reset(); + gameplayClockContainer.Reset(startClock: !whileStopped); }); AddStep($"set clock rate to {clockRate}", () => working.Track.AddAdjustment(AdjustableProperty.Frequency, new BindableDouble(clockRate))); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index 615bd41f3f..ececa1e497 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -55,8 +55,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public SpectatorGameplayClockContainer([NotNull] IClock sourceClock) : base(sourceClock) { - // the container should initially be in a stopped state until the catch-up clock is started by the sync manager. - Stop(); } protected override void Update() diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 66f5cb0bd4..226f5709c7 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Play /// /// Whether gameplay is paused. /// - public readonly BindableBool IsPaused = new BindableBool(); + public readonly BindableBool IsPaused = new BindableBool(true); /// /// The adjustable source clock used for gameplay. Should be used for seeks and clock control. @@ -115,7 +115,7 @@ namespace osu.Game.Screens.Play /// /// Resets this and the source to an initial state ready for gameplay. /// - /// Whether to start the clock immediately. + /// Whether to start the clock immediately, if not already started. /// A time to use for future calls as the definite start of gameplay. public void Reset(bool startClock = false, double? gameplayStartTime = null) { @@ -128,7 +128,7 @@ namespace osu.Game.Screens.Play // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); - if (!IsPaused.Value && startClock) + if (!IsPaused.Value || startClock) Start(); } From 0988c2b0fabd104792f7ebd7a4b3be49b1a26fb9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 15:28:48 +0900 Subject: [PATCH 010/134] Move `DrawableRuleset` binding to `LoadComplete` to avoid exceptions on `InputManager` access --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 29559f5036..be1105e7ff 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -133,6 +133,11 @@ namespace osu.Game.Rulesets.UI p.NewResult += (_, r) => NewResult?.Invoke(r); p.RevertResult += (_, r) => RevertResult?.Invoke(r); })); + } + + protected override void LoadComplete() + { + base.LoadComplete(); IsPaused.ValueChanged += paused => { From 611562c6502aaedeeba6f1c8ff883d51fb539518 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 16:21:14 +0900 Subject: [PATCH 011/134] Add more comments around catch up logic --- .../OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs | 5 ++++- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs index 1a5231e602..de23b4fef7 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/ISpectatorPlayerClock.cs @@ -17,8 +17,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate Bindable WaitingOnFrames { get; } /// - /// Whether this clock is resynchronising to the master clock. + /// Whether this clock is behind the master clock and running at a higher rate to catch up to it. /// + /// + /// Of note, this will be false if this clock is *ahead* of the master clock. + /// bool IsCatchingUp { get; set; } /// diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index ececa1e497..29afaf00d8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -59,6 +59,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate protected override void Update() { + // The SourceClock here is always a CatchUpSpectatorPlayerClock. // The player clock's running state is controlled externally, but the local pausing state needs to be updated to stop gameplay. if (SourceClock.IsRunning) Start(); From 4bc7c69bf70e7aa1bf91deec0e587523da0ea368 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 16:27:14 +0900 Subject: [PATCH 012/134] Fix test regression due to missing argument --- .../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index ed8b0bdd96..653a35417e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -473,7 +473,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } protected override MasterGameplayClockContainer CreateMasterGameplayClockContainer(WorkingBeatmap beatmap) - => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0); + => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0, true); } } } From 7cb7b03cee678db55530d8134d39295089a6a216 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Mar 2022 18:16:45 +0900 Subject: [PATCH 013/134] Fix `TestSceneMasterGameplayClockContainer` not clearing previous test clocks --- .../Gameplay/TestSceneMasterGameplayClockContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs index 0ed432bbea..b2dd6cbd97 100644 --- a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Gameplay var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); working.LoadTrack(); - Add(gameplayClockContainer = new MasterGameplayClockContainer(working, 0)); + Child = gameplayClockContainer = new MasterGameplayClockContainer(working, 0); }); AddStep("start clock", () => gameplayClockContainer.Start()); @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Gameplay var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); working.LoadTrack(); - Add(gameplayClockContainer = new MasterGameplayClockContainer(working, 0)); + Child = gameplayClockContainer = new MasterGameplayClockContainer(working, 0); }); AddStep("start clock", () => gameplayClockContainer.Start()); @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Gameplay working = new ClockBackedTestWorkingBeatmap(new OsuRuleset().RulesetInfo, new FramedClock(new ManualClock()), Audio); working.LoadTrack(); - Add(gameplayClockContainer = new MasterGameplayClockContainer(working, 0)); + Child = gameplayClockContainer = new MasterGameplayClockContainer(working, 0); gameplayClockContainer.Reset(startClock: !whileStopped); }); From 8ca9cbc866e089338b09585890c97fc5dfce8da4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 14:43:33 +0900 Subject: [PATCH 014/134] Set a more correct initial value for `pauseFreqAdjust` As the `GameplayClock` now starts paused, this value needs to match to ensure things work correctly. For a better explanation of how we got here, see discussion at https://github.com/ppy/osu/pull/17302#discussion_r830017735. --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 529503e020..9bcbbf789b 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Play private double totalAppliedOffset => userBeatmapOffsetClock.RateAdjustedOffset + userGlobalOffsetClock.RateAdjustedOffset + platformOffsetClock.RateAdjustedOffset; - private readonly BindableDouble pauseFreqAdjust = new BindableDouble(1); + private readonly BindableDouble pauseFreqAdjust = new BindableDouble(); // Important that this starts at zero, matching the paused state of the clock. private readonly WorkingBeatmap beatmap; From 16dc2f6ef50bccada9e9cba2f3e934324204140c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 14:51:21 +0900 Subject: [PATCH 015/134] Adjust `TestSceneFailAnimation` to account for initial frequency change --- osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs index 744227c55e..83d7d769df 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs @@ -56,10 +56,11 @@ namespace osu.Game.Tests.Visual.Gameplay private double lastFrequency = double.MaxValue; - protected override void Update() + protected override void UpdateAfterChildren() { - base.Update(); + base.UpdateAfterChildren(); + // This must be done in UpdateAfterChildren to allow the gameplay clock to have updated before checking values. double freq = Beatmap.Value.Track.AggregateFrequency.Value; FrequencyIncreased |= freq > lastFrequency; From 1049e349e39a8944c223b71ad8d0a621a3965781 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 14:55:01 +0900 Subject: [PATCH 016/134] Add test coverage of user offset being set before construction of `MasterGameplayClockContainer` --- .../Gameplay/TestSceneMasterGameplayClockContainer.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs index b2dd6cbd97..75be7b0362 100644 --- a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs @@ -73,11 +73,15 @@ namespace osu.Game.Tests.Gameplay public void TestSeekPerformsInGameplayTime( [Values(1.0, 0.5, 2.0)] double clockRate, [Values(0.0, 200.0, -200.0)] double userOffset, - [Values(false, true)] bool whileStopped) + [Values(false, true)] bool whileStopped, + [Values(false, true)] bool setAudioOffsetBeforeConstruction) { ClockBackedTestWorkingBeatmap working = null; GameplayClockContainer gameplayClockContainer = null; + if (setAudioOffsetBeforeConstruction) + AddStep($"preset audio offset to {userOffset}", () => localConfig.SetValue(OsuSetting.AudioOffset, userOffset)); + AddStep("create container", () => { working = new ClockBackedTestWorkingBeatmap(new OsuRuleset().RulesetInfo, new FramedClock(new ManualClock()), Audio); @@ -89,7 +93,9 @@ namespace osu.Game.Tests.Gameplay }); AddStep($"set clock rate to {clockRate}", () => working.Track.AddAdjustment(AdjustableProperty.Frequency, new BindableDouble(clockRate))); - AddStep($"set audio offset to {userOffset}", () => localConfig.SetValue(OsuSetting.AudioOffset, userOffset)); + + if (!setAudioOffsetBeforeConstruction) + AddStep($"set audio offset to {userOffset}", () => localConfig.SetValue(OsuSetting.AudioOffset, userOffset)); AddStep("seek to 2500", () => gameplayClockContainer.Seek(2500)); AddAssert("gameplay clock time = 2500", () => Precision.AlmostEquals(gameplayClockContainer.CurrentTime, 2500, 10f)); From c2b2d443ed070da9b8fafa5b871e235f40ca13f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 15:31:12 +0900 Subject: [PATCH 017/134] Add more comprehensive assert output to try and discern CI issues --- .../Visual/Gameplay/TestSceneLeadIn.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index 5a1fc1b1e5..58d2e4a1da 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -36,10 +36,10 @@ namespace osu.Game.Tests.Visual.Gameplay BeatmapInfo = { AudioLeadIn = leadIn } }); - AddAssert($"first frame is {expectedStartTime}", () => + AddStep($"check first frame time", () => { - Debug.Assert(player.FirstFrameClockTime != null); - return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms); + Assert.That(player.FirstFrameClockTime, Is.Not.Null); + Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); }); } @@ -59,10 +59,10 @@ namespace osu.Game.Tests.Visual.Gameplay loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard); - AddAssert($"first frame is {expectedStartTime}", () => + AddStep($"check first frame time", () => { - Debug.Assert(player.FirstFrameClockTime != null); - return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms); + Assert.That(player.FirstFrameClockTime, Is.Not.Null); + Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); }); } @@ -97,10 +97,10 @@ namespace osu.Game.Tests.Visual.Gameplay loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard); - AddAssert($"first frame is {expectedStartTime}", () => + AddStep($"check first frame time", () => { - Debug.Assert(player.FirstFrameClockTime != null); - return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms); + Assert.That(player.FirstFrameClockTime, Is.Not.Null); + Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); }); } From 065bb60324c6f62393aee92799b4a6d916a9fa77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Apr 2022 16:05:11 +0900 Subject: [PATCH 018/134] Remove unused using statements --- osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index 58d2e4a1da..5ec9c6c474 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -1,12 +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.Diagnostics; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Timing; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Osu; From 808f0ecb74d2f2c65fdc9558d1eb07a616b6ac18 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 14:10:50 +0900 Subject: [PATCH 019/134] Ensure running state is updated before performing `Seek` in `GameplayClockContainer.Reset` --- osu.Game/Screens/Play/GameplayClockContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 226f5709c7..f2df00d691 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -122,14 +122,14 @@ namespace osu.Game.Screens.Play if (gameplayStartTime != null) StartTime = gameplayStartTime; - ensureSourceClockSet(); - Seek(StartTime ?? 0); - // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); if (!IsPaused.Value || startClock) Start(); + + ensureSourceClockSet(); + Seek(StartTime ?? 0); } /// From cb6e557212a5156ba2f59dd542943c878414b40f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 14:11:23 +0900 Subject: [PATCH 020/134] Fix `MasterGameplayClockContainer` having incorrect rate-based offsets immediately after `LoadComplete` I've attempted to explain why this is required using inline comments. There's also further conversation at https://github.com/ppy/osu/pull/17302#issuecomment-1091850927. --- .../Play/MasterGameplayClockContainer.cs | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 9bcbbf789b..54901746fc 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -130,17 +130,32 @@ namespace osu.Game.Screens.Play protected override void OnIsPausedChanged(ValueChangedEvent isPaused) { - // The source is stopped by a frequency fade first. - if (isPaused.NewValue) + if (IsLoaded) { - this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => + // During normal operation, the source is stopped after performing a frequency ramp. + if (isPaused.NewValue) { - if (IsPaused.Value == isPaused.NewValue) - AdjustableSource.Stop(); - }); + this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => + { + if (IsPaused.Value == isPaused.NewValue) + AdjustableSource.Stop(); + }); + } + else + this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); } else - this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); + { + if (isPaused.NewValue) + AdjustableSource.Stop(); + + // If not yet loaded, we still want to ensure relevant state is correct, as it is used for offset calculations. + pauseFreqAdjust.Value = isPaused.NewValue ? 0 : 1; + + // We must also process underlying gameplay clocks to update rate-adjusted offsets with the new frequency adjustment. + // Without doing this, an initial seek may be performed with the wrong offset. + GameplayClock.UnderlyingClock.ProcessFrame(); + } } public override void Start() From 01cec7d3fb0e11f8e8f0b55a4ad509b6f6865a8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Apr 2022 19:37:01 +0900 Subject: [PATCH 021/134] Remove unnecessary string literals --- osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index 5ec9c6c474..b90bd93002 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Gameplay BeatmapInfo = { AudioLeadIn = leadIn } }); - AddStep($"check first frame time", () => + AddStep("check first frame time", () => { Assert.That(player.FirstFrameClockTime, Is.Not.Null); Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard); - AddStep($"check first frame time", () => + AddStep("check first frame time", () => { Assert.That(player.FirstFrameClockTime, Is.Not.Null); Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Gameplay loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard); - AddStep($"check first frame time", () => + AddStep("check first frame time", () => { Assert.That(player.FirstFrameClockTime, Is.Not.Null); Assert.That(player.FirstFrameClockTime.Value, Is.EqualTo(expectedStartTime).Within(lenience_ms)); From 0e1d17d1d191f87bcff7c324b9ad7a2da5de0b37 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 12 Apr 2022 13:11:22 +0900 Subject: [PATCH 022/134] Use zh-Hant instead of zh-TW --- osu.Game/Localisation/Language.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index dc1fac47a8..ab96d01c82 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -110,6 +110,6 @@ namespace osu.Game.Localisation // zh_hk, [Description(@"繁體中文(台灣)")] - zh_tw + zh_hant } } From 282fccb4c8842ff30cde671989a620303f4dc0f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 12:24:47 +0900 Subject: [PATCH 023/134] Fix typo in xmldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- 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 6ec21ef924..be19565657 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -610,7 +610,7 @@ namespace osu.Game.Screens.Play /// Specify and seek to a custom start time from which gameplay should be observed. /// /// - /// This performance a non-frame-stable seek. Intermediate hitobject judgements may not be applied or reverted correctly during this seek. + /// This performs a non-frame-stable seek. Intermediate hitobject judgements may not be applied or reverted correctly during this seek. /// /// The destination time to seek to. protected void SetGameplayStartTime(double time) From d4286255a094ac759e40e34c9be3aaa9caaf85b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 13:49:58 +0900 Subject: [PATCH 024/134] Expose and set `GameplayStartTime` directly, rather than via `Reset` parameter --- .../Multiplayer/Spectate/MultiSpectatorScreen.cs | 3 ++- osu.Game/Screens/Play/GameplayClockContainer.cs | 8 ++------ osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 3 ++- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 523301f4fd..2d03276fe5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -197,7 +197,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate .DefaultIfEmpty(0) .Min(); - masterClockContainer.Reset(true, startTime); + masterClockContainer.StartTime = startTime; + masterClockContainer.Reset(true); // Although the clock has been started, this flag is set to allow for later synchronisation state changes to also be able to start it. canStartMasterClock = true; diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index f2df00d691..721abc66f8 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Play /// If not set, a value of zero will be used. /// Importantly, the value will be inferred from the current ruleset in unless specified. /// - protected double? StartTime { get; set; } + public double? StartTime { get; set; } /// /// Creates a new . @@ -116,12 +116,8 @@ namespace osu.Game.Screens.Play /// Resets this and the source to an initial state ready for gameplay. /// /// Whether to start the clock immediately, if not already started. - /// A time to use for future calls as the definite start of gameplay. - public void Reset(bool startClock = false, double? gameplayStartTime = null) + public void Reset(bool startClock = false) { - if (gameplayStartTime != null) - StartTime = gameplayStartTime; - // Manually stop the source in order to not affect the IsPaused state. AdjustableSource.Stop(); diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 54901746fc..88dc6db0f7 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -100,9 +100,9 @@ namespace osu.Game.Screens.Play bool isStarted = !IsPaused.Value; // If a custom start time was not specified, calculate the best value to use. - double gameplayStartTime = StartTime ?? findEarliestStartTime(); + StartTime ??= findEarliestStartTime(); - Reset(startClock: isStarted, gameplayStartTime: gameplayStartTime); + Reset(startClock: isStarted); } private double findEarliestStartTime() diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index be19565657..f99b07c313 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -621,7 +621,8 @@ namespace osu.Game.Screens.Play bool wasFrameStable = DrawableRuleset.FrameStablePlayback; DrawableRuleset.FrameStablePlayback = false; - GameplayClockContainer.Reset(gameplayStartTime: time); + GameplayClockContainer.StartTime = time; + GameplayClockContainer.Reset(); // Delay resetting frame-stable playback for one frame to give the FrameStabilityContainer a chance to seek. frameStablePlaybackResetDelegate = ScheduleAfterChildren(() => DrawableRuleset.FrameStablePlayback = wasFrameStable); From a59c6013c790f305d8567d98ec4e3a846c29fdeb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 14:03:52 +0900 Subject: [PATCH 025/134] Rename `latestGameplayStartTime` to `skipTargetTime` --- .../Play/MasterGameplayClockContainer.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 88dc6db0f7..61f31db435 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Play private IDisposable beatmapOffsetSubscription; - private readonly double latestGameplayStartTime; + private readonly double skipTargetTime; [Resolved] private RealmAccess realm { get; set; } @@ -70,17 +70,17 @@ namespace osu.Game.Screens.Play /// Create a new master gameplay clock container. /// /// The beatmap to be used for time and metadata references. - /// The latest time which should be used when introducing gameplay. Will be used when skipping forward. - /// Whether to start from the provided latest start time rather than zero. - public MasterGameplayClockContainer(WorkingBeatmap beatmap, double latestGameplayStartTime, bool startFromLatestStartTime = false) + /// The latest time which should be used when introducing gameplay. Will be used when skipping forward. + /// Whether to start from the provided latest start time rather than zero. + public MasterGameplayClockContainer(WorkingBeatmap beatmap, double skipTargetTime, bool startFromSkipTarget = false) : base(beatmap.Track) { this.beatmap = beatmap; - this.latestGameplayStartTime = latestGameplayStartTime; + this.skipTargetTime = skipTargetTime; - if (startFromLatestStartTime) - StartTime = latestGameplayStartTime; + if (startFromSkipTarget) + StartTime = skipTargetTime; } protected override void LoadComplete() @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Play // generally this is either zero, or some point earlier than zero in the case of storyboards, lead-ins etc. // start with the originally provided latest time (if before zero). - double time = Math.Min(0, latestGameplayStartTime); + double time = Math.Min(0, skipTargetTime); // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. // this is commonly used to display an intro before the audio track start. @@ -183,10 +183,10 @@ namespace osu.Game.Screens.Play /// public void Skip() { - if (GameplayClock.CurrentTime > latestGameplayStartTime - MINIMUM_SKIP_TIME) + if (GameplayClock.CurrentTime > skipTargetTime - MINIMUM_SKIP_TIME) return; - double skipTarget = latestGameplayStartTime - MINIMUM_SKIP_TIME; + double skipTarget = skipTargetTime - MINIMUM_SKIP_TIME; if (GameplayClock.CurrentTime < 0 && skipTarget > 6000) // double skip exception for storyboards with very long intros From fbf0e5a45cf4f4f073126a717b35c6edf258c412 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 14:09:49 +0900 Subject: [PATCH 026/134] Remove `startFromSkipTarget` parameter and update usages that required said behaviour --- osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs | 7 +++++-- .../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 8 ++++---- osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs | 2 +- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 7 +------ 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index 76ec35d87d..e0a497cf24 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Gameplay [Test] public void TestSampleHasLifetimeEndWithInitialClockTime() { - GameplayClockContainer gameplayContainer = null; + MasterGameplayClockContainer gameplayContainer = null; DrawableStoryboardSample sample = null; AddStep("create container", () => @@ -96,8 +96,11 @@ namespace osu.Game.Tests.Gameplay var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); working.LoadTrack(); - Add(gameplayContainer = new MasterGameplayClockContainer(working, 1000, true) + const double start_time = 1000; + + Add(gameplayContainer = new MasterGameplayClockContainer(working, start_time) { + StartTime = start_time, IsPaused = { Value = true }, Child = new FrameStabilityContainer { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 653a35417e..703b526e8c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -464,16 +464,16 @@ namespace osu.Game.Tests.Visual.Multiplayer private class TestMultiSpectatorScreen : MultiSpectatorScreen { - private readonly double? gameplayStartTime; + private readonly double? startTime; - public TestMultiSpectatorScreen(Room room, MultiplayerRoomUser[] users, double? gameplayStartTime = null) + public TestMultiSpectatorScreen(Room room, MultiplayerRoomUser[] users, double? startTime = null) : base(room, users) { - this.gameplayStartTime = gameplayStartTime; + this.startTime = startTime; } protected override MasterGameplayClockContainer CreateMasterGameplayClockContainer(WorkingBeatmap beatmap) - => new MasterGameplayClockContainer(beatmap, gameplayStartTime ?? 0, true); + => new MasterGameplayClockContainer(beatmap, 0) { StartTime = startTime ?? 0 }; } } } diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs index f49603c754..d9e19df350 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Edit.GameplayTest } protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) - => new MasterGameplayClockContainer(beatmap, editorState.Time, true); + => new MasterGameplayClockContainer(beatmap, gameplayStart) { StartTime = editorState.Time }; protected override void LoadComplete() { diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 61f31db435..c5a5c14070 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -71,16 +71,11 @@ namespace osu.Game.Screens.Play /// /// The beatmap to be used for time and metadata references. /// The latest time which should be used when introducing gameplay. Will be used when skipping forward. - /// Whether to start from the provided latest start time rather than zero. - public MasterGameplayClockContainer(WorkingBeatmap beatmap, double skipTargetTime, bool startFromSkipTarget = false) + public MasterGameplayClockContainer(WorkingBeatmap beatmap, double skipTargetTime) : base(beatmap.Track) { this.beatmap = beatmap; - this.skipTargetTime = skipTargetTime; - - if (startFromSkipTarget) - StartTime = skipTargetTime; } protected override void LoadComplete() From 2bb0d9e6d3d52a4e9da8f7405a95cedc1a8cfe2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 17:02:33 +0900 Subject: [PATCH 027/134] Expose required classes/properties for mocking --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 4 ++-- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 4c10949225..e29aee2c6d 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -114,12 +114,12 @@ namespace osu.Game.Online.Multiplayer /// /// The corresponding to the local player, if available. /// - public MultiplayerRoomUser? LocalUser => Room?.Users.SingleOrDefault(u => u.User?.Id == API.LocalUser.Value.Id); + public virtual MultiplayerRoomUser? LocalUser => Room?.Users.SingleOrDefault(u => u.User?.Id == API.LocalUser.Value.Id); /// /// Whether the is the host in . /// - public bool IsHost + public virtual bool IsHost { get { diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 07506ba1f0..4ca6d79b19 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -25,7 +25,7 @@ namespace osu.Game.Online.Rooms /// This differs from a regular download tracking composite as this accounts for the /// databased beatmap set's checksum, to disallow from playing with an altered version of the beatmap. /// - public sealed class OnlinePlayBeatmapAvailabilityTracker : CompositeDrawable + public class OnlinePlayBeatmapAvailabilityTracker : CompositeDrawable { public readonly IBindable SelectedItem = new Bindable(); @@ -41,7 +41,7 @@ namespace osu.Game.Online.Rooms /// /// The availability state of the currently selected playlist item. /// - public IBindable Availability => availability; + public virtual IBindable Availability => availability; private readonly Bindable availability = new Bindable(BeatmapAvailability.NotDownloaded()); From 1f01714ec207c4fb4090efe72769234037bc9071 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 17:02:53 +0900 Subject: [PATCH 028/134] Apply initial structural changes to `TestSceneMatchStartControl` --- .../Multiplayer/TestSceneMatchStartControl.cs | 233 +++++++++++------- 1 file changed, 148 insertions(+), 85 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 5c2fd26857..dcf73f9633 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -3,74 +3,119 @@ using System; using System.Linq; +using Moq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Utils; -using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.Countdown; using osu.Game.Online.Rooms; -using osu.Game.Rulesets; +using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; -using osu.Game.Tests.Resources; using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMatchStartControl : MultiplayerTestScene + public class TestSceneMatchStartControl : OsuManualInputManagerTestScene { + private readonly Mock multiplayerClient = new Mock(); + private readonly Mock availabilityTracker = new Mock(); + + private readonly Bindable beatmapAvailability = new Bindable(); + private readonly Bindable room = new Bindable(); + + private MultiplayerRoom multiplayerRoom; + private MultiplayerRoomUser localUser; + private OngoingOperationTracker ongoingOperationTracker; + + private PopoverContainer content; private MatchStartControl control; - private BeatmapSetInfo importedSet; - private readonly Bindable selectedItem = new Bindable(); - - private BeatmapManager beatmaps; - private RulesetStore rulesets; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => + new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) { Model = { BindTarget = room } }; [BackgroundDependencyLoader] - private void load(GameHost host, AudioManager audio) + private void load() { - Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default)); - Dependencies.Cache(Realm); + Dependencies.CacheAs(multiplayerClient.Object); + Dependencies.CacheAs(ongoingOperationTracker = new OngoingOperationTracker()); + + availabilityTracker.SetupGet(a => a.Availability).Returns(beatmapAvailability); + Dependencies.CacheAs(availabilityTracker.Object); + + multiplayerClient.SetupGet(m => m.LocalUser).Returns(() => localUser); + multiplayerClient.SetupGet(m => m.Room).Returns(() => multiplayerRoom); + + // By default, the local user is to be the host. + multiplayerClient.SetupGet(m => m.IsHost).Returns(true); + + // Assume all state changes are accepted by the server. + multiplayerClient.Setup(m => m.ChangeState(It.IsAny())) + .Callback((MultiplayerUserState r) => + { + localUser.State = r; + raiseRoomUpdated(); + }); + + Children = new Drawable[] + { + ongoingOperationTracker, + content = new PopoverContainer { RelativeSizeAxes = Axes.Both } + }; } - [SetUp] - public new void Setup() => Schedule(() => + [SetUpSteps] + public void SetUpSteps() { - AvailabilityTracker.SelectedItem.BindTo(selectedItem); - - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); - importedSet = beatmaps.GetAllUsableBeatmapSets().First(); - Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()); - - selectedItem.Value = new PlaylistItem(Beatmap.Value.BeatmapInfo) + AddStep("reset state", () => { - RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID - }; + multiplayerClient.Invocations.Clear(); - Child = new PopoverContainer + beatmapAvailability.Value = BeatmapAvailability.LocallyAvailable(); + + var playlistItem = new PlaylistItem(Beatmap.Value.BeatmapInfo) + { + RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID + }; + + room.Value = new Room + { + Playlist = { playlistItem }, + CurrentPlaylistItem = { Value = playlistItem } + }; + + multiplayerRoom = new MultiplayerRoom(0) + { + Playlist = + { + new MultiplayerPlaylistItem(playlistItem), + }, + Users = + { + (localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id) { User = API.LocalUser.Value }), + } + }; + }); + + AddStep("create control", () => { - RelativeSizeAxes = Axes.Both, - Child = control = new MatchStartControl + content.Child = control = new MatchStartControl { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(250, 50), - } - }; - }); + }; + }); + } [Test] public void TestStartWithCountdown() @@ -85,8 +130,12 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - AddStep("finish countdown", () => MultiplayerClient.SkipToEndOfCountdown()); - AddUntilStep("match started", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.WaitingForLoad); + AddStep("check request received", () => + { + multiplayerClient.Verify(m => m.SendMatchRequest(It.Is(req => + req.Duration == TimeSpan.FromSeconds(10) + )), Times.Once); + }); } [Test] @@ -110,7 +159,7 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - AddStep("finish countdown", () => MultiplayerClient.SkipToEndOfCountdown()); + // AddStep("finish countdown", skipToEndOfCountdown); AddUntilStep("match not started", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); } @@ -119,32 +168,32 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add second user as host", () => { - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); - MultiplayerClient.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }); + multiplayerClient.Object.TransferHost(2); }); - AddStep("start with countdown", () => MultiplayerClient.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(2) }).WaitSafely()); + AddStep("start with countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(2) }).WaitSafely()); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); } [Test] public void TestCountdownWhileSpectating() { - AddStep("set spectating", () => MultiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating)); - AddUntilStep("local user is spectating", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating); + AddStep("set spectating", () => changeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating)); + AddUntilStep("local user is spectating", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Spectating); AddAssert("countdown button is visible", () => this.ChildrenOfType().Single().IsPresent); AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); - AddStep("add second user", () => MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" })); + AddStep("add second user", () => addUser(new APIUser { Id = 2, Username = "Another user" })); AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); - AddStep("set second user ready", () => MultiplayerClient.ChangeUserState(2, MultiplayerUserState.Ready)); + AddStep("set second user ready", () => changeUserState(2, MultiplayerUserState.Ready)); AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); } @@ -153,44 +202,44 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add second user as host", () => { - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); - MultiplayerClient.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }); + multiplayerClient.Object.TransferHost(2); }); - AddStep("start countdown", () => MultiplayerClient.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(1) }).WaitSafely()); - AddUntilStep("countdown started", () => MultiplayerClient.Room?.Countdown != null); + AddStep("start countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(1) }).WaitSafely()); + AddUntilStep("countdown started", () => multiplayerClient.Object.Room?.Countdown != null); - AddStep("transfer host to local user", () => MultiplayerClient.TransferHost(API.LocalUser.Value.OnlineID)); - AddUntilStep("local user is host", () => MultiplayerClient.Room?.Host?.Equals(MultiplayerClient.LocalUser) == true); + AddStep("transfer host to local user", () => multiplayerClient.Object.TransferHost(API.LocalUser.Value.OnlineID)); + AddUntilStep("local user is host", () => multiplayerClient.Object.Room?.Host?.Equals(multiplayerClient.Object.LocalUser) == true); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); - AddAssert("countdown still active", () => MultiplayerClient.Room?.Countdown != null); + AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); + AddAssert("countdown still active", () => multiplayerClient.Object.Room?.Countdown != null); } [Test] public void TestCountdownButtonVisibilityWithAutoStartEnablement() { ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); + AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); AddUntilStep("countdown button visible", () => this.ChildrenOfType().Single().IsPresent); - AddStep("enable auto start", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); + AddStep("enable auto start", () => multiplayerClient.Object.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); + AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); AddUntilStep("countdown button not visible", () => !this.ChildrenOfType().Single().IsPresent); } [Test] public void TestClickingReadyButtonUnReadiesDuringAutoStart() { - AddStep("enable auto start", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); + AddStep("enable auto start", () => multiplayerClient.Object.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); + AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("local user became idle", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Idle); + AddUntilStep("local user became idle", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Idle); } [Test] @@ -204,9 +253,9 @@ namespace osu.Game.Tests.Visual.Multiplayer return readyButton.Enabled.Value; }); - AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); + // TODO: AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); AddUntilStep("ready button disabled", () => !readyButton.Enabled.Value); - AddStep("undelete beatmap", () => beatmaps.Undelete(importedSet)); + // TODO: AddStep("undelete beatmap", () => beatmaps.Undelete(importedSet)); AddUntilStep("ready button enabled back", () => readyButton.Enabled.Value); } @@ -215,15 +264,15 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add second user as host", () => { - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); - MultiplayerClient.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }); + multiplayerClient.Object.TransferHost(2); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); } [TestCase(true)] @@ -232,14 +281,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("setup", () => { - MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0); + multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); if (!allReady) - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); + addUser(new APIUser { Id = 2, Username = "Another user" }); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); verifyGameplayStartFlow(); } @@ -249,12 +298,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add host", () => { - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); - MultiplayerClient.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }); + multiplayerClient.Object.TransferHost(2); }); ClickButtonWhenEnabled(); - AddStep("make user host", () => MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0)); + AddStep("make user host", () => multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0)); verifyGameplayStartFlow(); } @@ -264,17 +313,17 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("setup", () => { - MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0); - MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" }); + multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); + addUser(new APIUser { Id = 2, Username = "Another user" }); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); - AddStep("transfer host", () => MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[1].UserID ?? 0)); + AddStep("transfer host", () => multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[1].UserID ?? 0)); ClickButtonWhenEnabled(); - AddUntilStep("user is idle (match not started)", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle (match not started)", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } @@ -285,42 +334,56 @@ namespace osu.Game.Tests.Visual.Multiplayer const int users = 10; AddStep("setup", () => { - MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0); + multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); for (int i = 0; i < users; i++) - MultiplayerClient.AddUser(new APIUser { Id = i, Username = "Another user" }); + addUser(new APIUser { Id = i, Username = "Another user" }); }); if (!isHost) - AddStep("transfer host", () => MultiplayerClient.TransferHost(2)); + AddStep("transfer host", () => multiplayerClient.Object.TransferHost(2)); ClickButtonWhenEnabled(); AddRepeatStep("change user ready state", () => { - MultiplayerClient.ChangeUserState(RNG.Next(0, users), RNG.NextBool() ? MultiplayerUserState.Ready : MultiplayerUserState.Idle); + changeUserState(RNG.Next(0, users), RNG.NextBool() ? MultiplayerUserState.Ready : MultiplayerUserState.Idle); }, 20); AddRepeatStep("ready all users", () => { - var nextUnready = MultiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + var nextUnready = multiplayerClient.Object.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); if (nextUnready != null) - MultiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); + changeUserState(nextUnready.UserID, MultiplayerUserState.Ready); }, users); } private void verifyGameplayStartFlow() { - AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user waiting for load", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + AddUntilStep("user waiting for load", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); AddStep("finish gameplay", () => { - MultiplayerClient.ChangeUserState(MultiplayerClient.Room?.Users[0].UserID ?? 0, MultiplayerUserState.Loaded); - MultiplayerClient.ChangeUserState(MultiplayerClient.Room?.Users[0].UserID ?? 0, MultiplayerUserState.FinishedPlay); + changeUserState(multiplayerClient.Object.Room?.Users[0].UserID ?? 0, MultiplayerUserState.Loaded); + changeUserState(multiplayerClient.Object.Room?.Users[0].UserID ?? 0, MultiplayerUserState.FinishedPlay); }); AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } + + private void changeUserState(int userId, MultiplayerUserState newState) + { + multiplayerRoom.Users.Single(u => u.UserID == userId).State = newState; + raiseRoomUpdated(); + } + + private void addUser(APIUser user) + { + multiplayerRoom.Users.Add(new MultiplayerRoomUser(user.Id) { User = user }); + raiseRoomUpdated(); + } + + private void raiseRoomUpdated() => multiplayerClient.Raise(m => m.RoomUpdated -= null); } } From ca44d2c4dcda8df808ce615f25ef5002e534b98a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 17:32:04 +0900 Subject: [PATCH 029/134] Setup start/stop requests --- .../Multiplayer/TestSceneMatchStartControl.cs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index dcf73f9633..dabf8c5267 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -66,6 +66,23 @@ namespace osu.Game.Tests.Visual.Multiplayer raiseRoomUpdated(); }); + multiplayerClient.Setup(m => m.SendMatchRequest(It.IsAny())) + .Callback((MatchUserRequest request) => + { + switch (request) + { + case StartMatchCountdownRequest countdownStart: + multiplayerRoom.Countdown = new MatchStartCountdown { TimeRemaining = countdownStart.Duration }; + raiseRoomUpdated(); + break; + + case StopCountdownRequest _: + multiplayerRoom.Countdown = null; + raiseRoomUpdated(); + break; + } + }); + Children = new Drawable[] { ongoingOperationTracker, @@ -151,6 +168,13 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); + AddStep("check request received", () => + { + multiplayerClient.Verify(m => m.SendMatchRequest(It.Is(req => + req.Duration == TimeSpan.FromSeconds(10) + )), Times.Once); + }); + ClickButtonWhenEnabled(); AddStep("click the cancel button", () => { @@ -159,8 +183,10 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - // AddStep("finish countdown", skipToEndOfCountdown); - AddUntilStep("match not started", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready); + AddStep("check request received", () => + { + multiplayerClient.Verify(m => m.SendMatchRequest(It.IsAny()), Times.Once); + }); } [Test] From 0a5a3415f8e5a54374f22f841a10850aea1a1430 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 20:28:53 +0900 Subject: [PATCH 030/134] Setup host switching and avoid starting gameplay --- .../Multiplayer/TestSceneMatchStartControl.cs | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index dabf8c5267..dccfd98c4d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Threading.Tasks; using Moq; using NUnit.Framework; using osu.Framework.Allocation; @@ -11,6 +12,7 @@ using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Logging; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; @@ -62,18 +64,22 @@ namespace osu.Game.Tests.Visual.Multiplayer multiplayerClient.Setup(m => m.ChangeState(It.IsAny())) .Callback((MultiplayerUserState r) => { + Logger.Log($"Changing local user state from {localUser.State} to {r}"); localUser.State = r; raiseRoomUpdated(); }); + // We don't want to have to handle entering into actual gameplay in this test. + // Returning a failed task should always allow the button to always stay in a clickable state. + multiplayerClient.Setup(m => m.StartMatch()).Returns(Task.FromException(new InvalidOperationException())); + multiplayerClient.Setup(m => m.SendMatchRequest(It.IsAny())) .Callback((MatchUserRequest request) => { switch (request) { case StartMatchCountdownRequest countdownStart: - multiplayerRoom.Countdown = new MatchStartCountdown { TimeRemaining = countdownStart.Duration }; - raiseRoomUpdated(); + setRoomCountdown(countdownStart.Duration); break; case StopCountdownRequest _: @@ -110,16 +116,16 @@ namespace osu.Game.Tests.Visual.Multiplayer CurrentPlaylistItem = { Value = playlistItem } }; + localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id) { User = API.LocalUser.Value }; + multiplayerRoom = new MultiplayerRoom(0) { Playlist = { new MultiplayerPlaylistItem(playlistItem), }, - Users = - { - (localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id) { User = API.LocalUser.Value }), - } + Users = { localUser }, + Host = localUser, }; }); @@ -160,6 +166,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { ClickButtonWhenEnabled(); AddUntilStep("countdown button shown", () => this.ChildrenOfType().SingleOrDefault()?.IsPresent == true); + ClickButtonWhenEnabled(); AddStep("click the first countdown button", () => { @@ -192,19 +199,15 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestReadyAndUnReadyDuringCountdown() { - AddStep("add second user as host", () => - { - addUser(new APIUser { Id = 2, Username = "Another user" }); - multiplayerClient.Object.TransferHost(2); - }); + AddStep("add second user as host", () => addUser(new APIUser { Id = 2, Username = "Another user" }, true)); - AddStep("start with countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(2) }).WaitSafely()); + AddStep("start countdown", () => setRoomCountdown(TimeSpan.FromMinutes(1))); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => localUser.State == MultiplayerUserState.Idle); } [Test] @@ -398,15 +401,27 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } + private void setRoomCountdown(TimeSpan duration) + { + multiplayerRoom.Countdown = new MatchStartCountdown { TimeRemaining = duration }; + raiseRoomUpdated(); + } + private void changeUserState(int userId, MultiplayerUserState newState) { multiplayerRoom.Users.Single(u => u.UserID == userId).State = newState; raiseRoomUpdated(); } - private void addUser(APIUser user) + private void addUser(APIUser user, bool asHost = false) { - multiplayerRoom.Users.Add(new MultiplayerRoomUser(user.Id) { User = user }); + var multiplayerRoomUser = new MultiplayerRoomUser(user.Id) { User = user }; + + multiplayerRoom.Users.Add(multiplayerRoomUser); + + if (asHost) + multiplayerRoom.Host = multiplayerRoomUser; + raiseRoomUpdated(); } From 331242d585ab23d8f11d8863ac991bb74600d038 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 20:38:39 +0900 Subject: [PATCH 031/134] Update transfer host logic and simplify local user checks --- .../Multiplayer/TestSceneMatchStartControl.cs | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index dccfd98c4d..e10a63d010 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -231,14 +231,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add second user as host", () => { - addUser(new APIUser { Id = 2, Username = "Another user" }); - multiplayerClient.Object.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }, true); }); AddStep("start countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(1) }).WaitSafely()); AddUntilStep("countdown started", () => multiplayerClient.Object.Room?.Countdown != null); - AddStep("transfer host to local user", () => multiplayerClient.Object.TransferHost(API.LocalUser.Value.OnlineID)); + AddStep("transfer host to local user", () => transferHost(localUser)); AddUntilStep("local user is host", () => multiplayerClient.Object.Room?.Host?.Equals(multiplayerClient.Object.LocalUser) == true); ClickButtonWhenEnabled(); @@ -293,15 +292,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add second user as host", () => { - addUser(new APIUser { Id = 2, Username = "Another user" }); - multiplayerClient.Object.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }, true); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => localUser.State == MultiplayerUserState.Idle); } [TestCase(true)] @@ -310,14 +308,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("setup", () => { - multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); + transferHost(multiplayerRoom.Users[0]); if (!allReady) addUser(new APIUser { Id = 2, Username = "Another user" }); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); verifyGameplayStartFlow(); } @@ -327,12 +325,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add host", () => { - addUser(new APIUser { Id = 2, Username = "Another user" }); - multiplayerClient.Object.TransferHost(2); + addUser(new APIUser { Id = 2, Username = "Another user" }, true); }); ClickButtonWhenEnabled(); - AddStep("make user host", () => multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0)); + + AddStep("make user host", () => transferHost(localUser)); verifyGameplayStartFlow(); } @@ -342,17 +340,16 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("setup", () => { - multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); addUser(new APIUser { Id = 2, Username = "Another user" }); }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); - AddStep("transfer host", () => multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[1].UserID ?? 0)); + AddStep("transfer host", () => transferHost(multiplayerRoom.Users[1])); ClickButtonWhenEnabled(); - AddUntilStep("user is idle (match not started)", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle (match not started)", () => localUser.State == MultiplayerUserState.Idle); AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } @@ -363,14 +360,11 @@ namespace osu.Game.Tests.Visual.Multiplayer const int users = 10; AddStep("setup", () => { - multiplayerClient.Object.TransferHost(multiplayerClient.Object.Room?.Users[0].UserID ?? 0); + transferHost(localUser); for (int i = 0; i < users; i++) - addUser(new APIUser { Id = i, Username = "Another user" }); + addUser(new APIUser { Id = i, Username = "Another user" }, !isHost && i == 2); }); - if (!isHost) - AddStep("transfer host", () => multiplayerClient.Object.TransferHost(2)); - ClickButtonWhenEnabled(); AddRepeatStep("change user ready state", () => @@ -388,14 +382,14 @@ namespace osu.Game.Tests.Visual.Multiplayer private void verifyGameplayStartFlow() { - AddUntilStep("user is ready", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user waiting for load", () => multiplayerClient.Object.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + AddUntilStep("user waiting for load", () => localUser.State == MultiplayerUserState.WaitingForLoad); AddStep("finish gameplay", () => { - changeUserState(multiplayerClient.Object.Room?.Users[0].UserID ?? 0, MultiplayerUserState.Loaded); - changeUserState(multiplayerClient.Object.Room?.Users[0].UserID ?? 0, MultiplayerUserState.FinishedPlay); + changeUserState(localUser.UserID, MultiplayerUserState.Loaded); + changeUserState(localUser.UserID, MultiplayerUserState.FinishedPlay); }); AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); @@ -420,11 +414,17 @@ namespace osu.Game.Tests.Visual.Multiplayer multiplayerRoom.Users.Add(multiplayerRoomUser); if (asHost) - multiplayerRoom.Host = multiplayerRoomUser; + transferHost(multiplayerRoomUser); raiseRoomUpdated(); } + private void transferHost(MultiplayerRoomUser user) + { + multiplayerRoom.Host = user; + raiseRoomUpdated(); + } + private void raiseRoomUpdated() => multiplayerClient.Raise(m => m.RoomUpdated -= null); } } From 4b6a9fbf474031d3df852a099cce731a95c7fa91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 20:41:56 +0900 Subject: [PATCH 032/134] Reimplement beatmap availability test --- .../Visual/Multiplayer/TestSceneMatchStartControl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index e10a63d010..16f7426360 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -281,9 +281,9 @@ namespace osu.Game.Tests.Visual.Multiplayer return readyButton.Enabled.Value; }); - // TODO: AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); + AddStep("mark beatmap not available", () => beatmapAvailability.Value = BeatmapAvailability.NotDownloaded()); AddUntilStep("ready button disabled", () => !readyButton.Enabled.Value); - // TODO: AddStep("undelete beatmap", () => beatmaps.Undelete(importedSet)); + AddStep("mark beatmap available", () => beatmapAvailability.Value = BeatmapAvailability.LocallyAvailable()); AddUntilStep("ready button enabled back", () => readyButton.Enabled.Value); } From 8a7b37856c8902681a35f5f9b786af5f79fc23bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 20:51:05 +0900 Subject: [PATCH 033/134] Implement `IsHost` to return correct value for current state --- osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 16f7426360..bf980b0694 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Multiplayer multiplayerClient.SetupGet(m => m.Room).Returns(() => multiplayerRoom); // By default, the local user is to be the host. - multiplayerClient.SetupGet(m => m.IsHost).Returns(true); + multiplayerClient.SetupGet(m => m.IsHost).Returns(() => ReferenceEquals(multiplayerRoom.Host, localUser)); // Assume all state changes are accepted by the server. multiplayerClient.Setup(m => m.ChangeState(It.IsAny())) From c0ad91796d75e2f38b5117c0b484589cba1c588b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 20:57:40 +0900 Subject: [PATCH 034/134] Fix gameplay start flow --- .../Multiplayer/TestSceneMatchStartControl.cs | 16 +++++++--------- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index bf980b0694..8af1187b67 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using System.Threading.Tasks; using Moq; using NUnit.Framework; using osu.Framework.Allocation; @@ -69,9 +68,8 @@ namespace osu.Game.Tests.Visual.Multiplayer raiseRoomUpdated(); }); - // We don't want to have to handle entering into actual gameplay in this test. - // Returning a failed task should always allow the button to always stay in a clickable state. - multiplayerClient.Setup(m => m.StartMatch()).Returns(Task.FromException(new InvalidOperationException())); + multiplayerClient.Setup(m => m.StartMatch()) + .Callback(() => multiplayerClient.Raise(m => m.LoadRequested -= null)); multiplayerClient.Setup(m => m.SendMatchRequest(It.IsAny())) .Callback((MatchUserRequest request) => @@ -384,13 +382,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); ClickButtonWhenEnabled(); + + AddStep("check start request received", () => multiplayerClient.Verify(m => m.StartMatch(), Times.Once)); AddUntilStep("user waiting for load", () => localUser.State == MultiplayerUserState.WaitingForLoad); - AddStep("finish gameplay", () => - { - changeUserState(localUser.UserID, MultiplayerUserState.Loaded); - changeUserState(localUser.UserID, MultiplayerUserState.FinishedPlay); - }); + AddUntilStep("ready button disabled", () => !control.ChildrenOfType().Single().Enabled.Value); + + AddStep("finish gameplay", () => changeUserState(localUser.UserID, MultiplayerUserState.Idle)); AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index e29aee2c6d..967220abbf 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -67,7 +67,7 @@ namespace osu.Game.Online.Multiplayer /// /// Invoked when the multiplayer server requests the current beatmap to be loaded into play. /// - public event Action? LoadRequested; + public virtual event Action? LoadRequested; /// /// Invoked when the multiplayer server requests gameplay to be started. From 8b1fd051c65c40febb3193a7cb25910135768f9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 21:04:15 +0900 Subject: [PATCH 035/134] Fix remaining autostart and gameplay tests --- .../Multiplayer/TestSceneMatchStartControl.cs | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 8af1187b67..acee35d3ba 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -69,7 +69,13 @@ namespace osu.Game.Tests.Visual.Multiplayer }); multiplayerClient.Setup(m => m.StartMatch()) - .Callback(() => multiplayerClient.Raise(m => m.LoadRequested -= null)); + .Callback(() => + { + multiplayerClient.Raise(m => m.LoadRequested -= null); + + // immediately "end" gameplay, as we don't care about that part of the process. + changeUserState(localUser.UserID, MultiplayerUserState.Idle); + }); multiplayerClient.Setup(m => m.SendMatchRequest(It.IsAny())) .Callback((MatchUserRequest request) => @@ -244,13 +250,13 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestCountdownButtonVisibilityWithAutoStartEnablement() + public void TestCountdownButtonVisibilityWithAutoStart() { ClickButtonWhenEnabled(); AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); AddUntilStep("countdown button visible", () => this.ChildrenOfType().Single().IsPresent); - AddStep("enable auto start", () => multiplayerClient.Object.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); + AddStep("enable auto start", () => changeRoomSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) })); ClickButtonWhenEnabled(); AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); @@ -260,7 +266,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestClickingReadyButtonUnReadiesDuringAutoStart() { - AddStep("enable auto start", () => multiplayerClient.Object.ChangeSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) }).WaitSafely()); + AddStep("enable auto start", () => changeRoomSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) })); ClickButtonWhenEnabled(); AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); @@ -384,13 +390,6 @@ namespace osu.Game.Tests.Visual.Multiplayer ClickButtonWhenEnabled(); AddStep("check start request received", () => multiplayerClient.Verify(m => m.StartMatch(), Times.Once)); - AddUntilStep("user waiting for load", () => localUser.State == MultiplayerUserState.WaitingForLoad); - - AddUntilStep("ready button disabled", () => !control.ChildrenOfType().Single().Enabled.Value); - - AddStep("finish gameplay", () => changeUserState(localUser.UserID, MultiplayerUserState.Idle)); - - AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); } private void setRoomCountdown(TimeSpan duration) @@ -423,6 +422,20 @@ namespace osu.Game.Tests.Visual.Multiplayer raiseRoomUpdated(); } + private void changeRoomSettings(MultiplayerRoomSettings settings) + { + multiplayerRoom.Settings = settings; + + // Changing settings should reset all user ready statuses. + foreach (var user in multiplayerRoom.Users) + { + if (user.State == MultiplayerUserState.Ready) + user.State = MultiplayerUserState.Idle; + } + + raiseRoomUpdated(); + } + private void raiseRoomUpdated() => multiplayerClient.Raise(m => m.RoomUpdated -= null); } } From c77a7b75d326c90b5444bc26302b4ce7d38e38a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 21:11:45 +0900 Subject: [PATCH 036/134] Tidy things up --- .../Multiplayer/TestSceneMatchStartControl.cs | 70 +++++++++---------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index acee35d3ba..ff6c02c4e5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -41,6 +41,8 @@ namespace osu.Game.Tests.Visual.Multiplayer private PopoverContainer content; private MatchStartControl control; + private OsuButton readyButton => control.ChildrenOfType().Single(); + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) { Model = { BindTarget = room } }; @@ -49,9 +51,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { Dependencies.CacheAs(multiplayerClient.Object); Dependencies.CacheAs(ongoingOperationTracker = new OngoingOperationTracker()); + Dependencies.CacheAs(availabilityTracker.Object); availabilityTracker.SetupGet(a => a.Availability).Returns(beatmapAvailability); - Dependencies.CacheAs(availabilityTracker.Object); multiplayerClient.SetupGet(m => m.LocalUser).Returns(() => localUser); multiplayerClient.SetupGet(m => m.Room).Returns(() => multiplayerRoom); @@ -149,6 +151,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { ClickButtonWhenEnabled(); AddUntilStep("countdown button shown", () => this.ChildrenOfType().SingleOrDefault()?.IsPresent == true); + ClickButtonWhenEnabled(); AddStep("click the first countdown button", () => { @@ -208,17 +211,17 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("start countdown", () => setRoomCountdown(TimeSpan.FromMinutes(1))); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => localUser.State == MultiplayerUserState.Idle); + checkLocalUserState(MultiplayerUserState.Idle); } [Test] public void TestCountdownWhileSpectating() { AddStep("set spectating", () => changeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating)); - AddUntilStep("local user is spectating", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Spectating); + checkLocalUserState(MultiplayerUserState.Spectating); AddAssert("countdown button is visible", () => this.ChildrenOfType().Single().IsPresent); AddAssert("countdown button enabled", () => this.ChildrenOfType().Single().Enabled.Value); @@ -239,27 +242,27 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddStep("start countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(1) }).WaitSafely()); - AddUntilStep("countdown started", () => multiplayerClient.Object.Room?.Countdown != null); + AddUntilStep("countdown started", () => multiplayerRoom.Countdown != null); AddStep("transfer host to local user", () => transferHost(localUser)); - AddUntilStep("local user is host", () => multiplayerClient.Object.Room?.Host?.Equals(multiplayerClient.Object.LocalUser) == true); + AddUntilStep("local user is host", () => multiplayerRoom.Host?.Equals(multiplayerClient.Object.LocalUser) == true); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); - AddAssert("countdown still active", () => multiplayerClient.Object.Room?.Countdown != null); + checkLocalUserState(MultiplayerUserState.Ready); + AddAssert("countdown still active", () => multiplayerRoom.Countdown != null); } [Test] public void TestCountdownButtonVisibilityWithAutoStart() { ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); AddUntilStep("countdown button visible", () => this.ChildrenOfType().Single().IsPresent); AddStep("enable auto start", () => changeRoomSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) })); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); AddUntilStep("countdown button not visible", () => !this.ChildrenOfType().Single().IsPresent); } @@ -268,25 +271,20 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("enable auto start", () => changeRoomSettings(new MultiplayerRoomSettings { AutoStartDuration = TimeSpan.FromMinutes(1) })); ClickButtonWhenEnabled(); - AddUntilStep("local user became ready", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("local user became idle", () => multiplayerClient.Object.LocalUser?.State == MultiplayerUserState.Idle); + checkLocalUserState(MultiplayerUserState.Idle); } [Test] public void TestDeletedBeatmapDisableReady() { - OsuButton readyButton = null; - - AddUntilStep("ensure ready button enabled", () => - { - readyButton = control.ChildrenOfType().Single(); - return readyButton.Enabled.Value; - }); + AddUntilStep("ready button enabled", () => readyButton.Enabled.Value); AddStep("mark beatmap not available", () => beatmapAvailability.Value = BeatmapAvailability.NotDownloaded()); AddUntilStep("ready button disabled", () => !readyButton.Enabled.Value); + AddStep("mark beatmap available", () => beatmapAvailability.Value = BeatmapAvailability.LocallyAvailable()); AddUntilStep("ready button enabled back", () => readyButton.Enabled.Value); } @@ -300,26 +298,21 @@ namespace osu.Game.Tests.Visual.Multiplayer }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => localUser.State == MultiplayerUserState.Idle); + checkLocalUserState(MultiplayerUserState.Idle); } [TestCase(true)] [TestCase(false)] public void TestToggleStateWhenHost(bool allReady) { - AddStep("setup", () => - { - transferHost(multiplayerRoom.Users[0]); - - if (!allReady) - addUser(new APIUser { Id = 2, Username = "Another user" }); - }); + if (!allReady) + AddStep("add other user", () => addUser(new APIUser { Id = 2, Username = "Another user" })); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); verifyGameplayStartFlow(); } @@ -334,7 +327,7 @@ namespace osu.Game.Tests.Visual.Multiplayer ClickButtonWhenEnabled(); - AddStep("make user host", () => transferHost(localUser)); + AddStep("make local user host", () => transferHost(localUser)); verifyGameplayStartFlow(); } @@ -348,13 +341,13 @@ namespace osu.Game.Tests.Visual.Multiplayer }); ClickButtonWhenEnabled(); - AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); AddStep("transfer host", () => transferHost(multiplayerRoom.Users[1])); ClickButtonWhenEnabled(); - AddUntilStep("user is idle (match not started)", () => localUser.State == MultiplayerUserState.Idle); - AddUntilStep("ready button enabled", () => control.ChildrenOfType().Single().Enabled.Value); + checkLocalUserState(MultiplayerUserState.Idle); + AddUntilStep("ready button enabled", () => readyButton.Enabled.Value); } [TestCase(true)] @@ -362,9 +355,9 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestManyUsersChangingState(bool isHost) { const int users = 10; - AddStep("setup", () => + + AddStep("add many users", () => { - transferHost(localUser); for (int i = 0; i < users; i++) addUser(new APIUser { Id = i, Username = "Another user" }, !isHost && i == 2); }); @@ -378,7 +371,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddRepeatStep("ready all users", () => { - var nextUnready = multiplayerClient.Object.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + var nextUnready = multiplayerRoom.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); if (nextUnready != null) changeUserState(nextUnready.UserID, MultiplayerUserState.Ready); }, users); @@ -386,12 +379,15 @@ namespace osu.Game.Tests.Visual.Multiplayer private void verifyGameplayStartFlow() { - AddUntilStep("user is ready", () => localUser.State == MultiplayerUserState.Ready); + checkLocalUserState(MultiplayerUserState.Ready); ClickButtonWhenEnabled(); AddStep("check start request received", () => multiplayerClient.Verify(m => m.StartMatch(), Times.Once)); } + private void checkLocalUserState(MultiplayerUserState state) => + AddUntilStep($"local user is {state}", () => localUser.State == state); + private void setRoomCountdown(TimeSpan duration) { multiplayerRoom.Countdown = new MatchStartCountdown { TimeRemaining = duration }; From 9b433280bdd9623b0a296b9a78ceccdec2a4215d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Apr 2022 21:24:33 +0900 Subject: [PATCH 037/134] Remove countdown implementation from `TestMultiplayerClient` --- .../Multiplayer/TestMultiplayerClient.cs | 89 +------------------ 1 file changed, 1 insertion(+), 88 deletions(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 4a974cf61d..21774b73a0 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -7,19 +7,16 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Development; using osu.Framework.Extensions; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; -using osu.Game.Online.Multiplayer.Countdown; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Mods; -using APIUser = osu.Game.Online.API.Requests.Responses.APIUser; namespace osu.Game.Tests.Visual.Multiplayer { @@ -141,16 +138,6 @@ namespace osu.Game.Tests.Visual.Multiplayer switch (Room.State) { case MultiplayerRoomState.Open: - // If there are no remaining ready users or the host is not ready, stop any existing countdown. - // Todo: This doesn't yet support non-match-start countdowns. - if (Room.Settings.AutoStartEnabled) - { - bool shouldHaveCountdown = !APIRoom.Playlist.GetCurrentItem()!.Expired && Room.Users.Any(u => u.State == MultiplayerUserState.Ready); - - if (shouldHaveCountdown && Room.Countdown == null) - startCountdown(new MatchStartCountdown { TimeRemaining = Room.Settings.AutoStartDuration }, StartMatch); - } - break; case MultiplayerRoomState.WaitingForLoad: @@ -317,16 +304,6 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } - private CancellationTokenSource? countdownSkipSource; - private CancellationTokenSource? countdownStopSource; - private Task countdownTask = Task.CompletedTask; - - /// - /// Skips to the end of the currently-running countdown, if one is running, - /// and runs the callback (e.g. to start the match) as soon as possible unless the countdown has been cancelled. - /// - public void SkipToEndOfCountdown() => countdownSkipSource?.Cancel(); - public override async Task SendMatchRequest(MatchUserRequest request) { Debug.Assert(Room != null); @@ -334,14 +311,6 @@ namespace osu.Game.Tests.Visual.Multiplayer switch (request) { - case StartMatchCountdownRequest matchCountdownRequest: - startCountdown(new MatchStartCountdown { TimeRemaining = matchCountdownRequest.Duration }, StartMatch); - break; - - case StopCountdownRequest _: - stopCountdown(); - break; - case ChangeTeamRequest changeTeam: TeamVersusRoomState roomState = (TeamVersusRoomState)Room.MatchState!; @@ -360,62 +329,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } } - private void startCountdown(MultiplayerCountdown countdown, Func continuation) - { - Debug.Assert(Room != null); - Debug.Assert(ThreadSafety.IsUpdateThread); - - stopCountdown(); - - // Note that this will leak CTSs, however this is a test method and we haven't noticed foregoing disposal of non-linked CTSs to be detrimental. - // If necessary, this can be moved into the final schedule below, and the class-level fields be nulled out accordingly. - var stopSource = countdownStopSource = new CancellationTokenSource(); - var skipSource = countdownSkipSource = new CancellationTokenSource(); - - Task lastCountdownTask = countdownTask; - countdownTask = start(); - - async Task start() - { - await lastCountdownTask; - - Schedule(() => - { - if (stopSource.IsCancellationRequested) - return; - - Room.Countdown = countdown; - MatchEvent(new CountdownChangedEvent { Countdown = countdown }); - }); - - try - { - using (var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(stopSource.Token, skipSource.Token)) - await Task.Delay(countdown.TimeRemaining, cancellationSource.Token).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // Clients need to be notified of cancellations in the following code. - } - - Schedule(() => - { - if (Room.Countdown != countdown) - return; - - Room.Countdown = null; - MatchEvent(new CountdownChangedEvent { Countdown = null }); - - if (stopSource.IsCancellationRequested) - return; - - continuation().WaitSafely(); - }); - } - } - - private void stopCountdown() => countdownStopSource?.Cancel(); - public override Task StartMatch() { Debug.Assert(Room != null); From f48a9ba90a611424d57e55832316d0177e645dac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 16:19:32 +0900 Subject: [PATCH 038/134] Add test coverage of nested screen stacks not handling dialog dismissal properly --- .../Navigation/TestScenePerformFromScreen.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 1ebceed15d..5c5d6679aa 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -10,6 +10,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Screens; +using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; @@ -171,6 +172,46 @@ namespace osu.Game.Tests.Visual.Navigation } } + [TestCase(true)] + [TestCase(false)] + public void TestPerformBlockedByDialogSubScreen(bool confirm) + { + TestScreenWithNestedStack screenWithNestedStack = null; + + PushAndConfirm(() => screenWithNestedStack = new TestScreenWithNestedStack()); + + AddAssert("wait for nested screen", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); + + AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true)); + + AddUntilStep("wait for dialog", () => screenWithNestedStack.Blocker.ExitAttempts == 1); + + AddWaitStep("wait a bit", 10); + + AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + + AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); + AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); + + AddAssert("did not perform", () => !actionPerformed); + + AddAssert("only one exit attempt", () => screenWithNestedStack.Blocker.ExitAttempts == 1); + + if (confirm) + { + AddStep("accept dialog", () => InputManager.Key(Key.Number1)); + AddAssert("nested screen changed", () => screenWithNestedStack.SubScreenStack.CurrentScreen != screenWithNestedStack.Blocker); + AddUntilStep("did perform", () => actionPerformed); + } + else + { + AddStep("cancel dialog", () => InputManager.Key(Key.Number2)); + AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); + AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); + AddAssert("did not perform", () => !actionPerformed); + } + } + private void importAndWaitForSongSelect() { AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); @@ -200,5 +241,30 @@ namespace osu.Game.Tests.Visual.Navigation return base.OnExiting(next); } } + + public class TestScreenWithNestedStack : OsuScreen, IHasSubScreenStack + { + public DialogBlockingScreen Blocker { get; private set; } + + public ScreenStack SubScreenStack { get; } = new ScreenStack(); + + public TestScreenWithNestedStack() + { + AddInternal(SubScreenStack); + + SubScreenStack.Push(Blocker = new DialogBlockingScreen()); + } + + public override bool OnExiting(IScreen next) + { + if (SubScreenStack.CurrentScreen != null) + { + SubScreenStack.CurrentScreen.Exit(); + return true; + } + + return base.OnExiting(next); + } + } } } From f9d9e6aa61ee45c7d605441ea77a6892a1f07ce8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 15:58:03 +0900 Subject: [PATCH 039/134] Ensure popup dialogs are hidden before running any associated actions --- osu.Game/Overlays/Dialog/PopupDialog.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index a70a7f26cc..f5e6aed41b 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -87,10 +87,13 @@ namespace osu.Game.Overlays.Dialog { if (actionInvoked) return; + // Hide the dialog before running the action. + // This is important as the code which is performed may check for a dialog being present (ie. `OsuGame.PerformFromScreen`) + // and we don't want it to see the already dismissed dialog. + Hide(); + actionInvoked = true; action?.Invoke(); - - Hide(); }; } } From 2bc4bb9e206dd4c7ec337ddd44c8690ffe8ad787 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 16:03:34 +0900 Subject: [PATCH 040/134] Consider `SubScreenStack`s when allowing for exit confirmation sequence in `PerformFromMenuRunner` --- osu.Game/PerformFromMenuRunner.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 6f979b8dc8..7f0db96e6d 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -125,6 +125,18 @@ namespace osu.Game /// Whether a dialog blocked interaction. private bool checkForDialog(IScreen current) { + // An exit process may traverse multiple levels. + // When checking for dismissing dialogs, let's also consider sub screens. + while (current is IHasSubScreenStack currentWithSubScreenStack) + { + var nestedCurrent = currentWithSubScreenStack.SubScreenStack.CurrentScreen; + + if (nestedCurrent == null) + break; + + current = nestedCurrent; + } + var currentDialog = dialogOverlay.CurrentDialog; if (lastEncounteredDialog != null) From 8b901fe60b2b4de540008ac3efab127213e02bdb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 16:22:40 +0900 Subject: [PATCH 041/134] Fix potential null reference when running recursive `findValidTarget` --- osu.Game/PerformFromMenuRunner.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 7f0db96e6d..528470768c 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -97,11 +97,14 @@ namespace osu.Game // if this has a sub stack, recursively check the screens within it. if (current is IHasSubScreenStack currentSubScreen) { - if (findValidTarget(currentSubScreen.SubScreenStack.CurrentScreen)) + var nestedCurrent = currentSubScreen.SubScreenStack.CurrentScreen; + + if (nestedCurrent != null) { // should be correct in theory, but currently untested/unused in existing implementations. - current.MakeCurrent(); - return true; + // note that calling findValidTarget actually performs the final operation. + if (findValidTarget(nestedCurrent)) + return true; } } From 58d5cf4560d35f5100681d1dd069e8424f31d16d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 16:37:59 +0900 Subject: [PATCH 042/134] Remove unused using statement --- osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 5c5d6679aa..f045e3a3e8 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -10,7 +10,6 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Screens; -using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; From 1fdf742d3888fca4cf89e26f441ab4ef0b8e3a7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 17:51:50 +0900 Subject: [PATCH 043/134] Fix potentially incorrect button being pressed during `PopupDialog` hide due to flag set too late --- osu.Game/Overlays/Dialog/PopupDialog.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index f5e6aed41b..6159ab59f0 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -87,12 +87,13 @@ namespace osu.Game.Overlays.Dialog { if (actionInvoked) return; + actionInvoked = true; + // Hide the dialog before running the action. // This is important as the code which is performed may check for a dialog being present (ie. `OsuGame.PerformFromScreen`) // and we don't want it to see the already dismissed dialog. Hide(); - actionInvoked = true; action?.Invoke(); }; } From c8db7cf2a89dec23eec10b0ec217ef28802dd47a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 18:09:23 +0900 Subject: [PATCH 044/134] Reset audio offset before each test run in `TestSceneMasterGameplayClockContainer` --- .../Gameplay/TestSceneMasterGameplayClockContainer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs index 75be7b0362..5c04ac88a7 100644 --- a/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneMasterGameplayClockContainer.cs @@ -26,6 +26,12 @@ namespace osu.Game.Tests.Gameplay Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage)); } + [SetUpSteps] + public void SetUpSteps() + { + AddStep("reset audio offset", () => localConfig.SetValue(OsuSetting.AudioOffset, 0.0)); + } + [Test] public void TestStartThenElapsedTime() { From 2abdbe53e7e90a7dfb86541ee475a93fe4ec6665 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 14 Apr 2022 18:55:12 +0900 Subject: [PATCH 045/134] Cleanup whitespace --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index c5a5c14070..ea43fb1546 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -251,7 +251,6 @@ namespace osu.Game.Screens.Play } private class HardwareCorrectionOffsetClock : FramedOffsetClock - { private readonly BindableDouble pauseRateAdjust; @@ -297,7 +296,6 @@ namespace osu.Game.Screens.Play } private class MasterGameplayClock : GameplayClock - { public readonly List> MutableNonGameplayAdjustments = new List>(); public override IEnumerable> NonGameplayAdjustments => MutableNonGameplayAdjustments; From 566a20a623a5d82f73d4e11f6c2ea9836ce14566 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 14 Apr 2022 13:27:06 +0300 Subject: [PATCH 046/134] Add keyword "delay" to hold-to-confirm activation time setting --- .../Overlays/Settings/Sections/UserInterface/GeneralSettings.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 59894cbcae..6e1558f7d7 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -39,6 +39,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { LabelText = UserInterfaceStrings.HoldToConfirmActivationTime, Current = config.GetBindable(OsuSetting.UIHoldActivationDelay), + Keywords = new[] { @"delay" }, KeyboardStep = 50 }, }; From 59bb6b8f7cbbde0078a51c88e51245bdc9ea88c3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 14 Apr 2022 13:43:09 +0300 Subject: [PATCH 047/134] Add background dim effect to inactive settings sections --- osu.Game/Overlays/Settings/SettingsSection.cs | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 2539c32806..50a82b3d96 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -13,6 +13,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.Settings { @@ -23,6 +24,8 @@ namespace osu.Game.Overlays.Settings private IBindable selectedSection; + private Box dim; + private OsuSpriteText header; public abstract Drawable CreateIcon(); @@ -78,25 +81,40 @@ namespace osu.Game.Overlays.Settings }, new Container { - Padding = new MarginPadding - { - Top = 28, - Bottom = 40, - }, + Padding = new MarginPadding { Top = border_size }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Children = new Drawable[] { - header = new OsuSpriteText + dim = new Box { - Font = OsuFont.TorusAlternate.With(size: header_size), - Text = Header, - Margin = new MarginPadding - { - Horizontal = SettingsPanel.CONTENT_MARGINS - } + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0f, }, - FlowContent + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Top = 24, + Bottom = 40, + }, + Children = new Drawable[] + { + header = new OsuSpriteText + { + Font = OsuFont.TorusAlternate.With(size: header_size), + Text = Header, + Margin = new MarginPadding + { + Horizontal = SettingsPanel.CONTENT_MARGINS + } + }, + FlowContent + } + } } }, }); @@ -135,15 +153,16 @@ namespace osu.Game.Overlays.Settings private void updateContentFade() { float contentFade = 1; - float headerFade = 1; + float dimFade = 0; if (!isCurrentSection) { - contentFade = 0.25f; - headerFade = IsHovered ? 0.5f : 0.25f; + contentFade = IsHovered ? 0.5f : 0.25f; + dimFade = IsHovered ? 0.125f : 0.25f; } - header.FadeTo(headerFade, 500, Easing.OutQuint); + dim.FadeTo(dimFade, 500, Easing.OutQuint); + header.FadeTo(contentFade, 500, Easing.OutQuint); FlowContent.FadeTo(contentFade, 500, Easing.OutQuint); } } From f19208a2451d26a7973f2361b3d7e4250964ffed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 21:04:52 +0900 Subject: [PATCH 048/134] Move dim layer to top and only apply dimming at one location --- osu.Game/Overlays/Settings/SettingsSection.cs | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 50a82b3d96..49f0d5c153 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -13,7 +13,6 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.Settings { @@ -26,8 +25,6 @@ namespace osu.Game.Overlays.Settings private Box dim; - private OsuSpriteText header; - public abstract Drawable CreateIcon(); public abstract LocalisableString Header { get; } @@ -86,12 +83,6 @@ namespace osu.Game.Overlays.Settings AutoSizeAxes = Axes.Y, Children = new Drawable[] { - dim = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0f, - }, new Container { RelativeSizeAxes = Axes.X, @@ -103,7 +94,7 @@ namespace osu.Game.Overlays.Settings }, Children = new Drawable[] { - header = new OsuSpriteText + new OsuSpriteText { Font = OsuFont.TorusAlternate.With(size: header_size), Text = Header, @@ -114,7 +105,13 @@ namespace osu.Game.Overlays.Settings }, FlowContent } - } + }, + dim = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background5, + Alpha = 0f, + }, } }, }); @@ -152,18 +149,12 @@ namespace osu.Game.Overlays.Settings private void updateContentFade() { - float contentFade = 1; float dimFade = 0; if (!isCurrentSection) - { - contentFade = IsHovered ? 0.5f : 0.25f; - dimFade = IsHovered ? 0.125f : 0.25f; - } + dimFade = IsHovered ? 0.5f : 0.8f; - dim.FadeTo(dimFade, 500, Easing.OutQuint); - header.FadeTo(contentFade, 500, Easing.OutQuint); - FlowContent.FadeTo(contentFade, 500, Easing.OutQuint); + dim.FadeTo(dimFade, 300, Easing.OutQuint); } } } From 9240148c8e817869fcddc59f093f2eeb916c13dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 21:16:54 +0900 Subject: [PATCH 049/134] Avoid initial fade of dim layer --- osu.Game/Overlays/Settings/SettingsSection.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 49f0d5c153..6645d20dbe 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -25,6 +25,8 @@ namespace osu.Game.Overlays.Settings private Box dim; + private const float inactive_alpha = 0.8f; + public abstract Drawable CreateIcon(); public abstract LocalisableString Header { get; } @@ -110,7 +112,7 @@ namespace osu.Game.Overlays.Settings { RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background5, - Alpha = 0f, + Alpha = inactive_alpha, }, } }, @@ -152,7 +154,9 @@ namespace osu.Game.Overlays.Settings float dimFade = 0; if (!isCurrentSection) - dimFade = IsHovered ? 0.5f : 0.8f; + { + dimFade = IsHovered ? 0.5f : inactive_alpha; + } dim.FadeTo(dimFade, 300, Easing.OutQuint); } From b0889590458b16f1c43acb3ec5966af00e72a196 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Apr 2022 21:17:06 +0900 Subject: [PATCH 050/134] Reduce settings fade in duration to higher alpha/colour cross-talk --- osu.Game/Overlays/SettingsPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index b11b6fde27..d5b36713f3 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -197,7 +197,7 @@ namespace osu.Game.Overlays ContentContainer.Margin = new MarginPadding { Left = Sidebar?.DrawWidth ?? 0 }; } - private const double fade_in_duration = 1000; + private const double fade_in_duration = 500; private void loadSections() { From a7b04a8c19848c29443378b9c993c81842909c2f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Apr 2022 19:59:38 +0900 Subject: [PATCH 051/134] 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 331c4db01f..77deec031b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 308ec7e7d6..cf2245aa3a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 311c9ba345..b77dff9f41 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 8e6c0158568462e42d50a23307400de8f2c7cb76 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 15 Apr 2022 20:31:59 +0900 Subject: [PATCH 052/134] Use a fallback sample when a MainMenuButton doesn't provide a click sample --- osu.Game/Screens/Menu/MainMenuButton.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index 88bea43b23..c07ada9419 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -185,8 +185,7 @@ namespace osu.Game.Screens.Menu private void load(AudioManager audio) { sampleHover = audio.Samples.Get(@"Menu/button-hover"); - if (!string.IsNullOrEmpty(sampleName)) - sampleClick = audio.Samples.Get($@"Menu/{sampleName}"); + sampleClick = audio.Samples.Get(!string.IsNullOrEmpty(sampleName) ? $@"Menu/{sampleName}" : @"UI/button-select"); } protected override bool OnMouseDown(MouseDownEvent e) From ef5ed262ca83d09bf6cda980fbbbd9201b3eea9e Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 15 Apr 2022 20:32:41 +0900 Subject: [PATCH 053/134] Add pitch randomisation to the OsuLogo hover sample --- osu.Game/Screens/Menu/OsuLogo.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index c82efe2d32..1d3aef0653 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -283,9 +283,15 @@ namespace osu.Game.Screens.Menu this.Delay(early_activation).Schedule(() => { if (beatIndex % timingPoint.TimeSignature.Numerator == 0) - sampleDownbeat.Play(); + { + sampleDownbeat?.Play(); + } else - sampleBeat.Play(); + { + var channel = sampleBeat.GetChannel(); + channel.Frequency.Value = 0.95 + RNG.NextDouble(0.1); + channel.Play(); + } }); } From ceb5b4f9b404b949571c0654858eda885777ea89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Apr 2022 20:46:49 +0900 Subject: [PATCH 054/134] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 77deec031b..ec638f7736 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index cf2245aa3a..be4bee7c9d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index b77dff9f41..0a0cab91a4 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From f1dd319fd99393c2a9ae2a6741f5621c6d47c77d Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Fri, 15 Apr 2022 15:38:47 +0100 Subject: [PATCH 055/134] Implement `ChannelList` for new chat design Reference design: https://www.figma.com/file/f8b2dHp9LJCMOqYP4mdrPZ/Client%2FChat?node-id=1%3A297 Adds new component `ChannelList` which makes up the sidebar channel selector of the new chat design. Contains two separate fill flows for public and private `ChannelItem` child components. Exposed bindable `SelectorActive` to indicate current state of the "Add more channels" button. Requires `Bindable` from parent component. Renames and updates the `TestSceneChannelListItem` to `TestSceneChannelList` to make use of new component and having both tests seemed redundant. --- .../Visual/Online/TestSceneChannelList.cs | 187 ++++++++++++++++++ .../Visual/Online/TestSceneChannelListItem.cs | 163 --------------- .../Overlays/Chat/ChannelList/ChannelList.cs | 174 ++++++++++++++++ .../Chat/ChannelList/ChannelListSelector.cs | 84 ++++++++ 4 files changed, 445 insertions(+), 163 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneChannelList.cs delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneChannelListItem.cs create mode 100644 osu.Game/Overlays/Chat/ChannelList/ChannelList.cs create mode 100644 osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs new file mode 100644 index 0000000000..d6a1e71f0d --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -0,0 +1,187 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; +using osu.Game.Overlays; +using osu.Game.Overlays.Chat.ChannelList; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneChannelList : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + + [Cached] + private readonly Bindable selected = new Bindable(); + + private OsuSpriteText selectorText; + private OsuSpriteText selectedText; + private OsuSpriteText leaveText; + private ChannelList channelList; + + [SetUp] + public void SetUp() + { + Schedule(() => + { + Child = new GridContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Height = 0.7f, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 20), + new Dimension(GridSizeMode.Absolute, 20), + new Dimension(GridSizeMode.Absolute, 20), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + selectorText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + }, + new Drawable[] + { + selectedText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + }, + new Drawable[] + { + leaveText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + }, + new Drawable[] + { + channelList = new ChannelList + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Y, + Width = 190, + }, + }, + }, + }; + + channelList.OnRequestSelect += channel => + { + channelList.SelectorActive.Value = false; + selected.Value = channel; + }; + + channelList.OnRequestLeave += channel => + { + leaveText.Text = $"OnRequestLeave: {channel.Name}"; + leaveText.FadeOutFromOne(1000, Easing.InQuint); + selected.Value = null; + channelList.RemoveChannel(channel); + }; + + channelList.SelectorActive.BindValueChanged(change => + { + selectorText.Text = $"Channel Selector Active: {change.NewValue}"; + }, true); + + selected.BindValueChanged(change => + { + selectedText.Text = $"Selected Channel: {change.NewValue?.Name ?? "[null]"}"; + }, true); + }); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Add Public Channels", () => + { + for (int i = 0; i < 10; i++) + channelList.AddChannel(createRandomPublicChannel()); + }); + + AddStep("Add Private Channels", () => + { + for (int i = 0; i < 10; i++) + channelList.AddChannel(createRandomPrivateChannel()); + }); + } + + [Test] + public void TestVisual() + { + AddStep("Unread Selected", () => + { + if (selected.Value != null) + channelList.GetItem(selected.Value).Unread.Value = true; + }); + + AddStep("Read Selected", () => + { + if (selected.Value != null) + channelList.GetItem(selected.Value).Unread.Value = false; + }); + + AddStep("Add Mention Selected", () => + { + if (selected.Value != null) + channelList.GetItem(selected.Value).Mentions.Value++; + }); + + AddStep("Add 98 Mentions Selected", () => + { + if (selected.Value != null) + channelList.GetItem(selected.Value).Mentions.Value += 98; + }); + + AddStep("Clear Mentions Selected", () => + { + if (selected.Value != null) + channelList.GetItem(selected.Value).Mentions.Value = 0; + }); + } + + private Channel createRandomPublicChannel() + { + int id = RNG.Next(0, 10000); + return new Channel + { + Name = $"#channel-{id}", + Type = ChannelType.Public, + Id = id, + }; + } + + private Channel createRandomPrivateChannel() + { + int id = RNG.Next(0, 10000); + return new Channel(new APIUser + { + Id = id, + Username = $"test user {id}", + }); + } + } +} diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelListItem.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelListItem.cs deleted file mode 100644 index af419c8b91..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelListItem.cs +++ /dev/null @@ -1,163 +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 NUnit.Framework; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Online.Chat; -using osu.Game.Overlays; -using osu.Game.Overlays.Chat.ChannelList; -using osuTK; - -namespace osu.Game.Tests.Visual.Online -{ - [TestFixture] - public class TestSceneChannelListItem : OsuTestScene - { - [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); - - [Cached] - private readonly Bindable selected = new Bindable(); - - private static readonly List channels = new List - { - createPublicChannel("#public-channel"), - createPublicChannel("#public-channel-long-name"), - createPrivateChannel("test user", 2), - createPrivateChannel("test user long name", 3), - }; - - private readonly Dictionary channelMap = new Dictionary(); - - private FillFlowContainer flow; - private OsuSpriteText selectedText; - private OsuSpriteText leaveText; - - [SetUp] - public void SetUp() - { - Schedule(() => - { - foreach (var item in channelMap.Values) - item.Expire(); - - channelMap.Clear(); - - Child = new FillFlowContainer - { - Direction = FillDirection.Vertical, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(10), - Children = new Drawable[] - { - selectedText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - leaveText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Height = 16, - AlwaysPresent = true, - }, - new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Y, - Width = 190, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background6, - }, - flow = new FillFlowContainer - { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - }, - }, - }, - }; - - selected.BindValueChanged(change => - { - selectedText.Text = $"Selected Channel: {change.NewValue?.Name ?? "[null]"}"; - }, true); - - foreach (var channel in channels) - { - var item = new ChannelListItem(channel); - flow.Add(item); - channelMap.Add(channel, item); - item.OnRequestSelect += c => selected.Value = c; - item.OnRequestLeave += leaveChannel; - } - }); - } - - [Test] - public void TestVisual() - { - AddStep("Select second item", () => selected.Value = channels.Skip(1).First()); - - AddStep("Unread Selected", () => - { - if (selected.Value != null) - channelMap[selected.Value].Unread.Value = true; - }); - - AddStep("Read Selected", () => - { - if (selected.Value != null) - channelMap[selected.Value].Unread.Value = false; - }); - - AddStep("Add Mention Selected", () => - { - if (selected.Value != null) - channelMap[selected.Value].Mentions.Value++; - }); - - AddStep("Add 98 Mentions Selected", () => - { - if (selected.Value != null) - channelMap[selected.Value].Mentions.Value += 98; - }); - - AddStep("Clear Mentions Selected", () => - { - if (selected.Value != null) - channelMap[selected.Value].Mentions.Value = 0; - }); - } - - private void leaveChannel(Channel channel) - { - leaveText.Text = $"OnRequestLeave: {channel.Name}"; - leaveText.FadeOutFromOne(1000, Easing.InQuint); - } - - private static Channel createPublicChannel(string name) => - new Channel { Name = name, Type = ChannelType.Public, Id = 1234 }; - - private static Channel createPrivateChannel(string username, int id) - => new Channel(new APIUser { Id = id, Username = username }); - } -} diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs new file mode 100644 index 0000000000..4ffb6be451 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -0,0 +1,174 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using osuTK; + +namespace osu.Game.Overlays.Chat.ChannelList +{ + public class ChannelList : Container + { + public Action? OnRequestSelect; + public Action? OnRequestLeave; + + public readonly BindableBool SelectorActive = new BindableBool(); + + private readonly Dictionary channelMap = new Dictionary(); + + private ChannelListItemFlow publicChannelFlow = null!; + private ChannelListItemFlow privateChannelFlow = null!; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background6, + }, + new ChannelListScrollContainer + { + Padding = new MarginPadding { Vertical = 7 }, + RelativeSizeAxes = Axes.Both, + ScrollbarAnchor = Anchor.TopLeft, + ScrollDistance = 35f, + Child = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + publicChannelFlow = new ChannelListItemFlow("CHANNELS"), + new ChannelListSelector + { + Margin = new MarginPadding { Bottom = 10 }, + SelectorActive = { BindTarget = SelectorActive }, + }, + privateChannelFlow = new ChannelListItemFlow("DIRECT MESSAGES"), + }, + }, + }, + }; + } + + public void AddChannel(Channel channel) + { + if (channelMap.ContainsKey(channel)) + return; + + ChannelListItem item = new ChannelListItem(channel); + item.OnRequestSelect += channel => this.OnRequestSelect?.Invoke(channel); + item.OnRequestLeave += channel => this.OnRequestLeave?.Invoke(channel); + + ChannelListItemFlow flow = getFlowForChannel(channel); + channelMap.Add(channel, item); + flow.Add(item); + } + + public void RemoveChannel(Channel channel) + { + if (!channelMap.ContainsKey(channel)) + return; + + ChannelListItem item = channelMap[channel]; + ChannelListItemFlow flow = getFlowForChannel(channel); + + channelMap.Remove(channel); + flow.Remove(item); + item.Expire(); + } + + public ChannelListItem GetItem(Channel channel) + { + if (!channelMap.ContainsKey(channel)) + throw new ArgumentOutOfRangeException(); + + return channelMap[channel]; + } + + private ChannelListItemFlow getFlowForChannel(Channel channel) + { + switch (channel.Type) + { + case ChannelType.Public: + return publicChannelFlow; + + case ChannelType.PM: + return privateChannelFlow; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + private class ChannelListItemFlow : FillFlowContainer + { + public ChannelListItemFlow(string label) + { + Direction = FillDirection.Vertical; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Add(new OsuSpriteText + { + Text = label, + Margin = new MarginPadding { Left = 18, Bottom = 5 }, + Font = OsuFont.Torus.With(size: 12), + }); + } + } + + private class ChannelListScrollContainer : OsuScrollContainer + { + protected override bool OnHover(HoverEvent e) + { + ScrollbarVisible = true; + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + ScrollbarVisible = false; + base.OnHoverLost(e); + } + + protected override ScrollbarContainer CreateScrollbar(Direction direction) + => new ChannelListScrollBar(direction); + + protected class ChannelListScrollBar : OsuScrollbar + { + private const float BAR_SIZE = 4; + private const float BAR_MARGIN = 7; + + public ChannelListScrollBar(Direction scrollDir) : base(scrollDir) + { + Size = new Vector2(BAR_SIZE); + Margin = new MarginPadding { Left = BAR_MARGIN }; + CornerRadius = 2; + } + + public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None) + { + Vector2 size = new Vector2(BAR_SIZE, val); + this.ResizeTo(size, duration, easing); + } + } + } + } +} diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs new file mode 100644 index 0000000000..5bc9a01598 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs @@ -0,0 +1,84 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.Chat.ChannelList +{ + public class ChannelListSelector : OsuClickableContainer + { + public readonly BindableBool SelectorActive = new BindableBool(); + + private Box hoverBox = null!; + private Box selectBox = null!; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Height = 30; + RelativeSizeAxes = Axes.X; + + Children = new Drawable[] + { + hoverBox = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background3, + Alpha = 0f, + }, + selectBox = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4, + Alpha = 0f, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = 18, Right = 10 }, + Child = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "Add More Channels", + Font = OsuFont.Torus.With(size: 17, weight: FontWeight.SemiBold), + Colour = colourProvider.Light3, + Margin = new MarginPadding { Bottom = 2 }, + RelativeSizeAxes = Axes.X, + Truncate = true, + }, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + SelectorActive.BindValueChanged(selected => selectBox.FadeTo(selected.NewValue ? 1 : 0)); + Action = () => SelectorActive.Value = true; + } + + protected override bool OnHover(HoverEvent e) + { + hoverBox.FadeIn(300, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + hoverBox.FadeOut(200, Easing.OutQuint); + base.OnHoverLost(e); + } + } +} From a0a2f8118e520003c23993bb2074d5664803758f Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Fri, 15 Apr 2022 22:05:20 +0100 Subject: [PATCH 056/134] Code quality fixes --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 4ffb6be451..9fcd675dc5 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -73,8 +73,8 @@ namespace osu.Game.Overlays.Chat.ChannelList return; ChannelListItem item = new ChannelListItem(channel); - item.OnRequestSelect += channel => this.OnRequestSelect?.Invoke(channel); - item.OnRequestLeave += channel => this.OnRequestLeave?.Invoke(channel); + item.OnRequestSelect += chan => OnRequestSelect?.Invoke(chan); + item.OnRequestLeave += chan => OnRequestLeave?.Invoke(chan); ChannelListItemFlow flow = getFlowForChannel(channel); channelMap.Add(channel, item); @@ -153,19 +153,19 @@ namespace osu.Game.Overlays.Chat.ChannelList protected class ChannelListScrollBar : OsuScrollbar { - private const float BAR_SIZE = 4; - private const float BAR_MARGIN = 7; + private const float bar_size = 4; + private const float bar_margin = 7; public ChannelListScrollBar(Direction scrollDir) : base(scrollDir) { - Size = new Vector2(BAR_SIZE); - Margin = new MarginPadding { Left = BAR_MARGIN }; + Size = new Vector2(bar_size); + Margin = new MarginPadding { Left = bar_margin }; CornerRadius = 2; } public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None) { - Vector2 size = new Vector2(BAR_SIZE, val); + Vector2 size = new Vector2(bar_size, val); this.ResizeTo(size, duration, easing); } } From b6631fb9f97e5add211cc4187fef2f03210b71fd Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Fri, 15 Apr 2022 22:22:20 +0100 Subject: [PATCH 057/134] Add newline for base class call --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 9fcd675dc5..94c05770ab 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -156,7 +156,8 @@ namespace osu.Game.Overlays.Chat.ChannelList private const float bar_size = 4; private const float bar_margin = 7; - public ChannelListScrollBar(Direction scrollDir) : base(scrollDir) + public ChannelListScrollBar(Direction scrollDir) + : base(scrollDir) { Size = new Vector2(bar_size); Margin = new MarginPadding { Left = bar_margin }; From 4a0c5a07813fb94107e40acc9295f3f315abdf08 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 16 Apr 2022 19:16:06 +0100 Subject: [PATCH 058/134] Remove hover override for `ChannelListScrollContainer` --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 94c05770ab..ef2f132501 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -10,7 +10,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -136,18 +135,6 @@ namespace osu.Game.Overlays.Chat.ChannelList private class ChannelListScrollContainer : OsuScrollContainer { - protected override bool OnHover(HoverEvent e) - { - ScrollbarVisible = true; - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - ScrollbarVisible = false; - base.OnHoverLost(e); - } - protected override ScrollbarContainer CreateScrollbar(Direction direction) => new ChannelListScrollBar(direction); From 36aea9009e754ebd18872d259a137e2df8f52a88 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 16 Apr 2022 19:19:18 +0100 Subject: [PATCH 059/134] Move `ChannelList` scrollbar to the right --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index ef2f132501..759242a779 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Chat.ChannelList { Padding = new MarginPadding { Vertical = 7 }, RelativeSizeAxes = Axes.Both, - ScrollbarAnchor = Anchor.TopLeft, + ScrollbarAnchor = Anchor.TopRight, ScrollDistance = 35f, Child = new FillFlowContainer { @@ -147,7 +147,7 @@ namespace osu.Game.Overlays.Chat.ChannelList : base(scrollDir) { Size = new Vector2(bar_size); - Margin = new MarginPadding { Left = bar_margin }; + Margin = new MarginPadding { Horizontal = bar_margin }; CornerRadius = 2; } From 2f41cddb00124179b13ae5c69a633f00805dbec9 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 16 Apr 2022 19:28:24 +0100 Subject: [PATCH 060/134] Remove redundant `Expire` call --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 759242a779..250e0cdf21 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -90,7 +90,6 @@ namespace osu.Game.Overlays.Chat.ChannelList channelMap.Remove(channel); flow.Remove(item); - item.Expire(); } public ChannelListItem GetItem(Channel channel) From 47c8b8010f1580ee9c7e583d0340f85f4c791e1d Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 16 Apr 2022 19:30:03 +0100 Subject: [PATCH 061/134] Use semibold font weight for `ChannelListItemFlow` label --- osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 250e0cdf21..5c2a1ad89f 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -127,7 +127,7 @@ namespace osu.Game.Overlays.Chat.ChannelList { Text = label, Margin = new MarginPadding { Left = 18, Bottom = 5 }, - Font = OsuFont.Torus.With(size: 12), + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), }); } } From 399d8eac36d8171a806b004843dc9ab747d0e500 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Apr 2022 14:44:28 +0900 Subject: [PATCH 062/134] Update `LocalisationAnalyser` with dotnet 6 compatibility changes --- .config/dotnet-tools.json | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 7b94ee5bdd..5a3eadf607 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -27,10 +27,10 @@ ] }, "ppy.localisationanalyser.tools": { - "version": "2022.320.0", + "version": "2022.417.0", "commands": [ "localisation" ] } } -} \ No newline at end of file +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index be4bee7c9d..94614bea76 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From a3cc07ff3f6adcf9c42355772eb0e489cfd35931 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Apr 2022 15:08:15 +0900 Subject: [PATCH 063/134] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index ec638f7736..a8952df329 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 94614bea76..3246255815 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 0a0cab91a4..668d23f82e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From f37444938fb20b951fa7de9bdcba609173681967 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 02:05:09 +0900 Subject: [PATCH 064/134] Update squirrel to latest version Includes disk space checks and better erroring. --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 3ca6411812..a4f309c6ac 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -24,7 +24,7 @@ - + From 67c44db8d5aad22d8eb381574fb7421495840c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 27 Mar 2022 22:55:52 +0200 Subject: [PATCH 065/134] Add extension points required for replacing old mod overlay --- osu.Game/Overlays/Mods/ModColumn.cs | 7 +- osu.Game/Overlays/Mods/ModSelectScreen.cs | 89 +++++++++++++++++------ 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index f84ae4ac8a..abd243e1f7 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -54,6 +54,8 @@ namespace osu.Game.Overlays.Mods public Bindable> SelectedMods = new Bindable>(Array.Empty()); + protected virtual ModPanel CreateModPanel(Mod mod) => new ModPanel(mod); + private readonly Key[]? toggleKeys; private readonly Bindable>> availableMods = new Bindable>>(); @@ -258,10 +260,7 @@ namespace osu.Game.Overlays.Mods cancellationTokenSource?.Cancel(); - var panels = newMods.Select(mod => new ModPanel(mod) - { - Shear = new Vector2(-ModPanel.SHEAR_X, 0) - }); + var panels = newMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ModPanel.SHEAR_X, 0))); Task? loadTask; diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 62080ec1b5..d174474076 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics; @@ -31,16 +33,43 @@ namespace osu.Game.Overlays.Mods protected override bool StartHidden => true; + private Func isValidMod = m => true; + + public Func IsValidMod + { + get => isValidMod; + set + { + isValidMod = value ?? throw new ArgumentNullException(nameof(value)); + + if (IsLoaded) + updateAvailableMods(); + } + } + + /// + /// Whether configurable s can be configured by the local user. + /// + protected virtual bool AllowConfiguration => true; + + /// + /// Whether the total score multiplier calculated from the current selected set of mods should be shown. + /// + protected virtual bool ShowTotalMultiplier => true; + + protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys); + private readonly BindableBool customisationVisible = new BindableBool(); - private DifficultyMultiplierDisplay multiplierDisplay; - private ModSettingsArea modSettingsArea; - private FillFlowContainer columnFlow; - private GridContainer grid; - private Container mainContent; + private DifficultyMultiplierDisplay? multiplierDisplay; + private ModSettingsArea modSettingsArea = null!; + private Container aboveColumnsContainer = null!; + private FillFlowContainer columnFlow = null!; + private GridContainer grid = null!; + private Container mainContent = null!; - private PopupScreenTitle header; - private Container footer; + private PopupScreenTitle header = null!; + private Container footer = null!; [BackgroundDependencyLoader] private void load() @@ -82,7 +111,7 @@ namespace osu.Game.Overlays.Mods }, new Drawable[] { - new Container + aboveColumnsContainer = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -94,11 +123,6 @@ namespace osu.Game.Overlays.Mods { Horizontal = 100, Vertical = 10 - }, - Child = multiplierDisplay = new DifficultyMultiplierDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre } } }, @@ -126,11 +150,11 @@ namespace osu.Game.Overlays.Mods Margin = new MarginPadding { Right = 70 }, Children = new[] { - new ModColumn(ModType.DifficultyReduction, false, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), - new ModColumn(ModType.DifficultyIncrease, false, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), - new ModColumn(ModType.Automation, false, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), - new ModColumn(ModType.Conversion, false), - new ModColumn(ModType.Fun, false) + CreateModColumn(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), + CreateModColumn(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), + CreateModColumn(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), + CreateModColumn(ModType.Conversion), + CreateModColumn(ModType.Fun) } } } @@ -182,6 +206,15 @@ namespace osu.Game.Overlays.Mods } }; + if (ShowTotalMultiplier) + { + aboveColumnsContainer.Add(multiplierDisplay = new DifficultyMultiplierDisplay + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight + }); + } + columnFlow.Shear = new Vector2(ModPanel.SHEAR_X, 0); } @@ -204,10 +237,15 @@ namespace osu.Game.Overlays.Mods } customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); + + updateAvailableMods(); } private void updateMultiplier() { + if (multiplierDisplay == null) + return; + double multiplier = 1.0; foreach (var mod in SelectedMods.Value) @@ -216,8 +254,17 @@ namespace osu.Game.Overlays.Mods multiplierDisplay.Current.Value = multiplier; } + private void updateAvailableMods() + { + foreach (var column in columnFlow) + column.Filter = isValidMod; + } + private void updateCustomisation(ValueChangedEvent> valueChangedEvent) { + if (!AllowConfiguration) + return; + bool anyCustomisableMod = false; bool anyModWithRequiredCustomisationAdded = false; @@ -292,7 +339,7 @@ namespace osu.Game.Overlays.Mods header.MoveToY(0, fade_in_duration, Easing.OutQuint); footer.MoveToY(0, fade_in_duration, Easing.OutQuint); - multiplierDisplay + multiplierDisplay? .Delay(fade_in_duration * 0.65f) .FadeIn(fade_in_duration / 2, Easing.OutQuint) .ScaleTo(1, fade_in_duration, Easing.OutElastic); @@ -313,7 +360,7 @@ namespace osu.Game.Overlays.Mods base.PopOut(); this.FadeOut(fade_out_duration, Easing.OutQuint); - multiplierDisplay + multiplierDisplay? .FadeOut(fade_out_duration / 2, Easing.OutQuint) .ScaleTo(0.75f, fade_out_duration, Easing.OutQuint); @@ -368,7 +415,7 @@ namespace osu.Game.Overlays.Mods { public BindableBool HandleMouse { get; } = new BindableBool(); - public Action OnClicked { get; set; } + public Action? OnClicked { get; set; } protected override bool Handle(UIEvent e) { From 7eebc2012465f58c95b6ff3bc2bb452f30d1a314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 27 Mar 2022 23:07:52 +0200 Subject: [PATCH 066/134] Add replacements for mod overlays used by game --- osu.Game/Overlays/Mods/UserModSelectScreen.cs | 24 +++++++++++++++ .../Screens/OnlinePlay/FreeModSelectScreen.cs | 29 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 osu.Game/Overlays/Mods/UserModSelectScreen.cs create mode 100644 osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs diff --git a/osu.Game/Overlays/Mods/UserModSelectScreen.cs b/osu.Game/Overlays/Mods/UserModSelectScreen.cs new file mode 100644 index 0000000000..81943da514 --- /dev/null +++ b/osu.Game/Overlays/Mods/UserModSelectScreen.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Game.Rulesets.Mods; +using osuTK.Input; + +namespace osu.Game.Overlays.Mods +{ + public class UserModSelectScreen : ModSelectScreen + { + protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys); + + private class UserModColumn : ModColumn + { + public UserModColumn(ModType modType, bool allowBulkSelection, [CanBeNull] Key[] toggleKeys = null) + : base(modType, allowBulkSelection, toggleKeys) + { + } + + protected override ModPanel CreateModPanel(Mod mod) => new IncompatibilityDisplayingModPanel(mod); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs new file mode 100644 index 0000000000..438b334e0b --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -0,0 +1,29 @@ +// 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.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; +using osuTK.Input; + +namespace osu.Game.Screens.OnlinePlay +{ + public class FreeModSelectScreen : ModSelectScreen + { + protected override bool AllowConfiguration => false; + protected override bool ShowTotalMultiplier => false; + + public new Func IsValidMod + { + get => base.IsValidMod; + set => base.IsValidMod = m => m.HasImplementation && m.UserPlayable && value.Invoke(m); + } + + public FreeModSelectScreen() + { + IsValidMod = _ => true; + } + + protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys); + } +} From 20c17b8c98448f7ad1a93b53ebb259b8961765db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 19:42:52 +0200 Subject: [PATCH 067/134] Make base mod select screen abstract --- .../Visual/UserInterface/TestSceneModSelectScreen.cs | 4 ++-- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 34d9ddcc4e..4a738cb29d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Resolved] private RulesetStore rulesetStore { get; set; } - private ModSelectScreen modSelectScreen; + private UserModSelectScreen modSelectScreen; [SetUpSteps] public void SetUpSteps() @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void createScreen() { - AddStep("create screen", () => Child = modSelectScreen = new ModSelectScreen + AddStep("create screen", () => Child = modSelectScreen = new UserModSelectScreen { RelativeSizeAxes = Axes.Both, State = { Value = Visibility.Visible }, diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index d174474076..ad111cd5b9 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -23,7 +23,7 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public class ModSelectScreen : OsuFocusedOverlayContainer + public abstract class ModSelectScreen : OsuFocusedOverlayContainer { [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); From 9942b0a9467cf51fb6284d157fbc67ea78107af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 19:53:05 +0200 Subject: [PATCH 068/134] Add test scene for free mod select screen --- .../TestSceneFreeModSelectScreen.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs new file mode 100644 index 0000000000..974cd710f7 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Game.Screens.OnlinePlay; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneFreeModSelectScreen : MultiplayerTestScene + { + [Test] + public void TestFreeModSelect() + { + FreeModSelectScreen freeModSelectScreen = null; + + AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen + { + State = { Value = Visibility.Visible } + }); + AddToggleStep("toggle visibility", visible => + { + if (freeModSelectScreen != null) + freeModSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden; + }); + } + } +} From 4b6d42c7e890bcd797535fc6c5203e4fc70112aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 16:54:30 +0200 Subject: [PATCH 069/134] Add assertion covering free mod selection mod validity filter --- .../Visual/Multiplayer/TestSceneFreeModSelectScreen.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs index 974cd710f7..0db05e3a6a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -1,8 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Overlays.Mods; using osu.Game.Screens.OnlinePlay; namespace osu.Game.Tests.Visual.Multiplayer @@ -18,6 +21,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { State = { Value = Visibility.Visible } }); + + AddAssert("all visible mods are playable", + () => this.ChildrenOfType() + .Where(panel => panel.IsPresent) + .All(panel => panel.Mod.HasImplementation && panel.Mod.UserPlayable)); + AddToggleStep("toggle visibility", visible => { if (freeModSelectScreen != null) From ffb5c1e86c71003c6d6f8cda6e173948936e64f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 16:58:02 +0200 Subject: [PATCH 070/134] Tweak colours on incompatibility displaying mod panel --- osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs index 4b6759c209..6c6c36eec9 100644 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs @@ -46,8 +46,8 @@ namespace osu.Game.Overlays.Mods if (incompatible.Value) { - Colour4 backgroundColour = ColourProvider.Background5; - Colour4 textBackgroundColour = ColourProvider.Background4; + Colour4 backgroundColour = ColourProvider.Background6; + Colour4 textBackgroundColour = ColourProvider.Background5; Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(backgroundColour, textBackgroundColour), TRANSITION_DURATION, Easing.OutQuint); Background.FadeColour(backgroundColour, TRANSITION_DURATION, Easing.OutQuint); From 8af865a1c5766d850743f2654b48a50a63aa3316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 21:20:16 +0200 Subject: [PATCH 071/134] Fix incompatibility panel using reference equality --- osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs index 6c6c36eec9..38781455fa 100644 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs @@ -37,7 +37,9 @@ namespace osu.Game.Overlays.Mods private void updateIncompatibility() { - incompatible.Value = selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(Mod) && !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod)); + incompatible.Value = selectedMods.Value.Count > 0 + && selectedMods.Value.All(selected => selected.GetType() != Mod.GetType()) + && !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod)); } protected override void UpdateState() From 881df7663d8ee8fab93759c57651e78c4ddfcab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 22:30:02 +0200 Subject: [PATCH 072/134] Fix filter not taking effect if applied before panel load completion --- osu.Game/Overlays/Mods/ModPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index 312171cf74..c9aaa1c6c0 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -159,7 +159,7 @@ namespace osu.Game.Overlays.Mods playStateChangeSamples(); UpdateState(); }); - Filtered.BindValueChanged(_ => updateFilterState()); + Filtered.BindValueChanged(_ => updateFilterState(), true); UpdateState(); FinishTransforms(true); From 0d5ce336f49cfe4a475c6b587fc9c1e3dc52f3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Apr 2022 23:26:25 +0200 Subject: [PATCH 073/134] Hide mod customisation toggle if customisation not permitted --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index ad111cd5b9..237af2a71d 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -180,14 +180,6 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.BottomCentre, Colour = colourProvider.Background5 }, - new ShearedToggleButton(200) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = 14, Left = 70 }, - Text = "Mod Customisation", - Active = { BindTarget = customisationVisible } - } } }, new ClickToReturnContainer @@ -206,6 +198,18 @@ namespace osu.Game.Overlays.Mods } }; + if (AllowConfiguration) + { + footer.Add(new ShearedToggleButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Vertical = 14, Left = 70 }, + Text = "Mod Customisation", + Active = { BindTarget = customisationVisible } + }); + } + if (ShowTotalMultiplier) { aboveColumnsContainer.Add(multiplierDisplay = new DifficultyMultiplierDisplay From 338d94626e5cb5f86ce924c76e0cea3b16970a1f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 06:42:06 +0300 Subject: [PATCH 074/134] Expose underlying skin of `EditorBeatmapSkin` --- osu.Game/Screens/Edit/EditorBeatmapSkin.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs index 429df85904..243a994a7f 100644 --- a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs +++ b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs @@ -21,13 +21,18 @@ namespace osu.Game.Screens.Edit { public event Action BeatmapSkinChanged; + /// + /// The underlying beatmap skin. + /// + protected internal ISkin Skin => skin; + + private readonly Skin skin; + /// /// The combo colours of this skin. /// If empty, the default combo colours will be used. /// - public readonly BindableList ComboColours; - - private readonly Skin skin; + public BindableList ComboColours { get; } public EditorBeatmapSkin(Skin skin) { From 80cce7c3cb1d5c840ec6bbb599a3bcdf3baf1e17 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 06:46:06 +0300 Subject: [PATCH 075/134] Add failing test case --- .../Visual/Editing/TestSceneComposeScreen.cs | 63 +++++++++++++------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs index d100fba8d6..30c8539d85 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs @@ -1,44 +1,71 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose; +using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneComposeScreen : EditorClockTestScene { - [Cached(typeof(EditorBeatmap))] - [Cached(typeof(IBeatSnapProvider))] - private readonly EditorBeatmap editorBeatmap = - new EditorBeatmap(new OsuBeatmap - { - BeatmapInfo = - { - Ruleset = new OsuRuleset().RulesetInfo - } - }); + private EditorBeatmap editorBeatmap; [Cached] private EditorClipboard clipboard = new EditorClipboard(); - protected override void LoadComplete() + [SetUpSteps] + public void SetUpSteps() { - base.LoadComplete(); - - Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); - - Child = new ComposeScreen + AddStep("setup compose screen", () => { - State = { Value = Visibility.Visible }, - }; + var beatmap = new OsuBeatmap + { + BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } + }; + + editorBeatmap = new EditorBeatmap(beatmap, new LegacyBeatmapSkin(beatmap.BeatmapInfo, null)); + + Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + + Child = new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(EditorBeatmap), editorBeatmap), + (typeof(IBeatSnapProvider), editorBeatmap), + }, + Child = new ComposeScreen { State = { Value = Visibility.Visible } }, + }; + }); + + AddUntilStep("wait for composer", () => this.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + } + + /// + /// Ensures that the skin of the edited beatmap is properly wrapped in a . + /// + [Test] + public void TestLegacyBeatmapSkinHasTransformer() + { + AddAssert("legacy beatmap skin has transformer", () => + { + var sources = this.ChildrenOfType().First().AllSources; + return sources.OfType().Count(t => t.Skin == editorBeatmap.BeatmapSkin.AsNonNull().Skin) == 1; + }); } } } From 2cb217e06c04d63de4bc4151c7631b9abc448062 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 06:59:44 +0300 Subject: [PATCH 076/134] Fix editor legacy beatmap skins not receiving transformer --- osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs b/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs index decfa879a8..694d0253e0 100644 --- a/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs +++ b/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.Edit private readonly EditorBeatmapSkin? beatmapSkin; public EditorSkinProvidingContainer(EditorBeatmap editorBeatmap) - : base(editorBeatmap.PlayableBeatmap.BeatmapInfo.Ruleset.CreateInstance(), editorBeatmap.PlayableBeatmap, editorBeatmap.BeatmapSkin) + : base(editorBeatmap.PlayableBeatmap.BeatmapInfo.Ruleset.CreateInstance(), editorBeatmap.PlayableBeatmap, editorBeatmap.BeatmapSkin?.Skin) { beatmapSkin = editorBeatmap.BeatmapSkin; } From 90093c1d9d3fab7757218cdb021f08c64cdd1fe7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 13:32:37 +0900 Subject: [PATCH 077/134] Combine `private` skin variable into exposed one --- osu.Game/Screens/Edit/EditorBeatmapSkin.cs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs index 243a994a7f..f650ffa5a3 100644 --- a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs +++ b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs @@ -24,9 +24,7 @@ namespace osu.Game.Screens.Edit /// /// The underlying beatmap skin. /// - protected internal ISkin Skin => skin; - - private readonly Skin skin; + protected internal readonly Skin Skin; /// /// The combo colours of this skin. @@ -36,11 +34,11 @@ namespace osu.Game.Screens.Edit public EditorBeatmapSkin(Skin skin) { - this.skin = skin; + Skin = skin; ComboColours = new BindableList(); - if (skin.Configuration.ComboColours != null) - ComboColours.AddRange(skin.Configuration.ComboColours.Select(c => (Colour4)c)); + if (Skin.Configuration.ComboColours != null) + ComboColours.AddRange(Skin.Configuration.ComboColours.Select(c => (Colour4)c)); ComboColours.BindCollectionChanged((_, __) => updateColours()); } @@ -48,16 +46,16 @@ namespace osu.Game.Screens.Edit private void updateColours() { - skin.Configuration.CustomComboColours = ComboColours.Select(c => (Color4)c).ToList(); + Skin.Configuration.CustomComboColours = ComboColours.Select(c => (Color4)c).ToList(); invokeSkinChanged(); } #region Delegated ISkin implementation - public Drawable GetDrawableComponent(ISkinComponent component) => skin.GetDrawableComponent(component); - public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => skin.GetTexture(componentName, wrapModeS, wrapModeT); - public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo); - public IBindable GetConfig(TLookup lookup) => skin.GetConfig(lookup); + public Drawable GetDrawableComponent(ISkinComponent component) => Skin.GetDrawableComponent(component); + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => Skin.GetTexture(componentName, wrapModeS, wrapModeT); + public ISample GetSample(ISampleInfo sampleInfo) => Skin.GetSample(sampleInfo); + public IBindable GetConfig(TLookup lookup) => Skin.GetConfig(lookup); #endregion } From 2202863e1a1caf323aa341dec967fdaaefd4a237 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 17:49:28 +0900 Subject: [PATCH 078/134] Split out `IPerformFromScreenRunner` to allow for easier testing --- osu.Game/OsuGame.cs | 8 +----- .../Dashboard/CurrentlyPlayingDisplay.cs | 5 ++-- .../Sections/DebugSettings/GeneralSettings.cs | 5 ++-- .../StableDirectoryLocationDialog.cs | 7 ++--- osu.Game/Screens/IPerformFromScreenRunner.cs | 26 +++++++++++++++++++ osu.Game/Screens/Menu/MainMenu.cs | 4 +-- .../Skinning/Editor/SkinEditorSceneLibrary.cs | 7 ++--- 7 files changed, 43 insertions(+), 19 deletions(-) create mode 100644 osu.Game/Screens/IPerformFromScreenRunner.cs diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 73121f6e7d..76ccbe0a08 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -63,7 +63,7 @@ namespace osu.Game /// The full osu! experience. Builds on top of to add menus and binding logic /// for initial components that are generally retrieved via DI. /// - public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo + public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo, IPerformFromScreenRunner { /// /// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications). @@ -586,12 +586,6 @@ namespace osu.Game private PerformFromMenuRunner performFromMainMenuTask; - /// - /// Perform an action only after returning to a specific screen as indicated by . - /// Eagerly tries to exit the current screen until it succeeds. - /// - /// The action to perform once we are in the correct state. - /// An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. is used if not specified. public void PerformFromScreen(Action action, IEnumerable validScreens = null) { performFromMainMenuTask?.Cancel(); diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 117de88166..786401b7a8 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -14,6 +14,7 @@ using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Spectator; +using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; using osu.Game.Users; @@ -106,7 +107,7 @@ namespace osu.Game.Overlays.Dashboard public readonly APIUser User; [Resolved(canBeNull: true)] - private OsuGame game { get; set; } + private IPerformFromScreenRunner performer { get; set; } public PlayingUserPanel(APIUser user) { @@ -140,7 +141,7 @@ namespace osu.Game.Overlays.Dashboard Text = "Watch", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Action = () => game?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), + Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), Enabled = { Value = User.Id != api.LocalUser.Value.Id } } } diff --git a/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs index 60540a089e..8833420523 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Localisation; +using osu.Game.Screens; using osu.Game.Screens.Import; namespace osu.Game.Overlays.Settings.Sections.DebugSettings @@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings protected override LocalisableString Header => DebugSettingsStrings.GeneralHeader; [BackgroundDependencyLoader(true)] - private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, OsuGame game) + private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, IPerformFromScreenRunner performer) { Children = new Drawable[] { @@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings Add(new SettingsButton { Text = DebugSettingsStrings.ImportFiles, - Action = () => game?.PerformFromScreen(menu => menu.Push(new FileImportScreen())) + Action = () => performer?.PerformFromScreen(menu => menu.Push(new FileImportScreen())) }); } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 904c9deaae..4e81cd63b8 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -6,13 +6,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Game.Overlays.Dialog; +using osu.Game.Screens; namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class StableDirectoryLocationDialog : PopupDialog { - [Resolved] - private OsuGame game { get; set; } + [Resolved(canBeNull: true)] + private IPerformFromScreenRunner performer { get; set; } public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) { @@ -25,7 +26,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance new PopupDialogOkButton { Text = "Sure! I know where it is located!", - Action = () => Schedule(() => game.PerformFromScreen(screen => screen.Push(new StableDirectorySelectScreen(taskCompletionSource)))) + Action = () => Schedule(() => performer.PerformFromScreen(screen => screen.Push(new StableDirectorySelectScreen(taskCompletionSource)))) }, new PopupDialogCancelButton { diff --git a/osu.Game/Screens/IPerformFromScreenRunner.cs b/osu.Game/Screens/IPerformFromScreenRunner.cs new file mode 100644 index 0000000000..655bebdeb0 --- /dev/null +++ b/osu.Game/Screens/IPerformFromScreenRunner.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Screens; +using osu.Game.Screens.Menu; + +namespace osu.Game.Screens +{ + /// + /// Manages a global screen stack to allow nested components a guarantee of where work is executed. + /// + [Cached] + public interface IPerformFromScreenRunner + { + /// + /// Perform an action only after returning to a specific screen as indicated by . + /// Eagerly tries to exit the current screen until it succeeds. + /// + /// The action to perform once we are in the correct state. + /// An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. is used if not specified. + void PerformFromScreen(Action action, IEnumerable validScreens = null); + } +} diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e2d79b4015..442a536cf9 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -148,14 +148,14 @@ namespace osu.Game.Screens.Menu } [Resolved(canBeNull: true)] - private OsuGame game { get; set; } + private IPerformFromScreenRunner performer { get; set; } private void confirmAndExit() { if (exitConfirmed) return; exitConfirmed = true; - game?.PerformFromScreen(menu => menu.Exit()); + performer?.PerformFromScreen(menu => menu.Exit()); } private void preloadSongSelect() diff --git a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs index 0808cd157f..4507526806 100644 --- a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs +++ b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs @@ -15,6 +15,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Screens; using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osuTK; @@ -28,7 +29,7 @@ namespace osu.Game.Skinning.Editor private const float padding = 10; [Resolved(canBeNull: true)] - private OsuGame game { get; set; } + private IPerformFromScreenRunner performer { get; set; } [Resolved] private IBindable ruleset { get; set; } @@ -75,7 +76,7 @@ namespace osu.Game.Skinning.Editor Text = "Song Select", Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Action = () => game?.PerformFromScreen(screen => + Action = () => performer?.PerformFromScreen(screen => { if (screen is SongSelect) return; @@ -88,7 +89,7 @@ namespace osu.Game.Skinning.Editor Text = "Gameplay", Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Action = () => game?.PerformFromScreen(screen => + Action = () => performer?.PerformFromScreen(screen => { if (screen is Player) return; From e3153132666a911dc2ac47d759d6a7654b128f59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 18:09:14 +0900 Subject: [PATCH 079/134] Split out `IDialogOverlay` to allow for easier testing --- .../TestSceneManageCollectionsDialog.cs | 2 +- .../Visual/Navigation/TestSceneOsuGame.cs | 2 +- .../Navigation/TestScenePerformFromScreen.cs | 11 ++++--- .../Navigation/TestSceneScreenNavigation.cs | 13 ++++---- .../TestSceneDeleteLocalScore.cs | 2 +- .../UserInterface/TestSceneDialogOverlay.cs | 2 +- .../Collections/DrawableCollectionListItem.cs | 2 +- osu.Game/Database/LegacyImportManager.cs | 2 +- osu.Game/Online/Chat/ExternalLinkOpener.cs | 2 +- .../Online/Leaderboards/LeaderboardScore.cs | 2 +- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 +- osu.Game/Overlays/DialogOverlay.cs | 2 +- osu.Game/Overlays/IDialogOverlay.cs | 31 +++++++++++++++++++ .../Sections/Maintenance/GeneralSettings.cs | 2 +- .../Maintenance/MigrationSelectScreen.cs | 2 +- osu.Game/PerformFromMenuRunner.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Menu/MainMenu.cs | 2 +- osu.Game/Screens/Menu/StorageErrorDialog.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 4 +-- osu.Game/Tests/Visual/EditorTestScene.cs | 2 +- osu.Game/Tests/Visual/ScreenTestScene.cs | 2 +- 24 files changed, 66 insertions(+), 33 deletions(-) create mode 100644 osu.Game/Overlays/IDialogOverlay.cs diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index e40dd58663..51ca55f37f 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Collections }); Dependencies.Cache(manager); - Dependencies.Cache(dialogOverlay); + Dependencies.CacheAs(dialogOverlay); } [SetUp] diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index b8d1636ea0..921d088fc7 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Navigation typeof(LoginOverlay), typeof(MusicController), typeof(AccountCreationOverlay), - typeof(DialogOverlay), + typeof(IDialogOverlay), typeof(ScreenshotManager) }; diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index f045e3a3e8..c4099b55c7 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; @@ -113,12 +114,12 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("did not perform", () => !actionPerformed); AddAssert("only one exit attempt", () => blocker.ExitAttempts == 1); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); if (confirmed) { AddStep("accept dialog", () => InputManager.Key(Key.Number1)); - AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); + AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); AddUntilStep("did perform", () => actionPerformed); } else @@ -145,7 +146,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == blocker2); AddAssert("did not perform", () => !actionPerformed); @@ -187,7 +188,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); @@ -221,7 +222,7 @@ namespace osu.Game.Tests.Visual.Navigation public class DialogBlockingScreen : OsuScreen { [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private int dialogDisplayCount; diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 394976eb43..0b7242eee0 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; @@ -200,10 +201,10 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("choose clear all scores", () => InputManager.Key(Key.Number4)); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); - AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); AddStep("confirm deletion", () => InputManager.Key(Key.Number1)); - AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); + AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find(score.ID)?.DeletePending == true)); @@ -246,10 +247,10 @@ namespace osu.Game.Tests.Visual.Navigation InputManager.Click(MouseButton.Left); }); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); - AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); AddStep("confirm deletion", () => InputManager.Key(Key.Number1)); - AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); + AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find(score.ID)?.DeletePending == true)); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 72dfc53c01..1350052ae6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.UserInterface private BeatmapInfo beatmapInfo; - [Cached] + [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay; public TestSceneDeleteLocalScore() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index 405461eec8..17234f4022 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneDialogOverlay : OsuTestScene + public class TestSceneIDialogOverlay : OsuTestScene { private DialogOverlay overlay; diff --git a/osu.Game/Collections/DrawableCollectionListItem.cs b/osu.Game/Collections/DrawableCollectionListItem.cs index c4cb040b52..5a20b7e7bd 100644 --- a/osu.Game/Collections/DrawableCollectionListItem.cs +++ b/osu.Game/Collections/DrawableCollectionListItem.cs @@ -158,7 +158,7 @@ namespace osu.Game.Collections public Func IsTextBoxHovered; [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private CollectionManager collectionManager { get; set; } diff --git a/osu.Game/Database/LegacyImportManager.cs b/osu.Game/Database/LegacyImportManager.cs index 4dc26b18bb..59394c2952 100644 --- a/osu.Game/Database/LegacyImportManager.cs +++ b/osu.Game/Database/LegacyImportManager.cs @@ -40,7 +40,7 @@ namespace osu.Game.Database private OsuGame game { get; set; } [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private DesktopGameHost desktopGameHost { get; set; } diff --git a/osu.Game/Online/Chat/ExternalLinkOpener.cs b/osu.Game/Online/Chat/ExternalLinkOpener.cs index 328b43c4e8..20d8459132 100644 --- a/osu.Game/Online/Chat/ExternalLinkOpener.cs +++ b/osu.Game/Online/Chat/ExternalLinkOpener.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online.Chat private GameHost host { get; set; } [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private Bindable externalLinkWarning; diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index ddd9d9a2b2..eecb7ff6b3 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -64,7 +64,7 @@ namespace osu.Game.Online.Leaderboards private List statisticsLabels; [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private SongSelect songSelect { get; set; } diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 6159ab59f0..9fd1a32e84 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -216,7 +216,7 @@ namespace osu.Game.Overlays.Dialog }; // It's important we start in a visible state so our state fires on hide, even before load. - // This is used by the DialogOverlay to know when the dialog was dismissed. + // This is used by the IDialogOverlay to know when the dialog was dismissed. Show(); } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 9dea1ca00a..15d89a561a 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -14,7 +14,7 @@ using osu.Game.Audio.Effects; namespace osu.Game.Overlays { - public class DialogOverlay : OsuFocusedOverlayContainer + public class DialogOverlay : OsuFocusedOverlayContainer, IDialogOverlay { private readonly Container dialogContainer; diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs new file mode 100644 index 0000000000..07f28a7204 --- /dev/null +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -0,0 +1,31 @@ +#nullable enable +// 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.Game.Overlays.Dialog; + +namespace osu.Game.Overlays +{ + /// + /// A global overlay that can show popup dialogs. + /// + [Cached] + public interface IDialogOverlay + { + /// + /// Push a new dialog for display. + /// + /// + /// This will immediate dismiss any already displayed dialog (cancelling the action). + /// If the dialog instance provided is already displayed, it will be a noop. + /// + /// The dialog to be presented. + void Push(PopupDialog dialog); + + /// + /// The currently displayed dialog, if any. + /// + PopupDialog? CurrentDialog { get; } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index aa02d086f4..be4b0decd9 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private SettingsButton undeleteButton; [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] LegacyImportManager legacyImportManager, DialogOverlay dialogOverlay) + private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] LegacyImportManager legacyImportManager, IDialogOverlay dialogOverlay) { if (legacyImportManager?.SupportsImportFromStable == true) { diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs index 0304a4291a..3cb5521e51 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private OsuGameBase game { get; set; } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } protected override DirectoryInfo InitialPath => new DirectoryInfo(storage.GetFullPath(string.Empty)).Parent; diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 528470768c..7039d5379c 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -26,7 +26,7 @@ namespace osu.Game private NotificationOverlay notifications { get; set; } [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(canBeNull: true)] private OsuGame game { get; set; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 57f7429e06..255571dfdb 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -84,7 +84,7 @@ namespace osu.Game.Screens.Edit private Storage storage { get; set; } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(canBeNull: true)] private NotificationOverlay notifications { get; set; } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e2d79b4015..9e6af860fc 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Menu private IAPIProvider api { get; set; } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private BackgroundScreenDefault background; diff --git a/osu.Game/Screens/Menu/StorageErrorDialog.cs b/osu.Game/Screens/Menu/StorageErrorDialog.cs index 250623ec68..f4c77d5d8f 100644 --- a/osu.Game/Screens/Menu/StorageErrorDialog.cs +++ b/osu.Game/Screens/Menu/StorageErrorDialog.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Menu public class StorageErrorDialog : PopupDialog { [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } public StorageErrorDialog(OsuStorage storage, OsuStorageError error) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 7ef0bb5923..0dbf73fc5b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -238,7 +238,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private bool exitConfirmed; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 618c5cf5ec..2d70b1aecb 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select.Carousel private Action viewDetails; [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private CollectionManager collectionManager { get; set; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 2d1a2bce4e..dc505063d1 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Select protected Container LeftArea { get; private set; } private BeatmapInfoWedge beatmapInfoWedge; - private DialogOverlay dialogOverlay; + private IDialogOverlay dialogOverlay; [Resolved] private BeatmapManager beatmaps { get; set; } @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Select private MusicController music { get; set; } [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) + private void load(AudioManager audio, IDialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) { // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 51221cb8fe..ca12bc73fe 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual { [Resolved(canBeNull: true)] [CanBeNull] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } public new void Undo() => base.Undo(); diff --git a/osu.Game/Tests/Visual/ScreenTestScene.cs b/osu.Game/Tests/Visual/ScreenTestScene.cs index b6f6ca6daa..e9069d8c9c 100644 --- a/osu.Game/Tests/Visual/ScreenTestScene.cs +++ b/osu.Game/Tests/Visual/ScreenTestScene.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual protected override Container Content => content; - [Cached] + [Cached(typeof(IDialogOverlay))] protected DialogOverlay DialogOverlay { get; private set; } protected ScreenTestScene() From 25f1f772f3d6cad2a0bb9728057e106588555bd0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 18:36:22 +0900 Subject: [PATCH 080/134] Allow caching via `loadComponentSingleFile` with interface specification --- osu.Game/OsuGame.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 73121f6e7d..f8bc32431c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -825,7 +825,7 @@ namespace osu.Game }, rightFloatingOverlayContent.Add, true); loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); - loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); + loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(CreateHighPerformanceSession(), Add); @@ -982,12 +982,15 @@ namespace osu.Game /// The component to load. /// An action to invoke on load completion (generally to add the component to the hierarchy). /// Whether to cache the component as type into the game dependencies before any scheduling. - private T loadComponentSingleFile(T component, Action loadCompleteAction, bool cache = false) - where T : Drawable + private T loadComponentSingleFile(T component, Action loadCompleteAction, bool cache = false) + where T : class { if (cache) dependencies.CacheAs(component); + var drawableComponent = component as Drawable; + Debug.Assert(drawableComponent != null); + if (component is OsuFocusedOverlayContainer overlay) focusedOverlays.Add(overlay); @@ -1011,7 +1014,7 @@ namespace osu.Game // Since this is running in a separate thread, it is possible for OsuGame to be disposed after LoadComponentAsync has been called // throwing an exception. To avoid this, the call is scheduled on the update thread, which does not run if IsDisposed = true Task task = null; - var del = new ScheduledDelegate(() => task = LoadComponentAsync(component, loadCompleteAction)); + var del = new ScheduledDelegate(() => task = LoadComponentAsync(drawableComponent, loadCompleteAction)); Scheduler.Add(del); // The delegate won't complete if OsuGame has been disposed in the meantime From fea4632e839207ae1b183878aff42a437e271fa6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 20:04:19 +0900 Subject: [PATCH 081/134] Convert assert to hard throw --- osu.Game/OsuGame.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f8bc32431c..e10da39055 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -988,8 +988,7 @@ namespace osu.Game if (cache) dependencies.CacheAs(component); - var drawableComponent = component as Drawable; - Debug.Assert(drawableComponent != null); + var drawableComponent = component as Drawable ?? throw new ArgumentException($"Component must be a {nameof(Drawable)}", nameof(component)); if (component is OsuFocusedOverlayContainer overlay) focusedOverlays.Add(overlay); From d86e3abf2c5d4857062cf23d680fa40f94006a0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 20:04:38 +0900 Subject: [PATCH 082/134] Fix invalid rename --- osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index 17234f4022..405461eec8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneIDialogOverlay : OsuTestScene + public class TestSceneDialogOverlay : OsuTestScene { private DialogOverlay overlay; From 72a33c0926dde589b5dc1c2ffabdcf5184bdb474 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 20:06:08 +0900 Subject: [PATCH 083/134] Split out step for dialog overlay load wait --- .../Visual/Navigation/TestScenePerformFromScreen.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index c4099b55c7..559e6ddd89 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("did not perform", () => !actionPerformed); AddAssert("only one exit attempt", () => blocker.ExitAttempts == 1); - AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + waitForDialogOverlayLoad(); if (confirmed) { @@ -146,7 +146,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + waitForDialogOverlayLoad(); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == blocker2); AddAssert("did not perform", () => !actionPerformed); @@ -188,7 +188,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + waitForDialogOverlayLoad(); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); @@ -212,6 +212,8 @@ namespace osu.Game.Tests.Visual.Navigation } } + private void waitForDialogOverlayLoad() => AddUntilStep("wait for dialog overlay loaded", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + private void importAndWaitForSongSelect() { AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); From 043599081bc29f81fa6888d48d214d1cc505edb3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:59:57 +0900 Subject: [PATCH 084/134] Split out `INotificationOverlay` to allow for easier testing --- .../Security/ElevatedPrivilegesChecker.cs | 2 +- osu.Desktop/Updater/SquirrelUpdateManager.cs | 2 +- osu.Game.Tests/Visual/Menus/IntroTestScene.cs | 2 +- .../Visual/Navigation/TestSceneOsuGame.cs | 2 +- .../Visual/Online/TestSceneMessageNotifier.cs | 2 +- osu.Game/Database/EFToRealmMigrator.cs | 2 +- osu.Game/Graphics/ScreenshotManager.cs | 2 +- osu.Game/Online/Chat/MessageNotifier.cs | 4 +-- osu.Game/OsuGame.cs | 2 +- .../BeatmapSet/Buttons/FavouriteButton.cs | 2 +- osu.Game/Overlays/INotificationOverlay.cs | 26 +++++++++++++++++++ osu.Game/Overlays/NotificationOverlay.cs | 6 +---- .../Sections/General/UpdateSettings.cs | 2 +- .../Maintenance/MigrationRunScreen.cs | 2 +- osu.Game/PerformFromMenuRunner.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 4 +-- osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- osu.Game/Updater/UpdateManager.cs | 4 +-- 22 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 osu.Game/Overlays/INotificationOverlay.cs diff --git a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs index 8f3ad853dc..ba37a14442 100644 --- a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs +++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs @@ -19,7 +19,7 @@ namespace osu.Desktop.Security public class ElevatedPrivilegesChecker : Component { [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } private bool elevated; diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index b307146b10..bae850fce1 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -25,7 +25,7 @@ namespace osu.Desktop.Updater public class SquirrelUpdateManager : osu.Game.Updater.UpdateManager { private UpdateManager updateManager; - private NotificationOverlay notificationOverlay; + private INotificationOverlay notificationOverlay; public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited(); diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs index 82accceb23..c68cd39c65 100644 --- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs +++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Menus private IntroScreen intro; - [Cached] + [Cached(typeof(INotificationOverlay))] private NotificationOverlay notifications; private ScheduledDelegate trackResetDelegate; diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index 921d088fc7..0f8337deb6 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Navigation typeof(OsuLogo), typeof(IdleTracker), typeof(OnScreenDisplay), - typeof(NotificationOverlay), + typeof(INotificationOverlay), typeof(BeatmapListingOverlay), typeof(DashboardOverlay), typeof(NewsOverlay), diff --git a/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs b/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs index 2c253650d5..79f62a16e3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs @@ -200,7 +200,7 @@ namespace osu.Game.Tests.Visual.Online [Cached] public ChannelManager ChannelManager { get; } = new ChannelManager(); - [Cached] + [Cached(typeof(INotificationOverlay))] public NotificationOverlay NotificationOverlay { get; } = new NotificationOverlay { Anchor = Anchor.TopRight, diff --git a/osu.Game/Database/EFToRealmMigrator.cs b/osu.Game/Database/EFToRealmMigrator.cs index ae73e13b77..4e98b7d3d2 100644 --- a/osu.Game/Database/EFToRealmMigrator.cs +++ b/osu.Game/Database/EFToRealmMigrator.cs @@ -52,7 +52,7 @@ namespace osu.Game.Database private OsuConfigManager config { get; set; } = null!; [Resolved] - private NotificationOverlay notificationOverlay { get; set; } = null!; + private INotificationOverlay notificationOverlay { get; set; } = null!; [Resolved] private OsuGame game { get; set; } = null!; diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index b0f20de685..a2f1a3d7b9 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -43,7 +43,7 @@ namespace osu.Game.Graphics private Storage storage; [Resolved] - private NotificationOverlay notificationOverlay { get; set; } + private INotificationOverlay notificationOverlay { get; set; } private Sample shutter; diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index 20c33db61f..ca6082e19b 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -24,7 +24,7 @@ namespace osu.Game.Online.Chat public class MessageNotifier : Component { [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved] private ChatOverlay chatOverlay { get; set; } @@ -170,7 +170,7 @@ namespace osu.Game.Online.Chat public override bool IsImportant => false; [BackgroundDependencyLoader] - private void load(OsuColour colours, ChatOverlay chatOverlay, NotificationOverlay notificationOverlay) + private void load(OsuColour colours, ChatOverlay chatOverlay, INotificationOverlay notificationOverlay) { IconBackground.Colour = colours.PurpleDark; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e10da39055..45d76cc15b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -778,7 +778,7 @@ namespace osu.Game loadComponentSingleFile(onScreenDisplay, Add, true); - loadComponentSingleFile(Notifications.With(d => + loadComponentSingleFile(Notifications.With(d => { d.Anchor = Anchor.TopRight; d.Origin = Anchor.TopRight; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index 8fe7450873..28100e5fff 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons } [BackgroundDependencyLoader(true)] - private void load(IAPIProvider api, NotificationOverlay notifications) + private void load(IAPIProvider api, INotificationOverlay notifications) { SpriteIcon icon; diff --git a/osu.Game/Overlays/INotificationOverlay.cs b/osu.Game/Overlays/INotificationOverlay.cs new file mode 100644 index 0000000000..432dd2be54 --- /dev/null +++ b/osu.Game/Overlays/INotificationOverlay.cs @@ -0,0 +1,26 @@ +// 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.Game.Overlays.Notifications; + +namespace osu.Game.Overlays +{ + /// + /// An overlay which is capable of showing notifications to the user. + /// + [Cached] + public interface INotificationOverlay + { + /// + /// Post a new notification for display. + /// + /// The notification to display. + void Post(Notification notification); + + /// + /// Hide the overlay, if it is currently visible. + /// + void Hide(); + } +} diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index e4e3931048..0e8fbc6680 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -19,7 +19,7 @@ using osu.Game.Localisation; namespace osu.Game.Overlays { - public class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent + public class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent, INotificationOverlay { public string IconTexture => "Icons/Hexacons/notification"; public LocalisableString Title => NotificationsStrings.HeaderTitle; @@ -111,10 +111,6 @@ namespace osu.Game.Overlays private double? lastSamplePlayback; - /// - /// Post a new notification for display. - /// - /// The notification to display. public void Post(Notification notification) => postScheduler.Add(() => { ++runningDepth; diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 0b4eca6379..5bc88b8692 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.General private SettingsButton checkForUpdatesButton; [Resolved(CanBeNull = true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [BackgroundDependencyLoader(true)] private void load(Storage storage, OsuConfigManager config, OsuGame game) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs index fb7ff0dbd1..10d6f0aef5 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private OsuGame game { get; set; } [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved] private Storage storage { get; set; } diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 7039d5379c..ae9879fb5a 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -23,7 +23,7 @@ namespace osu.Game private readonly Func getCurrentScreen; [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved] private IDialogOverlay dialogOverlay { get; set; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 255571dfdb..602b7563ac 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Edit private IDialogOverlay dialogOverlay { get; set; } [Resolved(canBeNull: true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } public readonly Bindable Mode = new Bindable(); diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 8eeb90a3fd..598c441042 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -118,7 +118,7 @@ namespace osu.Game.Screens.Menu private IAPIProvider api { get; set; } [Resolved(CanBeNull = true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved(CanBeNull = true)] private LoginOverlay loginOverlay { get; set; } diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index e569b0e517..5575d3681f 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -171,7 +171,7 @@ namespace osu.Game.Screens.Menu } [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } private void ensureEventuallyArrivingAtMenu() { diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 628452fbc8..676a08b434 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader(true)] - private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) + private void load(OsuConfigManager config, INotificationOverlay notificationOverlay) { if (drawableRuleset != null) { diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index ba720af2a1..b72ec373d7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -515,7 +515,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audioManager, NotificationOverlay notificationOverlay, VolumeOverlay volumeOverlay) + private void load(OsuColour colours, AudioManager audioManager, INotificationOverlay notificationOverlay, VolumeOverlay volumeOverlay) { Icon = FontAwesome.Solid.VolumeMute; IconBackground.Colour = colours.RedDark; @@ -567,7 +567,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(OsuColour colours, NotificationOverlay notificationOverlay) + private void load(OsuColour colours, INotificationOverlay notificationOverlay) { Icon = FontAwesome.Solid.BatteryQuarter; IconBackground.Colour = colours.RedDark; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 593436bbb7..4a991ffc66 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Select private OsuScreen playerLoader; [Resolved(CanBeNull = true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } public override bool AllowExternalScreenChange => true; diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 9b9f354d23..c17d8304b9 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -31,7 +31,7 @@ namespace osu.Game.Updater private OsuGameBase game { get; set; } [Resolved] - protected NotificationOverlay Notifications { get; private set; } + protected INotificationOverlay Notifications { get; private set; } protected override void LoadComplete() { @@ -94,7 +94,7 @@ namespace osu.Game.Updater } [BackgroundDependencyLoader] - private void load(OsuColour colours, ChangelogOverlay changelog, NotificationOverlay notificationOverlay) + private void load(OsuColour colours, ChangelogOverlay changelog, INotificationOverlay notificationOverlay) { Icon = FontAwesome.Solid.CheckSquare; IconBackground.Colour = colours.BlueDark; From d29312e0709eb11e63effe51fb6c806966e3689d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 21:47:47 +0900 Subject: [PATCH 085/134] Fix rider incompetency --- osu.Game/Overlays/IDialogOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs index 07f28a7204..dedc164510 100644 --- a/osu.Game/Overlays/IDialogOverlay.cs +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -1,7 +1,8 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using osu.Framework.Allocation; using osu.Game.Overlays.Dialog; From aaf5577e6a035606f19e3fd249b7a4f464f35a13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 21:48:46 +0900 Subject: [PATCH 086/134] Remove unnecessary `canBeNull` specification --- .../Sections/Maintenance/StableDirectoryLocationDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 4e81cd63b8..b16fd9a5a1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class StableDirectoryLocationDialog : PopupDialog { - [Resolved(canBeNull: true)] + [Resolved] private IPerformFromScreenRunner performer { get; set; } public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) From 5d5e46ede71bd05ac9846da73eb03119f96cad7b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 21:47:47 +0900 Subject: [PATCH 087/134] Fix rider incompetency --- osu.Game/Overlays/IDialogOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs index 07f28a7204..dedc164510 100644 --- a/osu.Game/Overlays/IDialogOverlay.cs +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -1,7 +1,8 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using osu.Framework.Allocation; using osu.Game.Overlays.Dialog; From 3cbc6cd29767a7d47ba624dc1680c22f4a02cedd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 21:04:22 +0300 Subject: [PATCH 088/134] Update further tests to cache using `IDialogOverlay` --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 2 +- osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs | 2 +- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs | 2 +- .../Visual/SongSelect/TestSceneUserTopScoreContainer.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index d077868175..6818147da4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online Dependencies.Cache(chatManager); Dependencies.Cache(new ChatOverlay()); - Dependencies.Cache(dialogOverlay); + Dependencies.CacheAs(dialogOverlay); } [SetUp] diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index f9c9b2a68b..2106043544 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Settings Depth = -1 }); - Dependencies.Cache(dialogOverlay); + Dependencies.CacheAs(dialogOverlay); } } } diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 1ed6648131..3b15ee9c45 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.SongSelect { private readonly FailableLeaderboard leaderboard; - [Cached] + [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay; private ScoreManager scoreManager; diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs index dd7f9951bf..c71e54e9a8 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneUserTopScoreContainer : OsuTestScene { - [Cached] + [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay; public TestSceneUserTopScoreContainer() From 16a0efc5621ba8a751f874bb8fef91218766efa2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 21:06:27 +0300 Subject: [PATCH 089/134] Cache `IDialogOverlay` as its type in interface --- osu.Game/Overlays/IDialogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs index dedc164510..1c6a84cd64 100644 --- a/osu.Game/Overlays/IDialogOverlay.cs +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays /// /// A global overlay that can show popup dialogs. /// - [Cached] + [Cached(typeof(IDialogOverlay))] public interface IDialogOverlay { /// From 1fc28552b54400ca6e49f40ad5e40f4c88d3bd6e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 21:07:20 +0300 Subject: [PATCH 090/134] Minor reword inline comment --- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 9fd1a32e84..d08b6b7beb 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -216,7 +216,7 @@ namespace osu.Game.Overlays.Dialog }; // It's important we start in a visible state so our state fires on hide, even before load. - // This is used by the IDialogOverlay to know when the dialog was dismissed. + // This is used by the dialog overlay to know when the dialog was dismissed. Show(); } From 9d59cd408fda8b931d77f9001ac38a31a4a91fb7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 22:59:49 +0300 Subject: [PATCH 091/134] Add concept of "initial items count" in paginated API requests --- .../Online/API/Requests/GetUserBeatmapsRequest.cs | 4 ++-- .../API/Requests/GetUserKudosuHistoryRequest.cs | 4 ++-- .../Requests/GetUserMostPlayedBeatmapsRequest.cs | 4 ++-- .../API/Requests/GetUserRecentActivitiesRequest.cs | 4 ++-- .../Online/API/Requests/GetUserScoresRequest.cs | 4 ++-- osu.Game/Online/API/Requests/PaginatedAPIRequest.cs | 13 ++++++++++--- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index e2c0ed4301..b7681cb71b 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -13,8 +13,8 @@ namespace osu.Game.Online.API.Requests private readonly BeatmapSetType type; - public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int page = 0, int itemsPerPage = 6) - : base(page, itemsPerPage) + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int page, int itemsPerPage, int initialItems) + : base(page, itemsPerPage, initialItems) { this.userId = userId; this.type = type; diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs index e90e297672..d2102d7697 100644 --- a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserKudosuHistoryRequest(long userId, int page = 0, int itemsPerPage = 5) - : base(page, itemsPerPage) + public GetUserKudosuHistoryRequest(long userId, int page, int itemsPerPage, int initialItems) + : base(page, itemsPerPage, initialItems) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index 9f094e51c4..a9b6dd7edc 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserMostPlayedBeatmapsRequest(long userId, int page = 0, int itemsPerPage = 5) - : base(page, itemsPerPage) + public GetUserMostPlayedBeatmapsRequest(long userId, int page, int itemsPerPage, int initialItems) + : base(page, itemsPerPage, initialItems) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs index f2fa51bde7..60acc27182 100644 --- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserRecentActivitiesRequest(long userId, int page = 0, int itemsPerPage = 5) - : base(page, itemsPerPage) + public GetUserRecentActivitiesRequest(long userId, int page, int itemsPerPage, int initialItems) + : base(page, itemsPerPage, initialItems) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index 5d39799f6b..85b58fc596 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -14,8 +14,8 @@ namespace osu.Game.Online.API.Requests private readonly ScoreType type; private readonly RulesetInfo ruleset; - public GetUserScoresRequest(long userId, ScoreType type, int page = 0, int itemsPerPage = 5, RulesetInfo ruleset = null) - : base(page, itemsPerPage) + public GetUserScoresRequest(long userId, ScoreType type, int page, int itemsPerPage, int initialItems, RulesetInfo ruleset = null) + : base(page, itemsPerPage, initialItems) { this.userId = userId; this.type = type; diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs index bddc34a0dc..ea59fcfc25 100644 --- a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs +++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs @@ -9,11 +9,13 @@ namespace osu.Game.Online.API.Requests public abstract class PaginatedAPIRequest : APIRequest where T : class { private readonly int page; + private readonly int initialItems; private readonly int itemsPerPage; - protected PaginatedAPIRequest(int page, int itemsPerPage) + protected PaginatedAPIRequest(int page, int itemsPerPage, int initialItems) { this.page = page; + this.initialItems = initialItems; this.itemsPerPage = itemsPerPage; } @@ -21,8 +23,13 @@ namespace osu.Game.Online.API.Requests { var req = base.CreateWebRequest(); - req.AddParameter("offset", (page * itemsPerPage).ToString(CultureInfo.InvariantCulture)); - req.AddParameter("limit", itemsPerPage.ToString(CultureInfo.InvariantCulture)); + if (page == 0) + req.AddParameter("limit", initialItems.ToString(CultureInfo.InvariantCulture)); + else + { + req.AddParameter("offset", (initialItems + (page - 1) * itemsPerPage).ToString(CultureInfo.InvariantCulture)); + req.AddParameter("limit", itemsPerPage.ToString(CultureInfo.InvariantCulture)); + } return req; } From f08449e432d11625ec3b3fa3ad5d95df1ef091c8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 23:04:21 +0300 Subject: [PATCH 092/134] Update paginated profile subsections to display items inline with web --- .../Beatmaps/PaginatedBeatmapContainer.cs | 7 ++++--- .../PaginatedMostPlayedBeatmapContainer.cs | 5 ++--- .../Kudosu/PaginatedKudosuHistoryContainer.cs | 5 ++--- .../Sections/PaginatedProfileSubsection.cs | 19 +++++++++++++++---- .../Sections/Ranks/PaginatedScoreContainer.cs | 6 ++---- .../PaginatedRecentActivityContainer.cs | 5 ++--- 6 files changed, 27 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index d39074bd49..deeb63b5bf 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -20,11 +20,12 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps private const float panel_padding = 10f; private readonly BeatmapSetType type; + protected override int InitialItemsCount => type == BeatmapSetType.Graveyard ? 2 : 6; + public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, LocalisableString headerText) : base(user, headerText) { this.type = type; - ItemsPerPage = 6; } [BackgroundDependencyLoader] @@ -57,8 +58,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps } } - protected override APIRequest> CreateRequest() => - new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); + protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => + new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, itemsPerPage, initialItems); protected override Drawable CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0 ? new BeatmapCardNormal(model) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index ad1192a13a..ccd60fcd89 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -19,7 +19,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical public PaginatedMostPlayedBeatmapContainer(Bindable user) : base(user, UsersStrings.ShowExtraHistoricalMostPlayedTitle) { - ItemsPerPage = 5; } [BackgroundDependencyLoader] @@ -30,8 +29,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override int GetCount(APIUser user) => user.BeatmapPlayCountsCount; - protected override APIRequest> CreateRequest() => - new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); + protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => + new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap mostPlayed) => new DrawableMostPlayedBeatmap(mostPlayed); diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs index c4837cc0e2..311a64a10d 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -17,11 +17,10 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu public PaginatedKudosuHistoryContainer(Bindable user) : base(user, missingText: UsersStrings.ShowExtraKudosuEntryEmpty) { - ItemsPerPage = 5; } - protected override APIRequest> CreateRequest() - => new GetUserKudosuHistoryRequest(User.Value.Id, VisiblePages++, ItemsPerPage); + protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) + => new GetUserKudosuHistoryRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); protected override Drawable CreateDrawableItem(APIKudosuHistory item) => new DrawableKudosuHistoryItem(item); } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index 9dcbf6142d..646c16fbd7 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -21,11 +21,20 @@ namespace osu.Game.Overlays.Profile.Sections { public abstract class PaginatedProfileSubsection : ProfileSubsection { + /// + /// The number of items displayed per page. + /// + protected virtual int ItemsPerPage => 50; + + /// + /// The number of items displayed initially. + /// + protected virtual int InitialItemsCount => 5; + [Resolved] private IAPIProvider api { get; set; } protected int VisiblePages; - protected int ItemsPerPage; protected ReverseChildIDFillFlowContainer ItemsContainer { get; private set; } @@ -101,7 +110,7 @@ namespace osu.Game.Overlays.Profile.Sections { loadCancellation = new CancellationTokenSource(); - retrievalRequest = CreateRequest(); + retrievalRequest = CreateRequest(ItemsPerPage, InitialItemsCount); retrievalRequest.Success += UpdateItems; api.Queue(retrievalRequest); @@ -125,7 +134,9 @@ namespace osu.Game.Overlays.Profile.Sections LoadComponentsAsync(items.Select(CreateDrawableItem).Where(d => d != null), drawables => { missing.Hide(); - moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); + + int maxCount = VisiblePages == 1 ? InitialItemsCount : ItemsPerPage; + moreButton.FadeTo(items.Count == maxCount ? 1 : 0); moreButton.IsLoading = false; ItemsContainer.AddRange(drawables); @@ -138,7 +149,7 @@ namespace osu.Game.Overlays.Profile.Sections { } - protected abstract APIRequest> CreateRequest(); + protected abstract APIRequest> CreateRequest(int itemsPerPage, int initialItems); protected abstract Drawable CreateDrawableItem(TModel model); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 5c67da1911..cc5952017d 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -23,8 +23,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks : base(user, headerText) { this.type = type; - - ItemsPerPage = 5; } [BackgroundDependencyLoader] @@ -62,8 +60,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks base.OnItemsReceived(items); } - protected override APIRequest> CreateRequest() => - new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); + protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => + new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, itemsPerPage, initialItems); private int drawableItemIndex; diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index c5ff896654..60cba95fda 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -19,7 +19,6 @@ namespace osu.Game.Overlays.Profile.Sections.Recent public PaginatedRecentActivityContainer(Bindable user) : base(user, missingText: EventsStrings.Empty) { - ItemsPerPage = 10; } [BackgroundDependencyLoader] @@ -28,8 +27,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent ItemsContainer.Spacing = new Vector2(0, 8); } - protected override APIRequest> CreateRequest() => - new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, ItemsPerPage); + protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => + new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); protected override Drawable CreateDrawableItem(APIRecentActivity model) => new DrawableRecentActivity(model); } From c4d89b8c3c446d6440b023c2fb5508da4c99add6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 05:57:03 +0900 Subject: [PATCH 093/134] 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 a8952df329..8b22df418f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3246255815..0a102510e7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 668d23f82e..2f0facea18 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 94335c293848a5475aa0df3f8c71f6726a544ec8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 00:18:10 +0300 Subject: [PATCH 094/134] Update further usages to cache/resolve via interface --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 4 ++-- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index bae850fce1..c09cce1235 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -39,9 +39,9 @@ namespace osu.Desktop.Updater private readonly SquirrelLogger squirrelLogger = new SquirrelLogger(); [BackgroundDependencyLoader] - private void load(NotificationOverlay notification) + private void load(INotificationOverlay notifications) { - notificationOverlay = notification; + notificationOverlay = notifications; SquirrelLocator.CurrentMutable.Register(() => squirrelLogger, typeof(ILogger)); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 958d617d63..950c755cc1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private SessionStatics sessionStatics { get; set; } - [Cached] + [Cached(typeof(INotificationOverlay))] private readonly NotificationOverlay notificationOverlay; [Cached] diff --git a/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs b/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs index a68090504d..ac0956502e 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Settings { public class TestSceneMigrationScreens : ScreenTestScene { - [Cached] + [Cached(typeof(INotificationOverlay))] private readonly NotificationOverlay notifications; public TestSceneMigrationScreens() diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index b72ec373d7..aea39bc8ff 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Play private EpilepsyWarning? epilepsyWarning; [Resolved(CanBeNull = true)] - private NotificationOverlay? notificationOverlay { get; set; } + private INotificationOverlay? notificationOverlay { get; set; } [Resolved(CanBeNull = true)] private VolumeOverlay? volumeOverlay { get; set; } From 96d4369cc823d71370f1c2fa9f8faaecba66f828 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 02:04:23 +0300 Subject: [PATCH 095/134] Introduce `Pagination` and simplify paginated API requests --- .../API/Requests/GetUserBeatmapsRequest.cs | 4 +- .../Requests/GetUserKudosuHistoryRequest.cs | 4 +- .../GetUserMostPlayedBeatmapsRequest.cs | 4 +- .../GetUserRecentActivitiesRequest.cs | 4 +- .../API/Requests/GetUserScoresRequest.cs | 4 +- .../API/Requests/PaginatedAPIRequest.cs | 19 +++------- osu.Game/Online/API/Requests/Pagination.cs | 38 +++++++++++++++++++ .../Beatmaps/PaginatedBeatmapContainer.cs | 4 +- .../PaginatedMostPlayedBeatmapContainer.cs | 4 +- .../Kudosu/PaginatedKudosuHistoryContainer.cs | 4 +- .../Sections/PaginatedProfileSubsection.cs | 16 ++++---- .../Sections/Ranks/PaginatedScoreContainer.cs | 6 +-- .../PaginatedRecentActivityContainer.cs | 4 +- 13 files changed, 73 insertions(+), 42 deletions(-) create mode 100644 osu.Game/Online/API/Requests/Pagination.cs diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index b7681cb71b..019c805c9a 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -13,8 +13,8 @@ namespace osu.Game.Online.API.Requests private readonly BeatmapSetType type; - public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int page, int itemsPerPage, int initialItems) - : base(page, itemsPerPage, initialItems) + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, Pagination pagination) + : base(pagination) { this.userId = userId; this.type = type; diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs index d2102d7697..3759c59722 100644 --- a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserKudosuHistoryRequest(long userId, int page, int itemsPerPage, int initialItems) - : base(page, itemsPerPage, initialItems) + public GetUserKudosuHistoryRequest(long userId, Pagination pagination) + : base(pagination) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index a9b6dd7edc..37359e2cd0 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserMostPlayedBeatmapsRequest(long userId, int page, int itemsPerPage, int initialItems) - : base(page, itemsPerPage, initialItems) + public GetUserMostPlayedBeatmapsRequest(long userId, Pagination pagination) + : base(pagination) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs index 60acc27182..c16633c17a 100644 --- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserRecentActivitiesRequest(long userId, int page, int itemsPerPage, int initialItems) - : base(page, itemsPerPage, initialItems) + public GetUserRecentActivitiesRequest(long userId, Pagination pagination) + : base(pagination) { this.userId = userId; } diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index 85b58fc596..af01969a1a 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -14,8 +14,8 @@ namespace osu.Game.Online.API.Requests private readonly ScoreType type; private readonly RulesetInfo ruleset; - public GetUserScoresRequest(long userId, ScoreType type, int page, int itemsPerPage, int initialItems, RulesetInfo ruleset = null) - : base(page, itemsPerPage, initialItems) + public GetUserScoresRequest(long userId, ScoreType type, Pagination pagination, RulesetInfo ruleset = null) + : base(pagination) { this.userId = userId; this.type = type; diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs index ea59fcfc25..ddc453bf17 100644 --- a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs +++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs @@ -8,28 +8,19 @@ namespace osu.Game.Online.API.Requests { public abstract class PaginatedAPIRequest : APIRequest where T : class { - private readonly int page; - private readonly int initialItems; - private readonly int itemsPerPage; + private readonly Pagination pagination; - protected PaginatedAPIRequest(int page, int itemsPerPage, int initialItems) + protected PaginatedAPIRequest(Pagination pagination) { - this.page = page; - this.initialItems = initialItems; - this.itemsPerPage = itemsPerPage; + this.pagination = pagination; } protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); - if (page == 0) - req.AddParameter("limit", initialItems.ToString(CultureInfo.InvariantCulture)); - else - { - req.AddParameter("offset", (initialItems + (page - 1) * itemsPerPage).ToString(CultureInfo.InvariantCulture)); - req.AddParameter("limit", itemsPerPage.ToString(CultureInfo.InvariantCulture)); - } + req.AddParameter("offset", pagination.Offset.ToString(CultureInfo.InvariantCulture)); + req.AddParameter("limit", pagination.Limit.ToString(CultureInfo.InvariantCulture)); return req; } diff --git a/osu.Game/Online/API/Requests/Pagination.cs b/osu.Game/Online/API/Requests/Pagination.cs new file mode 100644 index 0000000000..a1caac7592 --- /dev/null +++ b/osu.Game/Online/API/Requests/Pagination.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online.API.Requests +{ + /// + /// Represents a pagination data used for . + /// + public readonly struct Pagination + { + /// + /// The starting point of the request. + /// + public int Offset { get; } + + /// + /// The maximum number of items to return in this request. + /// + public int Limit { get; } + + public Pagination(int offset, int limit) + { + Offset = offset; + Limit = limit; + } + + public Pagination(int limit) + : this(0, limit) + { + } + + /// + /// Returns a of the next number of items defined by after this. + /// + /// The limit of the next pagination. + public Pagination TakeNext(int limit) => new Pagination(Offset + Limit, limit); + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index deeb63b5bf..f1c2b4bf7a 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -58,8 +58,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps } } - protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => - new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, itemsPerPage, initialItems); + protected override APIRequest> CreateRequest(Pagination pagination) => + new GetUserBeatmapsRequest(User.Value.Id, type, pagination); protected override Drawable CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0 ? new BeatmapCardNormal(model) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index ccd60fcd89..281bcc3a03 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -29,8 +29,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override int GetCount(APIUser user) => user.BeatmapPlayCountsCount; - protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => - new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); + protected override APIRequest> CreateRequest(Pagination pagination) => + new GetUserMostPlayedBeatmapsRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap mostPlayed) => new DrawableMostPlayedBeatmap(mostPlayed); diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs index 311a64a10d..5aada2b655 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -19,8 +19,8 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { } - protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) - => new GetUserKudosuHistoryRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); + protected override APIRequest> CreateRequest(Pagination pagination) + => new GetUserKudosuHistoryRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIKudosuHistory item) => new DrawableKudosuHistoryItem(item); } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index 646c16fbd7..a1f9e7546a 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Graphics.Containers; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osuTK; @@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Profile.Sections [Resolved] private IAPIProvider api { get; set; } - protected int VisiblePages; + protected Pagination? CurrentPage { get; private set; } protected ReverseChildIDFillFlowContainer ItemsContainer { get; private set; } @@ -96,7 +97,7 @@ namespace osu.Game.Overlays.Profile.Sections loadCancellation?.Cancel(); retrievalRequest?.Cancel(); - VisiblePages = 0; + CurrentPage = null; ItemsContainer.Clear(); if (e.NewValue != null) @@ -110,7 +111,9 @@ namespace osu.Game.Overlays.Profile.Sections { loadCancellation = new CancellationTokenSource(); - retrievalRequest = CreateRequest(ItemsPerPage, InitialItemsCount); + CurrentPage = CurrentPage?.TakeNext(ItemsPerPage) ?? new Pagination(InitialItemsCount); + + retrievalRequest = CreateRequest(CurrentPage.Value); retrievalRequest.Success += UpdateItems; api.Queue(retrievalRequest); @@ -120,7 +123,7 @@ namespace osu.Game.Overlays.Profile.Sections { OnItemsReceived(items); - if (!items.Any() && VisiblePages == 1) + if (!items.Any() && CurrentPage?.Offset == 0) { moreButton.Hide(); moreButton.IsLoading = false; @@ -135,8 +138,7 @@ namespace osu.Game.Overlays.Profile.Sections { missing.Hide(); - int maxCount = VisiblePages == 1 ? InitialItemsCount : ItemsPerPage; - moreButton.FadeTo(items.Count == maxCount ? 1 : 0); + moreButton.FadeTo(items.Count == CurrentPage?.Limit ? 1 : 0); moreButton.IsLoading = false; ItemsContainer.AddRange(drawables); @@ -149,7 +151,7 @@ namespace osu.Game.Overlays.Profile.Sections { } - protected abstract APIRequest> CreateRequest(int itemsPerPage, int initialItems); + protected abstract APIRequest> CreateRequest(Pagination pagination); protected abstract Drawable CreateDrawableItem(TModel model); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index cc5952017d..c3a5e3f8b8 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -54,14 +54,14 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks protected override void OnItemsReceived(List items) { - if (VisiblePages == 0) + if (CurrentPage == null || CurrentPage?.Offset == 0) drawableItemIndex = 0; base.OnItemsReceived(items); } - protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => - new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, itemsPerPage, initialItems); + protected override APIRequest> CreateRequest(Pagination pagination) => + new GetUserScoresRequest(User.Value.Id, type, pagination); private int drawableItemIndex; diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index 60cba95fda..e5118094ce 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -27,8 +27,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent ItemsContainer.Spacing = new Vector2(0, 8); } - protected override APIRequest> CreateRequest(int itemsPerPage, int initialItems) => - new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++, itemsPerPage, initialItems); + protected override APIRequest> CreateRequest(Pagination pagination) => + new GetUserRecentActivitiesRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIRecentActivity model) => new DrawableRecentActivity(model); } From c858ec24833b2020a434180d9f63a8a472ec7173 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 02:48:34 +0300 Subject: [PATCH 096/134] `Pagination` -> `PaginationParameters --- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 2 +- .../Online/API/Requests/GetUserKudosuHistoryRequest.cs | 2 +- .../API/Requests/GetUserMostPlayedBeatmapsRequest.cs | 2 +- .../API/Requests/GetUserRecentActivitiesRequest.cs | 2 +- osu.Game/Online/API/Requests/GetUserScoresRequest.cs | 2 +- osu.Game/Online/API/Requests/PaginatedAPIRequest.cs | 4 ++-- .../{Pagination.cs => PaginationParameters.cs} | 10 +++++----- .../Sections/Beatmaps/PaginatedBeatmapContainer.cs | 2 +- .../Historical/PaginatedMostPlayedBeatmapContainer.cs | 2 +- .../Sections/Kudosu/PaginatedKudosuHistoryContainer.cs | 2 +- .../Profile/Sections/PaginatedProfileSubsection.cs | 6 +++--- .../Profile/Sections/Ranks/PaginatedScoreContainer.cs | 2 +- .../Recent/PaginatedRecentActivityContainer.cs | 2 +- 13 files changed, 20 insertions(+), 20 deletions(-) rename osu.Game/Online/API/Requests/{Pagination.cs => PaginationParameters.cs} (68%) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index 019c805c9a..ffb3a3be9b 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -13,7 +13,7 @@ namespace osu.Game.Online.API.Requests private readonly BeatmapSetType type; - public GetUserBeatmapsRequest(long userId, BeatmapSetType type, Pagination pagination) + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, PaginationParameters pagination) : base(pagination) { this.userId = userId; diff --git a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs index 3759c59722..67d3ad26b0 100644 --- a/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserKudosuHistoryRequest.cs @@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserKudosuHistoryRequest(long userId, Pagination pagination) + public GetUserKudosuHistoryRequest(long userId, PaginationParameters pagination) : base(pagination) { this.userId = userId; diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index 37359e2cd0..bef3df42fb 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserMostPlayedBeatmapsRequest(long userId, Pagination pagination) + public GetUserMostPlayedBeatmapsRequest(long userId, PaginationParameters pagination) : base(pagination) { this.userId = userId; diff --git a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs index c16633c17a..79f0549d4a 100644 --- a/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRecentActivitiesRequest.cs @@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests { private readonly long userId; - public GetUserRecentActivitiesRequest(long userId, Pagination pagination) + public GetUserRecentActivitiesRequest(long userId, PaginationParameters pagination) : base(pagination) { this.userId = userId; diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index af01969a1a..7250929f11 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.API.Requests private readonly ScoreType type; private readonly RulesetInfo ruleset; - public GetUserScoresRequest(long userId, ScoreType type, Pagination pagination, RulesetInfo ruleset = null) + public GetUserScoresRequest(long userId, ScoreType type, PaginationParameters pagination, RulesetInfo ruleset = null) : base(pagination) { this.userId = userId; diff --git a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs index ddc453bf17..3d719de958 100644 --- a/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs +++ b/osu.Game/Online/API/Requests/PaginatedAPIRequest.cs @@ -8,9 +8,9 @@ namespace osu.Game.Online.API.Requests { public abstract class PaginatedAPIRequest : APIRequest where T : class { - private readonly Pagination pagination; + private readonly PaginationParameters pagination; - protected PaginatedAPIRequest(Pagination pagination) + protected PaginatedAPIRequest(PaginationParameters pagination) { this.pagination = pagination; } diff --git a/osu.Game/Online/API/Requests/Pagination.cs b/osu.Game/Online/API/Requests/PaginationParameters.cs similarity index 68% rename from osu.Game/Online/API/Requests/Pagination.cs rename to osu.Game/Online/API/Requests/PaginationParameters.cs index a1caac7592..3593a4fe83 100644 --- a/osu.Game/Online/API/Requests/Pagination.cs +++ b/osu.Game/Online/API/Requests/PaginationParameters.cs @@ -6,7 +6,7 @@ namespace osu.Game.Online.API.Requests /// /// Represents a pagination data used for . /// - public readonly struct Pagination + public readonly struct PaginationParameters { /// /// The starting point of the request. @@ -18,21 +18,21 @@ namespace osu.Game.Online.API.Requests /// public int Limit { get; } - public Pagination(int offset, int limit) + public PaginationParameters(int offset, int limit) { Offset = offset; Limit = limit; } - public Pagination(int limit) + public PaginationParameters(int limit) : this(0, limit) { } /// - /// Returns a of the next number of items defined by after this. + /// Returns a of the next number of items defined by after this. /// /// The limit of the next pagination. - public Pagination TakeNext(int limit) => new Pagination(Offset + Limit, limit); + public PaginationParameters TakeNext(int limit) => new PaginationParameters(Offset + Limit, limit); } } diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index f1c2b4bf7a..0424fe8bd9 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps } } - protected override APIRequest> CreateRequest(Pagination pagination) => + protected override APIRequest> CreateRequest(PaginationParameters pagination) => new GetUserBeatmapsRequest(User.Value.Id, type, pagination); protected override Drawable CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0 diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 281bcc3a03..06de0f62dc 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override int GetCount(APIUser user) => user.BeatmapPlayCountsCount; - protected override APIRequest> CreateRequest(Pagination pagination) => + protected override APIRequest> CreateRequest(PaginationParameters pagination) => new GetUserMostPlayedBeatmapsRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap mostPlayed) => diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs index 5aada2b655..9af854e6b9 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { } - protected override APIRequest> CreateRequest(Pagination pagination) + protected override APIRequest> CreateRequest(PaginationParameters pagination) => new GetUserKudosuHistoryRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIKudosuHistory item) => new DrawableKudosuHistoryItem(item); diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index a1f9e7546a..33bd155d71 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Profile.Sections [Resolved] private IAPIProvider api { get; set; } - protected Pagination? CurrentPage { get; private set; } + protected PaginationParameters? CurrentPage { get; private set; } protected ReverseChildIDFillFlowContainer ItemsContainer { get; private set; } @@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Profile.Sections { loadCancellation = new CancellationTokenSource(); - CurrentPage = CurrentPage?.TakeNext(ItemsPerPage) ?? new Pagination(InitialItemsCount); + CurrentPage = CurrentPage?.TakeNext(ItemsPerPage) ?? new PaginationParameters(InitialItemsCount); retrievalRequest = CreateRequest(CurrentPage.Value); retrievalRequest.Success += UpdateItems; @@ -151,7 +151,7 @@ namespace osu.Game.Overlays.Profile.Sections { } - protected abstract APIRequest> CreateRequest(Pagination pagination); + protected abstract APIRequest> CreateRequest(PaginationParameters pagination); protected abstract Drawable CreateDrawableItem(TModel model); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index c3a5e3f8b8..ef9f4b5ff3 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks base.OnItemsReceived(items); } - protected override APIRequest> CreateRequest(Pagination pagination) => + protected override APIRequest> CreateRequest(PaginationParameters pagination) => new GetUserScoresRequest(User.Value.Id, type, pagination); private int drawableItemIndex; diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index e5118094ce..77008d5f34 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent ItemsContainer.Spacing = new Vector2(0, 8); } - protected override APIRequest> CreateRequest(Pagination pagination) => + protected override APIRequest> CreateRequest(PaginationParameters pagination) => new GetUserRecentActivitiesRequest(User.Value.Id, pagination); protected override Drawable CreateDrawableItem(APIRecentActivity model) => new DrawableRecentActivity(model); From 83e89b3e80c737b626fe1962d2652a64a381058e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 11:10:11 +0900 Subject: [PATCH 097/134] Add unread count to `INotificationOverlay` and fix toolbar button DI logic --- .../Visual/Menus/TestSceneToolbar.cs | 18 +++++++++++++----- osu.Game/Overlays/INotificationOverlay.cs | 6 ++++++ osu.Game/Overlays/NotificationOverlay.cs | 6 ++++-- .../Toolbar/ToolbarNotificationButton.cs | 8 ++++---- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs index c65595d82e..dbc7e54b5e 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using Moq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -22,6 +23,17 @@ namespace osu.Game.Tests.Visual.Menus [Resolved] private IRulesetStore rulesets { get; set; } + private readonly Mock notifications = new Mock(); + + private readonly BindableInt unreadNotificationCount = new BindableInt(); + + [BackgroundDependencyLoader] + private void load() + { + Dependencies.CacheAs(notifications.Object); + notifications.SetupGet(n => n.UnreadCount).Returns(unreadNotificationCount); + } + [SetUp] public void SetUp() => Schedule(() => { @@ -31,10 +43,6 @@ namespace osu.Game.Tests.Visual.Menus [Test] public void TestNotificationCounter() { - ToolbarNotificationButton notificationButton = null; - - AddStep("retrieve notification button", () => notificationButton = toolbar.ChildrenOfType().Single()); - setNotifications(1); setNotifications(2); setNotifications(3); @@ -43,7 +51,7 @@ namespace osu.Game.Tests.Visual.Menus void setNotifications(int count) => AddStep($"set notification count to {count}", - () => notificationButton.NotificationCount.Value = count); + () => unreadNotificationCount.Value = count); } [TestCase(false)] diff --git a/osu.Game/Overlays/INotificationOverlay.cs b/osu.Game/Overlays/INotificationOverlay.cs index 432dd2be54..1d8e33ea3a 100644 --- a/osu.Game/Overlays/INotificationOverlay.cs +++ b/osu.Game/Overlays/INotificationOverlay.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.Game.Overlays.Notifications; namespace osu.Game.Overlays @@ -22,5 +23,10 @@ namespace osu.Game.Overlays /// Hide the overlay, if it is currently visible. /// void Hide(); + + /// + /// Current number of unread notifications. + /// + IBindable UnreadCount { get; } } } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 0e8fbc6680..2b5909f45a 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -99,7 +99,9 @@ namespace osu.Game.Overlays OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true); } - public readonly BindableInt UnreadCount = new BindableInt(); + public IBindable UnreadCount => unreadCount; + + private readonly BindableInt unreadCount = new BindableInt(); private int runningDepth; @@ -180,7 +182,7 @@ namespace osu.Game.Overlays private void updateCounts() { - UnreadCount.Value = sections.Select(c => c.UnreadCount).Sum(); + unreadCount.Value = sections.Select(c => c.UnreadCount).Sum(); } private void markAllRead() diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs index 79d0fc74c1..313a2bc3f4 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Toolbar { protected override Anchor TooltipAnchor => Anchor.TopRight; - public BindableInt NotificationCount = new BindableInt(); + public IBindable NotificationCount = new BindableInt(); private readonly CountCircle countDisplay; @@ -36,10 +36,10 @@ namespace osu.Game.Overlays.Toolbar }); } - [BackgroundDependencyLoader(true)] - private void load(NotificationOverlay notificationOverlay) + [BackgroundDependencyLoader] + private void load(INotificationOverlay notificationOverlay) { - StateContainer = notificationOverlay; + StateContainer = notificationOverlay as NotificationOverlay; if (notificationOverlay != null) NotificationCount.BindTo(notificationOverlay.UnreadCount); From 8d0dd3961e056b2670f67412a48cfdf000ec1694 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 03:25:18 +0300 Subject: [PATCH 098/134] Add failing test cases --- .../LegacyMainCirclePieceTest.cs | 133 ++++++++++++++++++ .../Skinning/Legacy/LegacyMainCirclePiece.cs | 19 ++- 2 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs diff --git a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs new file mode 100644 index 0000000000..200dd2f593 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs @@ -0,0 +1,133 @@ +// 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.Diagnostics; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Testing; +using osu.Game.Audio; +using osu.Game.Rulesets.Osu.Skinning.Legacy; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; + +#nullable enable + +namespace osu.Game.Rulesets.Osu.Tests +{ + [HeadlessTest] + public class LegacyMainCirclePieceTest : OsuTestScene + { + private static readonly object?[][] texture_priority_cases = + { + // default priority lookup + new object?[] + { + // available textures + new[] { @"hitcircle", @"hitcircleoverlay" }, + // priority lookup + @"", + // expected circle and overlay + @"hitcircle", @"hitcircleoverlay", + }, + // custom priority lookup + new object?[] + { + new[] { @"hitcircle", @"hitcircleoverlay", @"sliderstartcircle", @"sliderstartcircleoverlay" }, + @"sliderstartcircle", + @"sliderstartcircle", @"sliderstartcircleoverlay", + }, + // when no sprites are available for the specified prefix, fall back to "hitcircle"/"hitcircleoverlay". + new object?[] + { + new[] { @"hitcircle", @"hitcircleoverlay" }, + @"sliderstartcircle", + @"hitcircle", @"hitcircleoverlay", + }, + // when a circle is available for the specified prefix but no overlay exists, no overlay is displayed. + new object?[] + { + new[] { @"hitcircle", @"hitcircleoverlay", @"sliderstartcircle" }, + @"sliderstartcircle", + @"sliderstartcircle", null + }, + // when no circle is available for the specified prefix but an overlay exists, the overlay is ignored. + new object?[] + { + new[] { @"hitcircle", @"hitcircleoverlay", @"sliderstartcircleoverlay" }, + @"sliderstartcircle", + @"hitcircle", @"hitcircleoverlay", + } + }; + + [TestCaseSource(nameof(texture_priority_cases))] + public void TestTexturePriorities(string[] textureFilenames, string priorityLookup, string? expectedCircle, string? expectedOverlay) + { + Sprite? circleSprite = null; + Sprite? overlaySprite = null; + + AddStep("load circle piece", () => + { + Child = new DependencyProvidingContainer + { + CachedDependencies = new (Type, object)[] + { + (typeof(ISkinSource), new TestSkin(textureFilenames)) + }, + Child = new LegacyMainCirclePiece(priorityLookup, false), + }; + + var sprites = this.ChildrenOfType().Where(s => s.Texture.AssetName != null).DistinctBy(s => s.Texture.AssetName).ToArray(); + Debug.Assert(sprites.Length <= 2); + + circleSprite = sprites.ElementAtOrDefault(0); + overlaySprite = sprites.ElementAtOrDefault(1); + }); + + AddAssert("check circle sprite", () => circleSprite?.Texture?.AssetName == expectedCircle); + AddAssert("check overlay sprite", () => overlaySprite?.Texture?.AssetName == expectedOverlay); + } + + private class TestSkin : ISkinSource + { + private readonly string[] textureFilenames; + + public TestSkin(string[] textureFilenames) + { + this.textureFilenames = textureFilenames; + } + + public Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) + { + if (textureFilenames.Contains(componentName)) + return new Texture(1, 1) { AssetName = componentName }; + + return null; + } + + public event Action SourceChanged + { + add { } + remove { } + } + + public IEnumerable AllSources { get; } = Enumerable.Empty(); + public Drawable? GetDrawableComponent(ISkinComponent component) => null; + public ISample? GetSample(ISampleInfo sampleInfo) => null; + + public IBindable? GetConfig(TLookup lookup) + where TLookup : notnull + where TValue : notnull + => null; + + public ISkin? FindProvider(Func lookupFunction) => lookupFunction(this) ? this : null; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index c6007885be..829dbb02f1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -43,7 +44,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly Bindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); - [Resolved] + [Resolved(canBeNull: true)] + [CanBeNull] private DrawableHitObject drawableObject { get; set; } [Resolved] @@ -107,8 +109,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (overlayAboveNumber) OverlayLayer.ChangeChildDepth(hitCircleOverlay, float.MinValue); - accentColour.BindTo(drawableObject.AccentColour); - indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); + if (drawableOsuObject != null) + { + accentColour.BindTo(drawableOsuObject.AccentColour); + indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); + } Texture getTextureWithFallback(string name) { @@ -149,15 +154,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (hasNumber) indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); - drawableObject.ApplyCustomUpdateState += updateStateTransforms; - updateStateTransforms(drawableObject, drawableObject.State.Value); + if (drawableObject != null) + drawableObject.ApplyCustomUpdateState += updateStateTransforms; + + updateStateTransforms(drawableObject, drawableObject?.State.Value ?? ArmedState.Idle); } private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { const double legacy_fade_duration = 240; - using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) + using (BeginAbsoluteSequence(drawableObject?.HitStateUpdateTime ?? 0)) { switch (state) { From ec7bb876b5f76416255fa04e71876f6f39980465 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 07:12:05 +0300 Subject: [PATCH 099/134] Improve legacy circle texture lookup to match 1:1 with stable --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 49 +++++-------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 829dbb02f1..5fe4a4b5df 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -23,9 +23,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public override bool RemoveCompletedTransforms => false; - private readonly string priorityLookup; private readonly bool hasNumber; + private string priorityLookup; + public LegacyMainCirclePiece(string priorityLookup = null, bool hasNumber = true) { this.priorityLookup = priorityLookup; @@ -56,21 +57,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { var drawableOsuObject = (DrawableOsuHitObject)drawableObject; - bool allowFallback = false; - // attempt lookup using priority specification - Texture baseTexture = getTextureWithFallback(string.Empty); + Texture baseTexture = getTexture(string.Empty); - // if the base texture was not found without a fallback, switch on fallback mode and re-perform the lookup. + // if the base texture was not found using the priority specification, nullify the specification and fall back to "hitcircle". if (baseTexture == null) { - allowFallback = true; - baseTexture = getTextureWithFallback(string.Empty); + priorityLookup = null; + baseTexture = getTexture(string.Empty); } // at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it. // the flow above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist. - // expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin). + // expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png. InternalChildren = new[] { @@ -83,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Child = hitCircleOverlay = new KiaiFlashingDrawable(() => getAnimationWithFallback(@"overlay", 1000 / 2d)) + Child = hitCircleOverlay = new KiaiFlashingDrawable(() => getAnimation(@"overlay", 1000 / 2d)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -115,35 +114,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); } - Texture getTextureWithFallback(string name) - { - Texture tex = null; + Texture getTexture(string name) + => skin.GetTexture($"{priorityLookup ?? @"hitcircle"}{name}"); - if (!string.IsNullOrEmpty(priorityLookup)) - { - tex = skin.GetTexture($"{priorityLookup}{name}"); - - if (!allowFallback) - return tex; - } - - return tex ?? skin.GetTexture($"hitcircle{name}"); - } - - Drawable getAnimationWithFallback(string name, double frameLength) - { - Drawable animation = null; - - if (!string.IsNullOrEmpty(priorityLookup)) - { - animation = skin.GetAnimation($"{priorityLookup}{name}", true, true, frameLength: frameLength); - - if (!allowFallback) - return animation; - } - - return animation ?? skin.GetAnimation($"hitcircle{name}", true, true, frameLength: frameLength); - } + Drawable getAnimation(string name, double frameLength) + => skin.GetAnimation($"{priorityLookup ?? @"hitcircle"}{name}", true, true, frameLength: frameLength); } protected override void LoadComplete() From f92a41b6c3758027e315347fafb01abbac7aa678 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 08:03:50 +0300 Subject: [PATCH 100/134] Replace local `TestSkin` implementation with simplified Moq setups --- .../LegacyMainCirclePieceTest.cs | 56 ++++--------------- 1 file changed, 10 insertions(+), 46 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs index 200dd2f593..766db25d71 100644 --- a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs @@ -1,25 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Moq; using NUnit.Framework; -using osu.Framework.Audio.Sample; -using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Testing; -using osu.Game.Audio; using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Skinning; using osu.Game.Tests.Visual; -#nullable enable - namespace osu.Game.Rulesets.Osu.Tests { [HeadlessTest] @@ -75,12 +71,15 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("load circle piece", () => { + var skin = new Mock(); + + skin.Setup(s => s.GetTexture(It.IsAny())).CallBase(); + skin.Setup(s => s.GetTexture(It.IsIn(textureFilenames), It.IsAny(), It.IsAny())) + .Returns((string componentName, WrapMode _, WrapMode __) => new Texture(1, 1) { AssetName = componentName }); + Child = new DependencyProvidingContainer { - CachedDependencies = new (Type, object)[] - { - (typeof(ISkinSource), new TestSkin(textureFilenames)) - }, + CachedDependencies = new (Type, object)[] { (typeof(ISkinSource), skin.Object) }, Child = new LegacyMainCirclePiece(priorityLookup, false), }; @@ -94,40 +93,5 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("check circle sprite", () => circleSprite?.Texture?.AssetName == expectedCircle); AddAssert("check overlay sprite", () => overlaySprite?.Texture?.AssetName == expectedOverlay); } - - private class TestSkin : ISkinSource - { - private readonly string[] textureFilenames; - - public TestSkin(string[] textureFilenames) - { - this.textureFilenames = textureFilenames; - } - - public Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) - { - if (textureFilenames.Contains(componentName)) - return new Texture(1, 1) { AssetName = componentName }; - - return null; - } - - public event Action SourceChanged - { - add { } - remove { } - } - - public IEnumerable AllSources { get; } = Enumerable.Empty(); - public Drawable? GetDrawableComponent(ISkinComponent component) => null; - public ISample? GetSample(ISampleInfo sampleInfo) => null; - - public IBindable? GetConfig(TLookup lookup) - where TLookup : notnull - where TValue : notnull - => null; - - public ISkin? FindProvider(Func lookupFunction) => lookupFunction(this) ? this : null; - } } } From fd113953ace591b867081da881821f15dd356511 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 08:06:39 +0300 Subject: [PATCH 101/134] Rename `prioritiyLookup` and add xmldoc --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 5fe4a4b5df..82bb4dcf2d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -25,11 +25,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly bool hasNumber; - private string priorityLookup; + /// + /// A prioritised prefix to perform texture lookups with. + /// + /// + /// If the "circle" texture could not be found with this prefix, + /// then it is nullified and the default prefix "hitcircle" is used instead. + /// + private string priorityLookupPrefix; - public LegacyMainCirclePiece(string priorityLookup = null, bool hasNumber = true) + public LegacyMainCirclePiece(string priorityLookupPrefix = null, bool hasNumber = true) { - this.priorityLookup = priorityLookup; + this.priorityLookupPrefix = priorityLookupPrefix; this.hasNumber = hasNumber; Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); @@ -63,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy // if the base texture was not found using the priority specification, nullify the specification and fall back to "hitcircle". if (baseTexture == null) { - priorityLookup = null; + priorityLookupPrefix = null; baseTexture = getTexture(string.Empty); } @@ -115,10 +122,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } Texture getTexture(string name) - => skin.GetTexture($"{priorityLookup ?? @"hitcircle"}{name}"); + => skin.GetTexture($"{priorityLookupPrefix ?? @"hitcircle"}{name}"); Drawable getAnimation(string name, double frameLength) - => skin.GetAnimation($"{priorityLookup ?? @"hitcircle"}{name}", true, true, frameLength: frameLength); + => skin.GetAnimation($"{priorityLookupPrefix ?? @"hitcircle"}{name}", true, true, frameLength: frameLength); } protected override void LoadComplete() From b067924adacb6ecdc38d3d9ccdf52a30b44458f7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 08:08:02 +0300 Subject: [PATCH 102/134] Avoid applying state transforms when no object is present --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 82bb4dcf2d..9d14041dd8 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -4,6 +4,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -137,16 +138,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); if (drawableObject != null) + { drawableObject.ApplyCustomUpdateState += updateStateTransforms; - - updateStateTransforms(drawableObject, drawableObject?.State.Value ?? ArmedState.Idle); + updateStateTransforms(drawableObject, drawableObject.State.Value); + } } private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { const double legacy_fade_duration = 240; - using (BeginAbsoluteSequence(drawableObject?.HitStateUpdateTime ?? 0)) + using (BeginAbsoluteSequence(drawableObject.AsNonNull().HitStateUpdateTime)) { switch (state) { From feeff1647697ef1c45156091cce88da20464caee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 14:30:45 +0900 Subject: [PATCH 103/134] Add debug language to help with localisation efforts The idea is to allow a developer to immediately see which text on a component or screen has already got localisation support. It can be a bit of a challenge to see this when creating a new component that doesn't yet have any translations populated. Curious to hear thoughts on this. I could see it working very well as a visual tests checkbox (implemented at o!f side), potentially in addition to having this at the game level, or replacing this PR. --- .../Localisation/DebugLocalisationStore.cs | 30 +++++++++++++++++++ osu.Game/Localisation/Language.cs | 7 ++++- osu.Game/OsuGame.cs | 8 +++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Localisation/DebugLocalisationStore.cs diff --git a/osu.Game/Localisation/DebugLocalisationStore.cs b/osu.Game/Localisation/DebugLocalisationStore.cs new file mode 100644 index 0000000000..99dca6265e --- /dev/null +++ b/osu.Game/Localisation/DebugLocalisationStore.cs @@ -0,0 +1,30 @@ +// 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.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public class DebugLocalisationStore : ILocalisationStore + { + public string Get(string lookup) => $@"[[{lookup.Substring(lookup.LastIndexOf('.') + 1)}]]"; + + public Task GetAsync(string lookup, CancellationToken cancellationToken = default) => Task.FromResult(Get(lookup)); + + public Stream GetStream(string name) => throw new NotImplementedException(); + + public IEnumerable GetAvailableResources() => throw new NotImplementedException(); + + public CultureInfo EffectiveCulture { get; } = new CultureInfo(@"debug"); + + public void Dispose() + { + } + } +} diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index ab96d01c82..c98586457e 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -110,6 +110,11 @@ namespace osu.Game.Localisation // zh_hk, [Description(@"繁體中文(台灣)")] - zh_hant + zh_hant, + +#if DEBUG + [Description(@"Debug (show raw keys)")] + DebugLocalisation +#endif } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index eee86de7ff..b591b09584 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -628,6 +628,14 @@ namespace osu.Game foreach (var language in Enum.GetValues(typeof(Language)).OfType()) { +#if DEBUG + if (language == Language.DebugLocalisation) + { + Localisation.AddLanguage(Language.DebugLocalisation.ToString(), new DebugLocalisationStore()); + continue; + } +#endif + string cultureCode = language.ToCultureCode(); try From a195d4f5aa5a132bf6860e23ab04f327cb84b8a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 16:49:41 +0900 Subject: [PATCH 104/134] Use a culture name that doesn't cause everything to fall over --- osu.Game/Localisation/DebugLocalisationStore.cs | 2 +- osu.Game/Localisation/Language.cs | 2 +- osu.Game/OsuGame.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/DebugLocalisationStore.cs b/osu.Game/Localisation/DebugLocalisationStore.cs index 99dca6265e..2b114b1bd8 100644 --- a/osu.Game/Localisation/DebugLocalisationStore.cs +++ b/osu.Game/Localisation/DebugLocalisationStore.cs @@ -21,7 +21,7 @@ namespace osu.Game.Localisation public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - public CultureInfo EffectiveCulture { get; } = new CultureInfo(@"debug"); + public CultureInfo EffectiveCulture { get; } = CultureInfo.CurrentCulture; public void Dispose() { diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index c98586457e..c13a1a10cb 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -114,7 +114,7 @@ namespace osu.Game.Localisation #if DEBUG [Description(@"Debug (show raw keys)")] - DebugLocalisation + debug #endif } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b591b09584..ea21954b9c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -629,9 +629,9 @@ namespace osu.Game foreach (var language in Enum.GetValues(typeof(Language)).OfType()) { #if DEBUG - if (language == Language.DebugLocalisation) + if (language == Language.debug) { - Localisation.AddLanguage(Language.DebugLocalisation.ToString(), new DebugLocalisationStore()); + Localisation.AddLanguage(Language.debug.ToString(), new DebugLocalisationStore()); continue; } #endif From 2a3a0c1cd38dbe61d4941913b18785ed3a5732a6 Mon Sep 17 00:00:00 2001 From: chickensalt Date: Tue, 19 Apr 2022 18:44:20 +1000 Subject: [PATCH 105/134] remove notification on trying to multi when logged out fixes #17877 --- osu.Game/Screens/Menu/ButtonSystem.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 598c441042..6d09b6de9f 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -161,17 +161,7 @@ namespace osu.Game.Screens.Menu { if (api.State.Value != APIState.Online) { - notifications?.Post(new SimpleNotification - { - Text = "You gotta be online to multi 'yo!", - Icon = FontAwesome.Solid.Globe, - Activated = () => - { - loginOverlay?.Show(); - return true; - } - }); - + loginOverlay?.Show(); return; } From 75a6e9fd7f7bbf82325051c89fe7eab3caa60dcc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 18:10:10 +0900 Subject: [PATCH 106/134] Convert to use `nullable` and rearrange fields --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 9d14041dd8..1fedc7daae 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -1,7 +1,7 @@ +#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; @@ -33,9 +33,25 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy /// If the "circle" texture could not be found with this prefix, /// then it is nullified and the default prefix "hitcircle" is used instead. /// - private string priorityLookupPrefix; + private string? priorityLookupPrefix; - public LegacyMainCirclePiece(string priorityLookupPrefix = null, bool hasNumber = true) + private Drawable hitCircleSprite = null!; + + protected Container OverlayLayer { get; private set; } = null!; + + private Drawable hitCircleOverlay = null!; + private SkinnableSpriteText hitCircleText = null!; + + private readonly Bindable accentColour = new Bindable(); + private readonly IBindable indexInCurrentCombo = new Bindable(); + + [Resolved(canBeNull: true)] + private DrawableHitObject? drawableObject { get; set; } + + [Resolved] + private ISkinSource skin { get; set; } = null!; + + public LegacyMainCirclePiece(string? priorityLookupPrefix = null, bool hasNumber = true) { this.priorityLookupPrefix = priorityLookupPrefix; this.hasNumber = hasNumber; @@ -43,30 +59,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); } - private Drawable hitCircleSprite; - - protected Container OverlayLayer { get; private set; } - - private Drawable hitCircleOverlay; - private SkinnableSpriteText hitCircleText; - - private readonly Bindable accentColour = new Bindable(); - private readonly IBindable indexInCurrentCombo = new Bindable(); - - [Resolved(canBeNull: true)] - [CanBeNull] - private DrawableHitObject drawableObject { get; set; } - - [Resolved] - private ISkinSource skin { get; set; } - [BackgroundDependencyLoader] private void load() { - var drawableOsuObject = (DrawableOsuHitObject)drawableObject; + var drawableOsuObject = (DrawableOsuHitObject?)drawableObject; // attempt lookup using priority specification - Texture baseTexture = getTexture(string.Empty); + Texture? baseTexture = getTexture(string.Empty); // if the base texture was not found using the priority specification, nullify the specification and fall back to "hitcircle". if (baseTexture == null) @@ -122,10 +121,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); } - Texture getTexture(string name) + Texture? getTexture(string name) => skin.GetTexture($"{priorityLookupPrefix ?? @"hitcircle"}{name}"); - Drawable getAnimation(string name, double frameLength) + Drawable? getAnimation(string name, double frameLength) => skin.GetAnimation($"{priorityLookupPrefix ?? @"hitcircle"}{name}", true, true, frameLength: frameLength); } From 44c822f34d7a332ba41381d871c42814e9a6daea Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 19 Apr 2022 16:56:55 +0100 Subject: [PATCH 107/134] Use normal `OsuScrollContainer` for `ChannelList` --- .../Overlays/Chat/ChannelList/ChannelList.cs | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 5c2a1ad89f..94c1a99a81 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Chat.ChannelList RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background6, }, - new ChannelListScrollContainer + new OsuScrollContainer { Padding = new MarginPadding { Vertical = 7 }, RelativeSizeAxes = Axes.Both, @@ -131,31 +131,5 @@ namespace osu.Game.Overlays.Chat.ChannelList }); } } - - private class ChannelListScrollContainer : OsuScrollContainer - { - protected override ScrollbarContainer CreateScrollbar(Direction direction) - => new ChannelListScrollBar(direction); - - protected class ChannelListScrollBar : OsuScrollbar - { - private const float bar_size = 4; - private const float bar_margin = 7; - - public ChannelListScrollBar(Direction scrollDir) - : base(scrollDir) - { - Size = new Vector2(bar_size); - Margin = new MarginPadding { Horizontal = bar_margin }; - CornerRadius = 2; - } - - public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None) - { - Vector2 size = new Vector2(bar_size, val); - this.ResizeTo(size, duration, easing); - } - } - } } } From 033b556be58fe1b780d52d0b6c90b75eaea23f31 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 19:39:06 +0300 Subject: [PATCH 108/134] Simplify texture lookup further --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 35 +++++-------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 1fedc7daae..2107de61b6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -8,7 +8,6 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; @@ -24,16 +23,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public override bool RemoveCompletedTransforms => false; - private readonly bool hasNumber; - /// /// A prioritised prefix to perform texture lookups with. /// - /// - /// If the "circle" texture could not be found with this prefix, - /// then it is nullified and the default prefix "hitcircle" is used instead. - /// - private string? priorityLookupPrefix; + private readonly string? priorityLookupPrefix; + + private readonly bool hasNumber; private Drawable hitCircleSprite = null!; @@ -64,23 +59,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { var drawableOsuObject = (DrawableOsuHitObject?)drawableObject; - // attempt lookup using priority specification - Texture? baseTexture = getTexture(string.Empty); - - // if the base texture was not found using the priority specification, nullify the specification and fall back to "hitcircle". - if (baseTexture == null) - { - priorityLookupPrefix = null; - baseTexture = getTexture(string.Empty); - } + // if a base texture for the specified prefix exists, continue using it for subsequent lookups. + // otherwise fall back to the default prefix "hitcircle". + string circleName = (priorityLookupPrefix != null && skin.GetTexture(priorityLookupPrefix) != null) ? priorityLookupPrefix : @"hitcircle"; // at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it. - // the flow above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist. + // the conditional above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist. // expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png. InternalChildren = new[] { - hitCircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = baseTexture }) + hitCircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) }) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -89,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Child = hitCircleOverlay = new KiaiFlashingDrawable(() => getAnimation(@"overlay", 1000 / 2d)) + Child = hitCircleOverlay = new KiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -120,12 +109,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy accentColour.BindTo(drawableOsuObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); } - - Texture? getTexture(string name) - => skin.GetTexture($"{priorityLookupPrefix ?? @"hitcircle"}{name}"); - - Drawable? getAnimation(string name, double frameLength) - => skin.GetAnimation($"{priorityLookupPrefix ?? @"hitcircle"}{name}", true, true, frameLength: frameLength); } protected override void LoadComplete() From 813dc2dd78fd0ba65bcae4caddd289be67447310 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 19:46:29 +0300 Subject: [PATCH 109/134] Fix wrong prefix for default priority lookup in test --- osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs index 766db25d71..c9eeef3c01 100644 --- a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Tests // available textures new[] { @"hitcircle", @"hitcircleoverlay" }, // priority lookup - @"", + null, // expected circle and overlay @"hitcircle", @"hitcircleoverlay", }, From a96664295bfd6cec5adc4b9c222ca47707e5f207 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 19:48:44 +0300 Subject: [PATCH 110/134] Fix nullability preprocessor placed over the copyright header --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 2107de61b6..db4d643046 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -1,4 +1,3 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. @@ -17,6 +16,8 @@ using osu.Game.Skinning; using osuTK; using osuTK.Graphics; +#nullable enable + namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacyMainCirclePiece : CompositeDrawable From fd20c2bdcd7c174b27efded6d8e371ec2233ba1f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 20 Apr 2022 00:24:18 +0300 Subject: [PATCH 111/134] Change circle/overlay sprite fields to `protected` for better test assertion --- .../LegacyMainCirclePieceTest.cs | 25 ++++++++++++------- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 20 +++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs index c9eeef3c01..fd5b6fd752 100644 --- a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Tests { // available textures new[] { @"hitcircle", @"hitcircleoverlay" }, - // priority lookup + // priority lookup prefix null, // expected circle and overlay @"hitcircle", @"hitcircleoverlay", @@ -66,8 +66,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCaseSource(nameof(texture_priority_cases))] public void TestTexturePriorities(string[] textureFilenames, string priorityLookup, string? expectedCircle, string? expectedOverlay) { - Sprite? circleSprite = null; - Sprite? overlaySprite = null; + TestLegacyMainCirclePiece piece = null!; AddStep("load circle piece", () => { @@ -80,18 +79,26 @@ namespace osu.Game.Rulesets.Osu.Tests Child = new DependencyProvidingContainer { CachedDependencies = new (Type, object)[] { (typeof(ISkinSource), skin.Object) }, - Child = new LegacyMainCirclePiece(priorityLookup, false), + Child = piece = new TestLegacyMainCirclePiece(priorityLookup), }; var sprites = this.ChildrenOfType().Where(s => s.Texture.AssetName != null).DistinctBy(s => s.Texture.AssetName).ToArray(); Debug.Assert(sprites.Length <= 2); - - circleSprite = sprites.ElementAtOrDefault(0); - overlaySprite = sprites.ElementAtOrDefault(1); }); - AddAssert("check circle sprite", () => circleSprite?.Texture?.AssetName == expectedCircle); - AddAssert("check overlay sprite", () => overlaySprite?.Texture?.AssetName == expectedOverlay); + AddAssert("check circle sprite", () => piece.CircleSprite?.Texture?.AssetName == expectedCircle); + AddAssert("check overlay sprite", () => piece.OverlaySprite?.Texture?.AssetName == expectedOverlay); + } + + private class TestLegacyMainCirclePiece : LegacyMainCirclePiece + { + public new Sprite? CircleSprite => base.CircleSprite.ChildrenOfType().DistinctBy(s => s.Texture.AssetName).SingleOrDefault(); + public new Sprite? OverlaySprite => base.OverlaySprite.ChildrenOfType().DistinctBy(s => s.Texture.AssetName).SingleOrDefault(); + + public TestLegacyMainCirclePiece(string? priorityLookupPrefix) + : base(priorityLookupPrefix, false) + { + } } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index db4d643046..391147648f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -31,11 +31,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly bool hasNumber; - private Drawable hitCircleSprite = null!; + protected Drawable CircleSprite = null!; + protected Drawable OverlaySprite = null!; protected Container OverlayLayer { get; private set; } = null!; - private Drawable hitCircleOverlay = null!; private SkinnableSpriteText hitCircleText = null!; private readonly Bindable accentColour = new Bindable(); @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy InternalChildren = new[] { - hitCircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) }) + CircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) }) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Child = hitCircleOverlay = new KiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d)) + Child = OverlaySprite = new KiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy bool overlayAboveNumber = skin.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true; if (overlayAboveNumber) - OverlayLayer.ChangeChildDepth(hitCircleOverlay, float.MinValue); + OverlayLayer.ChangeChildDepth(OverlaySprite, float.MinValue); if (drawableOsuObject != null) { @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { base.LoadComplete(); - accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true); + accentColour.BindValueChanged(colour => CircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true); if (hasNumber) indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); @@ -136,11 +136,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy switch (state) { case ArmedState.Hit: - hitCircleSprite.FadeOut(legacy_fade_duration, Easing.Out); - hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + CircleSprite.FadeOut(legacy_fade_duration, Easing.Out); + CircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); - hitCircleOverlay.FadeOut(legacy_fade_duration, Easing.Out); - hitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + OverlaySprite.FadeOut(legacy_fade_duration, Easing.Out); + OverlaySprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); if (hasNumber) { From 858c8f927f7baccc9328cb85847d0ea510d2b0a9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 20 Apr 2022 00:27:02 +0300 Subject: [PATCH 112/134] Attach comment explaining purpose of `CallBase` --- osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs index fd5b6fd752..d8c10b814d 100644 --- a/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs @@ -72,7 +72,11 @@ namespace osu.Game.Rulesets.Osu.Tests { var skin = new Mock(); + // shouldn't be required as GetTexture(string) calls GetTexture(string, WrapMode, WrapMode) by default, + // but moq doesn't handle that well, therefore explicitly requiring to use `CallBase`: + // https://github.com/moq/moq4/issues/972 skin.Setup(s => s.GetTexture(It.IsAny())).CallBase(); + skin.Setup(s => s.GetTexture(It.IsIn(textureFilenames), It.IsAny(), It.IsAny())) .Returns((string componentName, WrapMode _, WrapMode __) => new Texture(1, 1) { AssetName = componentName }); From 4f8065160684cbc49e7a20cad355663e245a5578 Mon Sep 17 00:00:00 2001 From: chickensalt Date: Wed, 20 Apr 2022 10:33:06 +1000 Subject: [PATCH 113/134] implement 2a3a0c1 for playlists as well --- osu.Game/Screens/Menu/ButtonSystem.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 6d09b6de9f..6d9941ba87 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -172,17 +172,7 @@ namespace osu.Game.Screens.Menu { if (api.State.Value != APIState.Online) { - notifications?.Post(new SimpleNotification - { - Text = "You gotta be online to view playlists 'yo!", - Icon = FontAwesome.Solid.Globe, - Activated = () => - { - loginOverlay?.Show(); - return true; - } - }); - + loginOverlay?.Show(); return; } From f5863c20307b8e4ad8123fb25641b588a782dbd7 Mon Sep 17 00:00:00 2001 From: chickensalt Date: Wed, 20 Apr 2022 13:40:02 +1000 Subject: [PATCH 114/134] remove unused import --- osu.Game/Screens/Menu/ButtonSystem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 6d9941ba87..b59e42551a 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -26,7 +26,6 @@ using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Online.API; using osu.Game.Overlays; -using osu.Game.Overlays.Notifications; using osuTK; using osuTK.Graphics; using osuTK.Input; From d46329f55a4fc651b34ac654e963eb79a6e73f23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 12:51:55 +0900 Subject: [PATCH 115/134] Remove unused dependency --- osu.Game/Screens/Menu/ButtonSystem.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index b59e42551a..2d18dce6da 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -116,9 +116,6 @@ namespace osu.Game.Screens.Menu [Resolved] private IAPIProvider api { get; set; } - [Resolved(CanBeNull = true)] - private INotificationOverlay notifications { get; set; } - [Resolved(CanBeNull = true)] private LoginOverlay loginOverlay { get; set; } From 8d31b0bc015461a5c05bb7e0ff9747823cdded5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 15:05:07 +0900 Subject: [PATCH 116/134] Split out the base design of sheared overlay into its own abstract class This will allow for reuse with the first-run overlay. --- .../UserInterface/PopupScreenTitle.cs | 4 +- osu.Game/Overlays/Mods/ModSelectScreen.cs | 101 ++++--------- .../Overlays/Mods/ShearedOverlayContainer.cs | 136 ++++++++++++++++++ 3 files changed, 165 insertions(+), 76 deletions(-) create mode 100644 osu.Game/Overlays/Mods/ShearedOverlayContainer.cs diff --git a/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs b/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs index 5b7db09e77..2e5519726b 100644 --- a/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs @@ -21,6 +21,8 @@ namespace osu.Game.Graphics.UserInterface { public class PopupScreenTitle : CompositeDrawable { + public const float HEIGHT = main_area_height + 2 * corner_radius; + public LocalisableString Title { set => titleSpriteText.Text = value; @@ -67,7 +69,7 @@ namespace osu.Game.Graphics.UserInterface underlayContainer = new Container { RelativeSizeAxes = Axes.X, - Height = main_area_height + 2 * corner_radius, + Height = HEIGHT, CornerRadius = corner_radius, Masking = true, BorderThickness = 2, diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 62080ec1b5..6e8684478e 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Layout; using osu.Game.Configuration; @@ -21,39 +20,27 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public class ModSelectScreen : OsuFocusedOverlayContainer + public class ModSelectScreen : ShearedOverlayContainer { - [Cached] - private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green; [Cached] public Bindable> SelectedMods { get; private set; } = new Bindable>(Array.Empty()); - protected override bool StartHidden => true; - private readonly BindableBool customisationVisible = new BindableBool(); private DifficultyMultiplierDisplay multiplierDisplay; private ModSettingsArea modSettingsArea; private FillFlowContainer columnFlow; private GridContainer grid; - private Container mainContent; - - private PopupScreenTitle header; - private Container footer; [BackgroundDependencyLoader] private void load() { - RelativeSizeAxes = Axes.Both; - RelativePositionAxes = Axes.Both; - - InternalChildren = new Drawable[] + MainAreaContent.AddRange(new Drawable[] { - mainContent = new Container + new Container { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { @@ -62,24 +49,11 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.Both, RowDimensions = new[] { - new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize), new Dimension(), - new Dimension(GridSizeMode.Absolute, 75), }, Content = new[] { - new Drawable[] - { - header = new PopupScreenTitle - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Title = "Mod Select", - Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.", - Close = Hide - } - }, new Drawable[] { new Container @@ -120,6 +94,7 @@ namespace osu.Game.Overlays.Mods Child = columnFlow = new ModColumnContainer { Direction = FillDirection.Horizontal, + Shear = new Vector2(ModPanel.SHEAR_X, 0), RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Spacing = new Vector2(10, 0), @@ -137,52 +112,36 @@ namespace osu.Game.Overlays.Mods } } }, - new[] { Empty() } } }, - footer = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.X, - Height = 50, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Colour = colourProvider.Background5 - }, - new ShearedToggleButton(200) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = 14, Left = 70 }, - Text = "Mod Customisation", - Active = { BindTarget = customisationVisible } - } - } - }, - new ClickToReturnContainer - { - RelativeSizeAxes = Axes.Both, - HandleMouse = { BindTarget = customisationVisible }, - OnClicked = () => customisationVisible.Value = false - } } }, + }); + + Footer.Add(new ShearedToggleButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Vertical = 14, Left = 70 }, + Text = "Mod Customisation", + Active = { BindTarget = customisationVisible } + }); + + AddRange(new Drawable[] + { + new ClickToReturnContainer + { + RelativeSizeAxes = Axes.Both, + HandleMouse = { BindTarget = customisationVisible }, + OnClicked = () => customisationVisible.Value = false + }, modSettingsArea = new ModSettingsArea { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Height = 0 } - }; - - columnFlow.Shear = new Vector2(ModPanel.SHEAR_X, 0); + }); } protected override void LoadComplete() @@ -252,7 +211,7 @@ namespace osu.Game.Overlays.Mods float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0; modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic); - mainContent.TransformTo(nameof(Margin), new MarginPadding { Bottom = modAreaHeight }, transition_duration, Easing.InOutCubic); + TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic); } private bool selectionBindableSyncInProgress; @@ -287,10 +246,6 @@ namespace osu.Game.Overlays.Mods const double fade_in_duration = 400; base.PopIn(); - this.FadeIn(fade_in_duration, Easing.OutQuint); - - header.MoveToY(0, fade_in_duration, Easing.OutQuint); - footer.MoveToY(0, fade_in_duration, Easing.OutQuint); multiplierDisplay .Delay(fade_in_duration * 0.65f) @@ -311,15 +266,11 @@ namespace osu.Game.Overlays.Mods const double fade_out_duration = 500; base.PopOut(); - this.FadeOut(fade_out_duration, Easing.OutQuint); multiplierDisplay .FadeOut(fade_out_duration / 2, Easing.OutQuint) .ScaleTo(0.75f, fade_out_duration, Easing.OutQuint); - header.MoveToY(-header.DrawHeight, fade_out_duration, Easing.OutQuint); - footer.MoveToY(footer.DrawHeight, fade_out_duration, Easing.OutQuint); - for (int i = 0; i < columnFlow.Count; i++) { const float distance = 700; diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs new file mode 100644 index 0000000000..46a3ad3c8d --- /dev/null +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -0,0 +1,136 @@ +// 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.Containers; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Mods +{ + /// + /// A sheared overlay which provides a header and footer and basic animations. + /// Exposes , and as valid targets for content. + /// + public abstract class ShearedOverlayContainer : OsuFocusedOverlayContainer + { + [Cached] + protected readonly OverlayColourProvider ColourProvider; + + /// + /// The overlay's header. + /// + protected PopupScreenTitle Header { get; private set; } + + /// + /// The overlay's footer. + /// + protected Container Footer { get; private set; } + + /// + /// A container containing all content, including the header and footer. + /// May be used for overlay-wide animations. + /// + protected Container TopLevelContent { get; private set; } + + /// + /// A container for content that is to be displayed between the header and footer. + /// + protected Container MainAreaContent { get; private set; } + + /// + /// A container for content that is to be displayed inside the footer. + /// + protected Container FooterContent { get; private set; } + + protected abstract OverlayColourScheme ColourScheme { get; } + + protected override bool StartHidden => true; + + protected override bool BlockNonPositionalInput => true; + + protected ShearedOverlayContainer() + { + RelativeSizeAxes = Axes.Both; + + ColourProvider = new OverlayColourProvider(ColourScheme); + } + + [BackgroundDependencyLoader] + private void load() + { + const float footer_height = 50; + + Child = TopLevelContent = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + Header = new PopupScreenTitle + { + Anchor = Anchor.TopCentre, + Depth = float.MinValue, + Origin = Anchor.TopCentre, + Title = "Mod Select", + Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.", + Close = Hide + }, + MainAreaContent = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Top = PopupScreenTitle.HEIGHT, + Bottom = footer_height, + } + }, + Footer = new Container + { + RelativeSizeAxes = Axes.X, + Depth = float.MinValue, + Height = footer_height, + Margin = new MarginPadding { Top = 10 }, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourProvider.Background5 + }, + FooterContent = new Container + { + RelativeSizeAxes = Axes.Both, + }, + } + } + } + }; + } + + protected override void PopIn() + { + const double fade_in_duration = 400; + + base.PopIn(); + this.FadeIn(fade_in_duration, Easing.OutQuint); + + Header.MoveToY(0, fade_in_duration, Easing.OutQuint); + Footer.MoveToY(0, fade_in_duration, Easing.OutQuint); + } + + protected override void PopOut() + { + const double fade_out_duration = 500; + + base.PopOut(); + this.FadeOut(fade_out_duration, Easing.OutQuint); + + Header.MoveToY(-Header.DrawHeight, fade_out_duration, Easing.OutQuint); + Footer.MoveToY(Footer.DrawHeight, fade_out_duration, Easing.OutQuint); + } + } +} From 1032dc235dae06d61460c0fd019472bb4994ad04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 15:50:57 +0900 Subject: [PATCH 117/134] Rename `PopupScreenTitle` to `ShearedOverlayHeader` --- ...reenTitle.cs => TestSceneShearedOverlayHeader.cs} | 12 ++++++------ .../{PopupScreenTitle.cs => ShearedOverlayHeader.cs} | 4 ++-- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestScenePopupScreenTitle.cs => TestSceneShearedOverlayHeader.cs} (76%) rename osu.Game/Graphics/UserInterface/{PopupScreenTitle.cs => ShearedOverlayHeader.cs} (98%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupScreenTitle.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayHeader.cs similarity index 76% rename from osu.Game.Tests/Visual/UserInterface/TestScenePopupScreenTitle.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayHeader.cs index 22a8fa8a46..ef2b25cd92 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupScreenTitle.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayHeader.cs @@ -10,19 +10,19 @@ using osu.Game.Overlays; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestScenePopupScreenTitle : OsuTestScene + public class TestSceneShearedOverlayHeader : OsuTestScene { [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); [Test] - public void TestPopupScreenTitle() + public void TestShearedOverlayHeader() { AddStep("create content", () => { - Child = new PopupScreenTitle + Child = new ShearedOverlayHeader { - Title = "Popup Screen Title", + Title = "Sheared overlay header", Description = string.Join(" ", Enumerable.Repeat("This is a description.", 20)), Close = () => { } }; @@ -34,9 +34,9 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("create content", () => { - Child = new PopupScreenTitle + Child = new ShearedOverlayHeader { - Title = "Popup Screen Title", + Title = "Sheared overlay header", Description = "This is a description." }; }); diff --git a/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs b/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs similarity index 98% rename from osu.Game/Graphics/UserInterface/PopupScreenTitle.cs rename to osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs index 2e5519726b..9ed7bb35de 100644 --- a/osu.Game/Graphics/UserInterface/PopupScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs @@ -19,7 +19,7 @@ using osuTK; namespace osu.Game.Graphics.UserInterface { - public class PopupScreenTitle : CompositeDrawable + public class ShearedOverlayHeader : CompositeDrawable { public const float HEIGHT = main_area_height + 2 * corner_radius; @@ -50,7 +50,7 @@ namespace osu.Game.Graphics.UserInterface private readonly OsuTextFlowContainer descriptionText; private readonly IconButton closeButton; - public PopupScreenTitle() + public ShearedOverlayHeader() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 46a3ad3c8d..b6f6cf1468 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Mods /// /// The overlay's header. /// - protected PopupScreenTitle Header { get; private set; } + protected ShearedOverlayHeader Header { get; private set; } /// /// The overlay's footer. @@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - Header = new PopupScreenTitle + Header = new ShearedOverlayHeader { Anchor = Anchor.TopCentre, Depth = float.MinValue, @@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Top = PopupScreenTitle.HEIGHT, + Top = ShearedOverlayHeader.HEIGHT, Bottom = footer_height, } }, From 2ee37aeceb5c30f1f36eaa94707a02484f4c217e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 15:57:45 +0900 Subject: [PATCH 118/134] Reduce nesting and usage of `GridContainer` --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 159 +++++++++------------- 1 file changed, 67 insertions(+), 92 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 6e8684478e..452c6e32a6 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -32,101 +32,10 @@ namespace osu.Game.Overlays.Mods private DifficultyMultiplierDisplay multiplierDisplay; private ModSettingsArea modSettingsArea; private FillFlowContainer columnFlow; - private GridContainer grid; [BackgroundDependencyLoader] private void load() { - MainAreaContent.AddRange(new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - grid = new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - }, - Content = new[] - { - new Drawable[] - { - new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.X, - RelativePositionAxes = Axes.X, - X = 0.3f, - Height = DifficultyMultiplierDisplay.HEIGHT, - Margin = new MarginPadding - { - Horizontal = 100, - Vertical = 10 - }, - Child = multiplierDisplay = new DifficultyMultiplierDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - } - } - }, - new Drawable[] - { - new Container - { - Depth = float.MaxValue, - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Both, - Children = new Drawable[] - { - new OsuScrollContainer(Direction.Horizontal) - { - RelativeSizeAxes = Axes.Both, - Masking = false, - ClampExtension = 100, - ScrollbarOverlapsContent = false, - Child = columnFlow = new ModColumnContainer - { - Direction = FillDirection.Horizontal, - Shear = new Vector2(ModPanel.SHEAR_X, 0), - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Spacing = new Vector2(10, 0), - Margin = new MarginPadding { Right = 70 }, - Children = new[] - { - new ModColumn(ModType.DifficultyReduction, false, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), - new ModColumn(ModType.DifficultyIncrease, false, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), - new ModColumn(ModType.Automation, false, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), - new ModColumn(ModType.Conversion, false), - new ModColumn(ModType.Fun, false) - } - } - } - } - } - }, - } - }, - } - }, - }); - - Footer.Add(new ShearedToggleButton(200) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = 14, Left = 70 }, - Text = "Mod Customisation", - Active = { BindTarget = customisationVisible } - }); - AddRange(new Drawable[] { new ClickToReturnContainer @@ -142,6 +51,72 @@ namespace osu.Game.Overlays.Mods Height = 0 } }); + + MainAreaContent.AddRange(new Drawable[] + { + new Container + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.X, + Height = DifficultyMultiplierDisplay.HEIGHT, + Margin = new MarginPadding + { + Horizontal = 100, + }, + Child = multiplierDisplay = new DifficultyMultiplierDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + } + }, + new Container + { + Margin = new MarginPadding + { + Vertical = DifficultyMultiplierDisplay.HEIGHT + 10, + }, + Depth = float.MaxValue, + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, + Children = new Drawable[] + { + new OsuScrollContainer(Direction.Horizontal) + { + RelativeSizeAxes = Axes.Both, + Masking = false, + ClampExtension = 100, + ScrollbarOverlapsContent = false, + Child = columnFlow = new ModColumnContainer + { + Direction = FillDirection.Horizontal, + Shear = new Vector2(ModPanel.SHEAR_X, 0), + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Spacing = new Vector2(10, 0), + Margin = new MarginPadding { Right = 70 }, + Children = new[] + { + new ModColumn(ModType.DifficultyReduction, false, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), + new ModColumn(ModType.DifficultyIncrease, false, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), + new ModColumn(ModType.Automation, false, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), + new ModColumn(ModType.Conversion, false), + new ModColumn(ModType.Fun, false) + } + } + } + } + } + }); + + Footer.Add(new ShearedToggleButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Vertical = 14, Left = 70 }, + Text = "Mod Customisation", + Active = { BindTarget = customisationVisible } + }); } protected override void LoadComplete() @@ -206,7 +181,7 @@ namespace osu.Game.Overlays.Mods { const double transition_duration = 300; - grid.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic); + MainAreaContent.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic); float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0; From 5c7ff363ce406ca8ef4c6c21bc6f76eb7b42f58d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 16:08:00 +0900 Subject: [PATCH 119/134] Move title/description to correct class --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 3 +++ osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 452c6e32a6..7c857d5a90 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -36,6 +36,9 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load() { + Header.Title = "Mod Select"; + Header.Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."; + AddRange(new Drawable[] { new ClickToReturnContainer diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index b6f6cf1468..76a81421fa 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -73,8 +73,6 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopCentre, Depth = float.MinValue, Origin = Anchor.TopCentre, - Title = "Mod Select", - Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.", Close = Hide }, MainAreaContent = new Container From e17f224793bc31ebf92b8ae5113f221d61d908b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 16:28:52 +0900 Subject: [PATCH 120/134] Fix padding mismatches --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 6 +++--- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 7c857d5a90..a9992d70bc 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -75,9 +75,9 @@ namespace osu.Game.Overlays.Mods }, new Container { - Margin = new MarginPadding + Padding = new MarginPadding { - Vertical = DifficultyMultiplierDisplay.HEIGHT + 10, + Top = DifficultyMultiplierDisplay.HEIGHT + PADDING, }, Depth = float.MaxValue, RelativeSizeAxes = Axes.Both, @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Mods { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = 14, Left = 70 }, + Margin = new MarginPadding { Vertical = PADDING, Left = 70 }, Text = "Mod Customisation", Active = { BindTarget = customisationVisible } }); diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 76a81421fa..0bdd4189a0 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -16,6 +16,8 @@ namespace osu.Game.Overlays.Mods /// public abstract class ShearedOverlayContainer : OsuFocusedOverlayContainer { + protected const float PADDING = 14; + [Cached] protected readonly OverlayColourProvider ColourProvider; @@ -81,7 +83,7 @@ namespace osu.Game.Overlays.Mods Padding = new MarginPadding { Top = ShearedOverlayHeader.HEIGHT, - Bottom = footer_height, + Bottom = footer_height + PADDING, } }, Footer = new Container @@ -89,7 +91,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.X, Depth = float.MinValue, Height = footer_height, - Margin = new MarginPadding { Top = 10 }, + Margin = new MarginPadding { Top = PADDING }, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Children = new Drawable[] From 65b2db5e71fc86dab1d1f2c7416123f002c83d93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 16:30:58 +0900 Subject: [PATCH 121/134] Move shear constant to overlay (this is going to likely be used everywhere ever) --- osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs | 6 +++--- osu.Game/Overlays/Mods/ModColumn.cs | 8 ++++---- osu.Game/Overlays/Mods/ModPanel.cs | 11 +++++------ osu.Game/Overlays/Mods/ModSelectScreen.cs | 4 ++-- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 2 ++ 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 248d4f288e..66fd6a202d 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.X, Masking = true, CornerRadius = ModPanel.CORNER_RADIUS, - Shear = new Vector2(ModPanel.SHEAR_X, 0), + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), Children = new Drawable[] { underlayBackground = new Box @@ -98,7 +98,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.Centre, Origin = Anchor.Centre, Margin = new MarginPadding { Horizontal = 18 }, - Shear = new Vector2(-ModPanel.SHEAR_X, 0), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Text = "Difficulty Multiplier", Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) } @@ -109,7 +109,7 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = new Vector2(-ModPanel.SHEAR_X, 0), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Direction = FillDirection.Horizontal, Spacing = new Vector2(2, 0), Children = new Drawable[] diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index f84ae4ac8a..78b4e24a4e 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Mods Width = 320; RelativeSizeAxes = Axes.Y; - Shear = new Vector2(ModPanel.SHEAR_X, 0); + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); Container controlContainer; InternalChildren = new Drawable[] @@ -113,7 +113,7 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.Y, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Shear = new Vector2(-ModPanel.SHEAR_X, 0), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Padding = new MarginPadding { Horizontal = 17, @@ -193,7 +193,7 @@ namespace osu.Game.Overlays.Mods Scale = new Vector2(0.8f), RelativeSizeAxes = Axes.X, LabelText = "Enable All", - Shear = new Vector2(-ModPanel.SHEAR_X, 0) + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) }); panelFlow.Padding = new MarginPadding { @@ -260,7 +260,7 @@ namespace osu.Game.Overlays.Mods var panels = newMods.Select(mod => new ModPanel(mod) { - Shear = new Vector2(-ModPanel.SHEAR_X, 0) + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) }); Task? loadTask; diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index 312171cf74..7c4f2dcb7e 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -42,7 +42,6 @@ namespace osu.Game.Overlays.Mods protected const double TRANSITION_DURATION = 150; - public const float SHEAR_X = 0.2f; public const float CORNER_RADIUS = 7; protected const float HEIGHT = 42; @@ -67,7 +66,7 @@ namespace osu.Game.Overlays.Mods Content.Masking = true; Content.CornerRadius = CORNER_RADIUS; Content.BorderThickness = 2; - Content.Shear = new Vector2(SHEAR_X, 0); + Content.Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); Children = new Drawable[] { @@ -83,7 +82,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.Centre, Origin = Anchor.Centre, Active = { BindTarget = Active }, - Shear = new Vector2(-SHEAR_X, 0), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Scale = new Vector2(HEIGHT / ModSwitchSmall.DEFAULT_SIZE) } }, @@ -116,10 +115,10 @@ namespace osu.Game.Overlays.Mods { Text = mod.Name, Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold), - Shear = new Vector2(-SHEAR_X, 0), + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Margin = new MarginPadding { - Left = -18 * SHEAR_X + Left = -18 * ShearedOverlayContainer.SHEAR } }, new OsuSpriteText @@ -128,7 +127,7 @@ namespace osu.Game.Overlays.Mods Font = OsuFont.Default.With(size: 12), RelativeSizeAxes = Axes.X, Truncate = true, - Shear = new Vector2(-SHEAR_X, 0) + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) } } } diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index a9992d70bc..94f877a08c 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -93,7 +93,7 @@ namespace osu.Game.Overlays.Mods Child = columnFlow = new ModColumnContainer { Direction = FillDirection.Horizontal, - Shear = new Vector2(ModPanel.SHEAR_X, 0), + Shear = new Vector2(SHEAR, 0), RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Spacing = new Vector2(10, 0), @@ -284,7 +284,7 @@ namespace osu.Game.Overlays.Mods { Padding = new MarginPadding { - Left = DrawHeight * ModPanel.SHEAR_X, + Left = DrawHeight * SHEAR, Bottom = 10 }; diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 0bdd4189a0..62ed736dc2 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -18,6 +18,8 @@ namespace osu.Game.Overlays.Mods { protected const float PADDING = 14; + public const float SHEAR = 0.2f; + [Cached] protected readonly OverlayColourProvider ColourProvider; From 4466e15bfcf3a0b7c93ba3c9cfc1da87509a6393 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 23:17:29 +0900 Subject: [PATCH 122/134] Rename `AllowConfiguration` to `AllowCustomisation` and simplify drawable addition --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 57 ++++++++++--------- .../Screens/OnlinePlay/FreeModSelectScreen.cs | 2 +- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 41cbfdafb3..693c85fafc 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Mods /// /// Whether configurable s can be configured by the local user. /// - protected virtual bool AllowConfiguration => true; + protected virtual bool AllowCustomisation => true; /// /// Whether the total score multiplier calculated from the current selected set of mods should be shown. @@ -85,29 +85,12 @@ namespace osu.Game.Overlays.Mods MainAreaContent.AddRange(new Drawable[] { - new Container - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.X, - Height = DifficultyMultiplierDisplay.HEIGHT, - Margin = new MarginPadding - { - Horizontal = 100, - }, - Child = multiplierDisplay = new DifficultyMultiplierDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - } - }, new Container { Padding = new MarginPadding { - Top = DifficultyMultiplierDisplay.HEIGHT + PADDING, + Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING, }, - Depth = float.MaxValue, RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, Children = new Drawable[] @@ -140,14 +123,34 @@ namespace osu.Game.Overlays.Mods } }); - Footer.Add(new ShearedToggleButton(200) + if (ShowTotalMultiplier) { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Vertical = PADDING, Left = 70 }, - Text = "Mod Customisation", - Active = { BindTarget = customisationVisible } - }); + MainAreaContent.Add(new Container + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.X, + Height = DifficultyMultiplierDisplay.HEIGHT, + Margin = new MarginPadding { Horizontal = 100 }, + Child = multiplierDisplay = new DifficultyMultiplierDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }, + }); + } + + if (AllowCustomisation) + { + Footer.Add(new ShearedToggleButton(200) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Vertical = PADDING, Left = 70 }, + Text = "Mod Customisation", + Active = { BindTarget = customisationVisible } + }); + } } protected override void LoadComplete() @@ -194,7 +197,7 @@ namespace osu.Game.Overlays.Mods private void updateCustomisation(ValueChangedEvent> valueChangedEvent) { - if (!AllowConfiguration) + if (!AllowCustomisation) return; bool anyCustomisableMod = false; diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs index 438b334e0b..c85a4fc38b 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs @@ -10,7 +10,7 @@ namespace osu.Game.Screens.OnlinePlay { public class FreeModSelectScreen : ModSelectScreen { - protected override bool AllowConfiguration => false; + protected override bool AllowCustomisation => false; protected override bool ShowTotalMultiplier => false; public new Func IsValidMod From e596c9d1713f82f3000f1c5029ee20d4a96ebdb3 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 20 Apr 2022 19:12:43 +0100 Subject: [PATCH 123/134] Use `Bindable` to control selector active visibility --- .../Visual/Online/TestSceneChannelList.cs | 9 +++-- .../Overlays/Chat/ChannelList/ChannelList.cs | 8 +++- .../Chat/ChannelList/ChannelListItem.cs | 38 +++++++++++-------- .../Chat/ChannelList/ChannelListSelector.cs | 14 ++++++- 4 files changed, 46 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs index d6a1e71f0d..2bfe42e6d4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -25,6 +25,9 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly Bindable selected = new Bindable(); + [Cached] + private readonly Bindable selector = new Bindable(); + private OsuSpriteText selectorText; private OsuSpriteText selectedText; private OsuSpriteText leaveText; @@ -89,7 +92,7 @@ namespace osu.Game.Tests.Visual.Online channelList.OnRequestSelect += channel => { - channelList.SelectorActive.Value = false; + selector.Value = ChannelSelectorState.Hidden; selected.Value = channel; }; @@ -101,9 +104,9 @@ namespace osu.Game.Tests.Visual.Online channelList.RemoveChannel(channel); }; - channelList.SelectorActive.BindValueChanged(change => + selector.BindValueChanged(change => { - selectorText.Text = $"Channel Selector Active: {change.NewValue}"; + selectorText.Text = $"Channel Selector State: {change.NewValue}"; }, true); selected.BindValueChanged(change => diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 94c1a99a81..578ac2aa45 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -14,7 +14,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; -using osuTK; namespace osu.Game.Overlays.Chat.ChannelList { @@ -57,7 +56,6 @@ namespace osu.Game.Overlays.Chat.ChannelList new ChannelListSelector { Margin = new MarginPadding { Bottom = 10 }, - SelectorActive = { BindTarget = SelectorActive }, }, privateChannelFlow = new ChannelListItemFlow("DIRECT MESSAGES"), }, @@ -132,4 +130,10 @@ namespace osu.Game.Overlays.Chat.ChannelList } } } + + public enum ChannelSelectorState + { + Visibile, + Hidden, + } } diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index 43574351ed..3d59d1dc4c 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -31,14 +31,17 @@ namespace osu.Game.Overlays.Chat.ChannelList private readonly Channel channel; - private Box? hoverBox; - private Box? selectBox; - private OsuSpriteText? text; - private ChannelListItemCloseButton? close; + private Box hoverBox = null!; + private Box selectBox = null!; + private OsuSpriteText text = null!; + private ChannelListItemCloseButton close = null!; [Resolved] private Bindable selectedChannel { get; set; } = null!; + [Resolved] + private Bindable selectorState { get; set; } = null!; + [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -124,31 +127,26 @@ namespace osu.Game.Overlays.Chat.ChannelList { base.LoadComplete(); - selectedChannel.BindValueChanged(change => - { - if (change.NewValue == channel) - selectBox?.FadeIn(300, Easing.OutQuint); - else - selectBox?.FadeOut(200, Easing.OutQuint); - }, true); + selectedChannel.BindValueChanged(_ => updateSelectState(), true); + selectorState.BindValueChanged(_ => updateSelectState(), true); Unread.BindValueChanged(change => { - text!.FadeColour(change.NewValue ? colourProvider.Content1 : colourProvider.Light3, 300, Easing.OutQuint); + text.FadeColour(change.NewValue ? colourProvider.Content1 : colourProvider.Light3, 300, Easing.OutQuint); }, true); } protected override bool OnHover(HoverEvent e) { - hoverBox?.FadeIn(300, Easing.OutQuint); - close?.FadeIn(300, Easing.OutQuint); + hoverBox.FadeIn(300, Easing.OutQuint); + close.FadeIn(300, Easing.OutQuint); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - hoverBox?.FadeOut(200, Easing.OutQuint); - close?.FadeOut(200, Easing.OutQuint); + hoverBox.FadeOut(200, Easing.OutQuint); + close.FadeOut(200, Easing.OutQuint); base.OnHoverLost(e); } @@ -167,5 +165,13 @@ namespace osu.Game.Overlays.Chat.ChannelList Masking = true, }; } + + private void updateSelectState() + { + if (selectedChannel.Value == channel && selectorState.Value == ChannelSelectorState.Hidden) + selectBox.FadeIn(300, Easing.OutQuint); + else + selectBox.FadeOut(200, Easing.OutQuint); + } } } diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs index 5bc9a01598..4529a34f84 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs @@ -22,6 +22,9 @@ namespace osu.Game.Overlays.Chat.ChannelList private Box hoverBox = null!; private Box selectBox = null!; + [Resolved] + private Bindable selectorState { get; set; } = null!; + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { @@ -65,8 +68,15 @@ namespace osu.Game.Overlays.Chat.ChannelList { base.LoadComplete(); - SelectorActive.BindValueChanged(selected => selectBox.FadeTo(selected.NewValue ? 1 : 0)); - Action = () => SelectorActive.Value = true; + selectorState.BindValueChanged(selector => + { + if (selector.NewValue == ChannelSelectorState.Visibile) + selectBox.FadeIn(300, Easing.OutQuint); + else + selectBox.FadeOut(200, Easing.OutQuint); + }, true); + + Action = () => selectorState.Value = ChannelSelectorState.Visibile; } protected override bool OnHover(HoverEvent e) From 5319bce772d9e91857108628997c78a8284c79fc Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 20 Apr 2022 21:05:33 +0100 Subject: [PATCH 124/134] Revert DI usage of `ChannelSelectorState` in favour of directly binding `BindableBool` `SelectorActive` --- osu.Game.Tests/Visual/Online/TestSceneChannelList.cs | 9 +++------ osu.Game/Overlays/Chat/ChannelList/ChannelList.cs | 8 ++------ osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs | 9 ++++----- .../Overlays/Chat/ChannelList/ChannelListSelector.cs | 9 +++------ 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs index 2bfe42e6d4..d6a1e71f0d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -25,9 +25,6 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly Bindable selected = new Bindable(); - [Cached] - private readonly Bindable selector = new Bindable(); - private OsuSpriteText selectorText; private OsuSpriteText selectedText; private OsuSpriteText leaveText; @@ -92,7 +89,7 @@ namespace osu.Game.Tests.Visual.Online channelList.OnRequestSelect += channel => { - selector.Value = ChannelSelectorState.Hidden; + channelList.SelectorActive.Value = false; selected.Value = channel; }; @@ -104,9 +101,9 @@ namespace osu.Game.Tests.Visual.Online channelList.RemoveChannel(channel); }; - selector.BindValueChanged(change => + channelList.SelectorActive.BindValueChanged(change => { - selectorText.Text = $"Channel Selector State: {change.NewValue}"; + selectorText.Text = $"Channel Selector Active: {change.NewValue}"; }, true); selected.BindValueChanged(change => diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs index 578ac2aa45..076dc5719e 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelList.cs @@ -56,6 +56,7 @@ namespace osu.Game.Overlays.Chat.ChannelList new ChannelListSelector { Margin = new MarginPadding { Bottom = 10 }, + SelectorActive = { BindTarget = SelectorActive }, }, privateChannelFlow = new ChannelListItemFlow("DIRECT MESSAGES"), }, @@ -72,6 +73,7 @@ namespace osu.Game.Overlays.Chat.ChannelList ChannelListItem item = new ChannelListItem(channel); item.OnRequestSelect += chan => OnRequestSelect?.Invoke(chan); item.OnRequestLeave += chan => OnRequestLeave?.Invoke(chan); + item.SelectorActive.BindTarget = SelectorActive; ChannelListItemFlow flow = getFlowForChannel(channel); channelMap.Add(channel, item); @@ -130,10 +132,4 @@ namespace osu.Game.Overlays.Chat.ChannelList } } } - - public enum ChannelSelectorState - { - Visibile, - Hidden, - } } diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index 3d59d1dc4c..7c4a72559b 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -29,6 +29,8 @@ namespace osu.Game.Overlays.Chat.ChannelList public readonly BindableBool Unread = new BindableBool(); + public readonly BindableBool SelectorActive = new BindableBool(); + private readonly Channel channel; private Box hoverBox = null!; @@ -39,9 +41,6 @@ namespace osu.Game.Overlays.Chat.ChannelList [Resolved] private Bindable selectedChannel { get; set; } = null!; - [Resolved] - private Bindable selectorState { get; set; } = null!; - [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -128,7 +127,7 @@ namespace osu.Game.Overlays.Chat.ChannelList base.LoadComplete(); selectedChannel.BindValueChanged(_ => updateSelectState(), true); - selectorState.BindValueChanged(_ => updateSelectState(), true); + SelectorActive.BindValueChanged(_ => updateSelectState(), true); Unread.BindValueChanged(change => { @@ -168,7 +167,7 @@ namespace osu.Game.Overlays.Chat.ChannelList private void updateSelectState() { - if (selectedChannel.Value == channel && selectorState.Value == ChannelSelectorState.Hidden) + if (selectedChannel.Value == channel && !SelectorActive.Value) selectBox.FadeIn(300, Easing.OutQuint); else selectBox.FadeOut(200, Easing.OutQuint); diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs index 4529a34f84..57ab7584b5 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs @@ -22,9 +22,6 @@ namespace osu.Game.Overlays.Chat.ChannelList private Box hoverBox = null!; private Box selectBox = null!; - [Resolved] - private Bindable selectorState { get; set; } = null!; - [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { @@ -68,15 +65,15 @@ namespace osu.Game.Overlays.Chat.ChannelList { base.LoadComplete(); - selectorState.BindValueChanged(selector => + SelectorActive.BindValueChanged(selector => { - if (selector.NewValue == ChannelSelectorState.Visibile) + if (selector.NewValue) selectBox.FadeIn(300, Easing.OutQuint); else selectBox.FadeOut(200, Easing.OutQuint); }, true); - Action = () => selectorState.Value = ChannelSelectorState.Visibile; + Action = () => SelectorActive.Value = true; } protected override bool OnHover(HoverEvent e) From 5e5c8e78a6538b4bcda713375f59b1b2828ec9fa Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 27 Jan 2022 20:53:48 -0800 Subject: [PATCH 125/134] Use existing web localisation for most hardcoded strings --- osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs | 5 +++-- osu.Game/Beatmaps/BeatmapStatistic.cs | 3 ++- .../Drawables/BeatmapDownloadButton.cs | 3 ++- osu.Game/Graphics/UserInterface/OsuMenuItem.cs | 3 ++- .../UserInterface/PageSelector/PageSelector.cs | 5 +++-- .../PageSelector/PageSelectorPrevNextButton.cs | 6 ++++-- .../Graphics/UserInterface/SearchTextBox.cs | 3 ++- .../Graphics/UserInterface/ShowMoreButton.cs | 4 +++- .../Graphics/UserInterfaceV2/ColourDisplay.cs | 3 ++- osu.Game/Localisation/CommonStrings.cs | 10 ---------- .../API/Requests/Responses/APIPlayStyle.cs | 11 ++++++----- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 3 ++- .../Online/Leaderboards/LeaderboardScore.cs | 11 ++++++----- osu.Game/Online/Rooms/MatchType.cs | 7 ++++--- .../Overlays/AccountCreation/ScreenEntry.cs | 3 ++- .../Overlays/BeatmapSet/BeatmapAvailability.cs | 7 ++++--- .../Scores/NotSupporterPlaceholder.cs | 3 ++- osu.Game/Overlays/ChatOverlay.cs | 2 +- .../Comments/Buttons/LoadRepliesButton.cs | 3 ++- .../Comments/Buttons/ShowMoreRepliesButton.cs | 3 ++- .../Comments/CancellableCommentEditor.cs | 3 ++- .../Overlays/Comments/CommentsContainer.cs | 3 ++- osu.Game/Overlays/Comments/CommentsHeader.cs | 10 ++++++++-- .../Comments/CommentsShowMoreButton.cs | 6 +++++- osu.Game/Overlays/Comments/DrawableComment.cs | 2 +- .../Overlays/Comments/TotalCommentsCounter.cs | 3 ++- .../Dashboard/CurrentlyPlayingDisplay.cs | 3 ++- .../Dashboard/Home/DrawableBeatmapList.cs | 3 ++- .../Dashboard/Home/DrawableNewBeatmapList.cs | 4 +++- .../Home/DrawablePopularBeatmapList.cs | 4 +++- .../Dashboard/Home/News/ShowMoreNewsPanel.cs | 3 ++- osu.Game/Overlays/Dialog/ConfirmDialog.cs | 3 ++- osu.Game/Overlays/Login/LoginForm.cs | 7 ++++--- osu.Game/Overlays/Login/UserAction.cs | 3 +++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 ++- osu.Game/Overlays/NotificationOverlay.cs | 5 +++-- .../Notifications/NotificationSection.cs | 7 ++++--- .../Profile/Header/BottomHeaderContainer.cs | 18 ++++++++++++++---- .../Settings/Sections/Input/KeyBindingRow.cs | 6 +++--- osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 3 ++- .../Wiki/Markdown/WikiNoticeContainer.cs | 8 +++++--- osu.Game/Overlays/Wiki/WikiSidebar.cs | 4 +++- .../Compose/Components/SelectionHandler.cs | 3 ++- osu.Game/Screens/Edit/Editor.cs | 3 ++- .../Screens/Edit/Setup/DifficultySection.cs | 9 +++++---- osu.Game/Screens/Edit/Setup/MetadataSection.cs | 7 ++++--- .../OnlinePlay/Components/OverlinedHeader.cs | 3 ++- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 3 ++- .../Match/Components/MatchLeaderboardScore.cs | 5 +++-- .../OnlinePlay/Match/DrawableMatchRoom.cs | 3 ++- .../Participants/ParticipantsListHeader.cs | 3 ++- .../Screens/Play/BeatmapMetadataDisplay.cs | 9 +++++---- osu.Game/Screens/Play/Break/BreakInfo.cs | 3 ++- osu.Game/Screens/Play/Break/BreakInfoLine.cs | 4 ++-- .../Play/HUD/PerformancePointsCounter.cs | 3 ++- .../Play/PlayerSettings/AudioSettings.cs | 3 ++- .../Play/PlayerSettings/VisualSettings.cs | 11 ++++++----- .../Contracted/ContractedPanelMiddleContent.cs | 8 +++++--- .../Expanded/Statistics/AccuracyStatistic.cs | 3 ++- .../Expanded/Statistics/ComboStatistic.cs | 3 ++- .../Expanded/Statistics/CounterStatistic.cs | 3 ++- .../Statistics/PerformanceStatistic.cs | 3 ++- .../Expanded/Statistics/StatisticDisplay.cs | 8 +++++--- osu.Game/Screens/Select/BeatmapDetails.cs | 3 ++- .../Select/Carousel/DrawableCarouselBeatmap.cs | 14 ++++---------- .../Screens/Select/Details/AdvancedStats.cs | 13 +++++++------ osu.Game/Screens/Select/Filter/SortMode.cs | 16 +++++++++------- osu.Game/Screens/Select/FilterControl.cs | 3 ++- .../Select/Options/BeatmapOptionsOverlay.cs | 3 ++- osu.Game/Users/UserStatus.cs | 10 ++++++---- 70 files changed, 224 insertions(+), 147 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs index 2d3cc3c103..a5282877ee 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Beatmaps @@ -20,13 +21,13 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { new BeatmapStatistic { - Name = @"Circle Count", + Name = BeatmapsetsStrings.ShowStatsCountCircles, Content = circles.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), }, new BeatmapStatistic { - Name = @"Slider Count", + Name = BeatmapsetsStrings.ShowStatsCountSliders, Content = sliders.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), }, diff --git a/osu.Game/Beatmaps/BeatmapStatistic.cs b/osu.Game/Beatmaps/BeatmapStatistic.cs index 7d7ba09fcf..94ebb56a5c 100644 --- a/osu.Game/Beatmaps/BeatmapStatistic.cs +++ b/osu.Game/Beatmaps/BeatmapStatistic.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics; +using osu.Framework.Localisation; namespace osu.Game.Beatmaps { @@ -14,6 +15,6 @@ namespace osu.Game.Beatmaps public Func CreateIcon; public string Content; - public string Name; + public LocalisableString Name; } } diff --git a/osu.Game/Beatmaps/Drawables/BeatmapDownloadButton.cs b/osu.Game/Beatmaps/Drawables/BeatmapDownloadButton.cs index e2485e7a77..6ab92a2ba2 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapDownloadButton.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapDownloadButton.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Beatmaps.Drawables { @@ -104,7 +105,7 @@ namespace osu.Game.Beatmaps.Drawables if ((beatmapSet as IBeatmapSetOnlineInfo)?.Availability.DownloadDisabled == true) { button.Enabled.Value = false; - button.TooltipText = "this beatmap is currently not available for download."; + button.TooltipText = BeatmapsetsStrings.AvailabilityDisabled; } break; diff --git a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs index 0fe41937ce..1da60415ba 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenuItem.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; namespace osu.Game.Graphics.UserInterface { @@ -15,7 +16,7 @@ namespace osu.Game.Graphics.UserInterface { } - public OsuMenuItem(string text, MenuItemType type, Action action) + public OsuMenuItem(LocalisableString text, MenuItemType type, Action action) : base(text, action) { Type = type; diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index 005729580c..5c6d087279 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Framework.Bindables; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Graphics.UserInterface.PageSelector { @@ -29,7 +30,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector Direction = FillDirection.Horizontal, Children = new Drawable[] { - previousPageButton = new PageSelectorPrevNextButton(false, "prev") + previousPageButton = new PageSelectorPrevNextButton(false, CommonStrings.PaginationPrevious) { Action = () => CurrentPage.Value -= 1, }, @@ -38,7 +39,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, }, - nextPageButton = new PageSelectorPrevNextButton(true, "next") + nextPageButton = new PageSelectorPrevNextButton(true, CommonStrings.PaginationNext) { Action = () => CurrentPage.Value += 1 } diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs index 7503ab8135..889917c397 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; using osuTK; @@ -13,12 +15,12 @@ namespace osu.Game.Graphics.UserInterface.PageSelector public class PageSelectorPrevNextButton : PageSelectorButton { private readonly bool rightAligned; - private readonly string text; + private readonly LocalisableString text; private SpriteIcon icon; private OsuSpriteText name; - public PageSelectorPrevNextButton(bool rightAligned, string text) + public PageSelectorPrevNextButton(bool rightAligned, LocalisableString text) { this.rightAligned = rightAligned; this.text = text; diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index 6937782be6..dd9ed7c9e9 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Input; @@ -27,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface }); TextFlow.Padding = new MarginPadding { Right = 35 }; - PlaceholderText = "type to search"; + PlaceholderText = HomeStrings.SearchPlaceholder; } public override bool OnPressed(KeyBindingPressEvent e) diff --git a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs index 615895074c..05dda324d4 100644 --- a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs +++ b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs @@ -11,7 +11,9 @@ using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osuTK; using System.Collections.Generic; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Graphics.UserInterface { @@ -80,7 +82,7 @@ namespace osu.Game.Graphics.UserInterface Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), - Text = "show more".ToUpper(), + Text = CommonStrings.ButtonsShowMore.ToUpper(), }, rightIcon = new ChevronIcon { diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index 5240df74a2..cec319f28e 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -14,6 +14,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osuTK; namespace osu.Game.Graphics.UserInterfaceV2 @@ -139,7 +140,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 public MenuItem[] ContextMenuItems => new MenuItem[] { - new OsuMenuItem("Delete", MenuItemType.Destructive, () => DeleteRequested?.Invoke()) + new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => DeleteRequested?.Invoke()) }; } } diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 3ea337c279..b717bb83dd 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -9,16 +9,6 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.Common"; - /// - /// "Cancel" - /// - public static LocalisableString Cancel => new TranslatableString(getKey(@"cancel"), @"Cancel"); - - /// - /// "Clear" - /// - public static LocalisableString Clear => new TranslatableString(getKey(@"clear"), @"Clear"); - /// /// "Enabled" /// diff --git a/osu.Game/Online/API/Requests/Responses/APIPlayStyle.cs b/osu.Game/Online/API/Requests/Responses/APIPlayStyle.cs index 9573ae1825..a9d66f3d6a 100644 --- a/osu.Game/Online/API/Requests/Responses/APIPlayStyle.cs +++ b/osu.Game/Online/API/Requests/Responses/APIPlayStyle.cs @@ -1,22 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.ComponentModel; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Online.API.Requests.Responses { public enum APIPlayStyle { - [Description("Keyboard")] + [LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceKeyboard))] Keyboard, - [Description("Mouse")] + [LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceMouse))] Mouse, - [Description("Tablet")] + [LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceTablet))] Tablet, - [Description("Touch Screen")] + [LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceTouch))] Touch, } } diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index f83bf4877e..6a7da52416 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -12,6 +12,7 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Chat; +using osu.Game.Resources.Localisation.Web; using osuTK.Graphics; namespace osu.Game.Online.Chat @@ -63,7 +64,7 @@ namespace osu.Game.Online.Chat { RelativeSizeAxes = Axes.X, Height = text_box_height, - PlaceholderText = "type your message", + PlaceholderText = ChatStrings.InputPlaceholder, CornerRadius = corner_radius, ReleaseFocusOnCommit = false, HoldFocus = true, diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index eecb7ff6b3..c75e98cdaa 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -30,6 +30,7 @@ using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; using osu.Game.Online.API; +using osu.Game.Resources.Localisation.Web; using osu.Game.Utils; namespace osu.Game.Online.Leaderboards @@ -291,8 +292,8 @@ namespace osu.Game.Online.Leaderboards protected virtual IEnumerable GetStatistics(ScoreInfo model) => new[] { - new LeaderboardScoreStatistic(FontAwesome.Solid.Link, "Max Combo", model.MaxCombo.ToString()), - new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", model.DisplayAccuracy) + new LeaderboardScoreStatistic(FontAwesome.Solid.Link, BeatmapsetsStrings.ShowScoreboardHeadersCombo, model.MaxCombo.ToString()), + new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, BeatmapsetsStrings.ShowScoreboardHeadersAccuracy, model.DisplayAccuracy) }; protected override bool OnHover(HoverEvent e) @@ -403,9 +404,9 @@ namespace osu.Game.Online.Leaderboards { public IconUsage Icon; public LocalisableString Value; - public string Name; + public LocalisableString Name; - public LeaderboardScoreStatistic(IconUsage icon, string name, LocalisableString value) + public LeaderboardScoreStatistic(IconUsage icon, LocalisableString name, LocalisableString value) { Icon = icon; Name = name; @@ -426,7 +427,7 @@ namespace osu.Game.Online.Leaderboards items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => new LegacyScoreExporter(storage).Export(Score))); if (!isOnlineScope) - items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score)))); + items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score)))); return items.ToArray(); } diff --git a/osu.Game/Online/Rooms/MatchType.cs b/osu.Game/Online/Rooms/MatchType.cs index 36f0dc0c81..278f0693eb 100644 --- a/osu.Game/Online/Rooms/MatchType.cs +++ b/osu.Game/Online/Rooms/MatchType.cs @@ -1,7 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.ComponentModel; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Online.Rooms { @@ -11,10 +12,10 @@ namespace osu.Game.Online.Rooms Playlists, - [Description("Head to head")] + [LocalisableDescription(typeof(MatchesStrings), nameof(MatchesStrings.MatchTeamTypesHeadToHead))] HeadToHead, - [Description("Team VS")] + [LocalisableDescription(typeof(MatchesStrings), nameof(MatchesStrings.MatchTeamTypesTeamVs))] TeamVersus, } } diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index a2c04c6989..6cf3fb4267 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -16,6 +16,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays.Settings; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Graphics; @@ -68,7 +69,7 @@ namespace osu.Game.Overlays.AccountCreation }, usernameTextBox = new OsuTextBox { - PlaceholderText = "username", + PlaceholderText = UsersStrings.LoginUsername, RelativeSizeAxes = Axes.X, TabbableContentContainer = this }, diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs b/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs index dc46452dcb..b6e768d632 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet @@ -69,14 +70,14 @@ namespace osu.Game.Overlays.BeatmapSet { textContainer.Clear(); textContainer.AddParagraph(downloadDisabled - ? "This beatmap is currently not available for download." - : "Portions of this beatmap have been removed at the request of the creator or a third-party rights holder.", t => t.Colour = Color4.Orange); + ? BeatmapsetsStrings.AvailabilityDisabled + : BeatmapsetsStrings.AvailabilityPartsRemoved, t => t.Colour = Color4.Orange); if (hasExternalLink) { textContainer.NewParagraph(); textContainer.NewParagraph(); - textContainer.AddLink("Check here for more information.", BeatmapSet.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10)); + textContainer.AddLink(BeatmapsetsStrings.AvailabilityMoreInfo, BeatmapSet.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10)); } } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/NotSupporterPlaceholder.cs b/osu.Game/Overlays/BeatmapSet/Scores/NotSupporterPlaceholder.cs index b2c87a1477..d1a0960a08 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/NotSupporterPlaceholder.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/NotSupporterPlaceholder.cs @@ -7,6 +7,7 @@ using osu.Game.Graphics.Sprites; using osuTK; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -28,7 +29,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = @"You need to be an osu!supporter to access the friend and country rankings!", + Text = BeatmapsetsStrings.ShowScoreboardSupporterOnly, Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), }, text = new LinkFlowContainer(t => t.Font = t.Font.With(size: 11)) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 64b972262b..034670cf37 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -160,7 +160,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both, Height = 1, - PlaceholderText = "type your message", + PlaceholderText = Resources.Localisation.Web.ChatStrings.InputPlaceholder, ReleaseFocusOnCommit = false, HoldFocus = true, } diff --git a/osu.Game/Overlays/Comments/Buttons/LoadRepliesButton.cs b/osu.Game/Overlays/Comments/Buttons/LoadRepliesButton.cs index 4998e5391e..4bb5b9d66d 100644 --- a/osu.Game/Overlays/Comments/Buttons/LoadRepliesButton.cs +++ b/osu.Game/Overlays/Comments/Buttons/LoadRepliesButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments.Buttons { @@ -25,7 +26,7 @@ namespace osu.Game.Overlays.Comments.Buttons { public ButtonContent() { - Text = "load replies"; + Text = CommentsStrings.LoadReplies; } } } diff --git a/osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs b/osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs index c115a8bb8f..4908e29b7d 100644 --- a/osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs +++ b/osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs @@ -9,6 +9,7 @@ using osu.Game.Graphics.Sprites; using System.Collections.Generic; using osuTK; using osu.Framework.Allocation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments.Buttons { @@ -38,7 +39,7 @@ namespace osu.Game.Overlays.Comments.Buttons { AlwaysPresent = true, Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), - Text = "show more" + Text = CommonStrings.ButtonsShowMore } }; diff --git a/osu.Game/Overlays/Comments/CancellableCommentEditor.cs b/osu.Game/Overlays/Comments/CancellableCommentEditor.cs index c226b7f07f..74c221bd82 100644 --- a/osu.Game/Overlays/Comments/CancellableCommentEditor.cs +++ b/osu.Game/Overlays/Comments/CancellableCommentEditor.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments { @@ -54,7 +55,7 @@ namespace osu.Game.Overlays.Comments Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), Margin = new MarginPadding { Horizontal = 20 }, - Text = @"Cancel" + Text = CommonStrings.ButtonsCancel } } }; diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index 6a5734b553..a28b13fc12 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -16,6 +16,7 @@ using osu.Framework.Threading; using System.Collections.Generic; using JetBrains.Annotations; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using APIUser = osu.Game.Online.API.Requests.Responses.APIUser; namespace osu.Game.Overlays.Comments @@ -328,7 +329,7 @@ namespace osu.Game.Overlays.Comments Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Margin = new MarginPadding { Left = 50 }, - Text = @"No comments yet." + Text = CommentsStrings.Empty } }); } diff --git a/osu.Game/Overlays/Comments/CommentsHeader.cs b/osu.Game/Overlays/Comments/CommentsHeader.cs index bf80655c3d..e7d9e72dcc 100644 --- a/osu.Game/Overlays/Comments/CommentsHeader.cs +++ b/osu.Game/Overlays/Comments/CommentsHeader.cs @@ -12,7 +12,9 @@ using osu.Game.Graphics; using osu.Framework.Graphics.Sprites; using osuTK; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments { @@ -91,7 +93,7 @@ namespace osu.Game.Overlays.Comments Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), - Text = @"Show deleted" + Text = CommonStrings.ButtonsShowDeleted } }, }); @@ -126,9 +128,13 @@ namespace osu.Game.Overlays.Comments public enum CommentsSortCriteria { - [System.ComponentModel.Description(@"Recent")] + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.New))] New, + + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Old))] Old, + + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Top))] Top } } diff --git a/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs b/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs index adf64eabb1..b1ca39c3bf 100644 --- a/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs +++ b/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments { @@ -18,7 +21,8 @@ namespace osu.Game.Overlays.Comments private void onCurrentChanged(ValueChangedEvent count) { - Text = $@"Show More ({count.NewValue})".ToUpper(); + Text = new TranslatableString(@"_", "{0} ({1})", + CommonStrings.ButtonsShowMore.ToUpper(), count.NewValue); } } } diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 3286b6c5c0..3ec91c8e63 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -150,7 +150,7 @@ namespace osu.Game.Overlays.Comments { Alpha = Comment.IsDeleted ? 1 : 0, Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), - Text = "deleted" + Text = CommentsStrings.Deleted } } }, diff --git a/osu.Game/Overlays/Comments/TotalCommentsCounter.cs b/osu.Game/Overlays/Comments/TotalCommentsCounter.cs index 1bb9b52689..221a745189 100644 --- a/osu.Game/Overlays/Comments/TotalCommentsCounter.cs +++ b/osu.Game/Overlays/Comments/TotalCommentsCounter.cs @@ -9,6 +9,7 @@ using osuTK; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments { @@ -39,7 +40,7 @@ namespace osu.Game.Overlays.Comments Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: 20, italics: true), Colour = colourProvider.Light1, - Text = @"Comments" + Text = CommentsStrings.Title }, new CircularContainer { diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 786401b7a8..e036f1d304 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -14,6 +14,7 @@ using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Spectator; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; @@ -138,7 +139,7 @@ namespace osu.Game.Overlays.Dashboard new PurpleTriangleButton { RelativeSizeAxes = Axes.X, - Text = "Watch", + Text = CommonStrings.ButtonsWatchTo1, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), diff --git a/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs index c73cc828e2..382bc00b1d 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; @@ -49,7 +50,7 @@ namespace osu.Game.Overlays.Dashboard.Home flow.AddRange(beatmapSets.Select(CreateBeatmapPanel)); } - protected abstract string Title { get; } + protected abstract LocalisableString Title { get; } protected abstract DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet); } diff --git a/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs index 714e07a7ed..331fff0aea 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dashboard.Home { @@ -15,6 +17,6 @@ namespace osu.Game.Overlays.Dashboard.Home protected override DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet) => new DashboardNewBeatmapPanel(beatmapSet); - protected override string Title => "New Ranked Beatmaps"; + protected override LocalisableString Title => HomeStrings.UserBeatmapsNew; } } diff --git a/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs index 48b100b04e..154813dea1 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dashboard.Home { @@ -15,6 +17,6 @@ namespace osu.Game.Overlays.Dashboard.Home protected override DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet) => new DashboardPopularBeatmapPanel(beatmapSet); - protected override string Title => "Popular Beatmaps"; + protected override LocalisableString Title => HomeStrings.UserBeatmapsPopular; } } diff --git a/osu.Game/Overlays/Dashboard/Home/News/ShowMoreNewsPanel.cs b/osu.Game/Overlays/Dashboard/Home/News/ShowMoreNewsPanel.cs index d25df6f189..f6e966957e 100644 --- a/osu.Game/Overlays/Dashboard/Home/News/ShowMoreNewsPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/News/ShowMoreNewsPanel.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osuTK.Graphics; namespace osu.Game.Overlays.Dashboard.Home.News @@ -35,7 +36,7 @@ namespace osu.Game.Overlays.Dashboard.Home.News Anchor = Anchor.Centre, Origin = Anchor.Centre, Margin = new MarginPadding { Vertical = 20 }, - Text = "see more" + Text = CommonStrings.ButtonsSeeMore } }; diff --git a/osu.Game/Overlays/Dialog/ConfirmDialog.cs b/osu.Game/Overlays/Dialog/ConfirmDialog.cs index d1c0d746d1..58ce84e13a 100644 --- a/osu.Game/Overlays/Dialog/ConfirmDialog.cs +++ b/osu.Game/Overlays/Dialog/ConfirmDialog.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dialog { @@ -33,7 +34,7 @@ namespace osu.Game.Overlays.Dialog }, new PopupDialogCancelButton { - Text = Localisation.CommonStrings.Cancel, + Text = CommonStrings.ButtonsCancel, Action = onCancel }, }; diff --git a/osu.Game/Overlays/Login/LoginForm.cs b/osu.Game/Overlays/Login/LoginForm.cs index f7842dcd30..c31416e078 100644 --- a/osu.Game/Overlays/Login/LoginForm.cs +++ b/osu.Game/Overlays/Login/LoginForm.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays.Settings; +using osu.Game.Resources.Localisation.Web; using osuTK; namespace osu.Game.Overlays.Login @@ -50,14 +51,14 @@ namespace osu.Game.Overlays.Login { username = new OsuTextBox { - PlaceholderText = "username", + PlaceholderText = UsersStrings.LoginUsername, RelativeSizeAxes = Axes.X, Text = api?.ProvidedUsername ?? string.Empty, TabbableContentContainer = this }, password = new OsuPasswordTextBox { - PlaceholderText = "password", + PlaceholderText = UsersStrings.LoginPassword, RelativeSizeAxes = Axes.X, TabbableContentContainer = this, }, @@ -88,7 +89,7 @@ namespace osu.Game.Overlays.Login AutoSizeAxes = Axes.Y, Child = new SettingsButton { - Text = "Sign in", + Text = UsersStrings.LoginButton, Action = performLogin }, } diff --git a/osu.Game/Overlays/Login/UserAction.cs b/osu.Game/Overlays/Login/UserAction.cs index 07b6b4bf7e..d216670a28 100644 --- a/osu.Game/Overlays/Login/UserAction.cs +++ b/osu.Game/Overlays/Login/UserAction.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using System.ComponentModel; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Login { public enum UserAction { + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusOnline))] Online, [Description(@"Do not disturb")] diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ec7e49920c..9ce79c25f7 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Mods; using osu.Game.Screens; using osu.Game.Utils; @@ -317,7 +318,7 @@ namespace osu.Game.Overlays.Mods CloseButton = new TriangleButton { Width = 180, - Text = "Close", + Text = CommonStrings.ButtonsClose, Action = Hide, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 2b5909f45a..f1ed5c4ba6 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -15,7 +15,8 @@ using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Graphics; -using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; +using NotificationsStrings = osu.Game.Localisation.NotificationsStrings; namespace osu.Game.Overlays { @@ -61,7 +62,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.X, Children = new[] { - new NotificationSection(@"Notifications", @"Clear All") + new NotificationSection(AccountsStrings.NotificationsTitle, "Clear All") { AcceptTypes = new[] { typeof(SimpleNotification) } }, diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index a23ff07a64..a4851ab365 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; @@ -34,9 +35,9 @@ namespace osu.Game.Overlays.Notifications private readonly string clearButtonText; - private readonly string titleText; + private readonly LocalisableString titleText; - public NotificationSection(string title, string clearButtonText) + public NotificationSection(LocalisableString title, string clearButtonText) { this.clearButtonText = clearButtonText.ToUpperInvariant(); titleText = title; @@ -84,7 +85,7 @@ namespace osu.Game.Overlays.Notifications { new OsuSpriteText { - Text = titleText.ToUpperInvariant(), + Text = titleText.ToUpper(), Font = OsuFont.GetFont(weight: FontWeight.Bold) }, countDrawable = new OsuSpriteText diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index ea52cec2e1..a70d57661b 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -11,10 +10,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Graphics; @@ -83,7 +84,7 @@ namespace osu.Game.Overlays.Profile.Header if (user == null) return; if (user.JoinDate.ToUniversalTime().Year < 2008) - topLinkContainer.AddText("Here since the beginning"); + topLinkContainer.AddText(UsersStrings.ShowFirstMembers); else { topLinkContainer.AddText("Joined "); @@ -94,7 +95,7 @@ namespace osu.Game.Overlays.Profile.Header if (user.IsOnline) { - topLinkContainer.AddText("Currently online"); + topLinkContainer.AddText(UsersStrings.ShowLastvisitOnline); addSpacer(topLinkContainer); } else if (user.LastVisit.HasValue) @@ -108,7 +109,16 @@ namespace osu.Game.Overlays.Profile.Header if (user.PlayStyles?.Length > 0) { topLinkContainer.AddText("Plays with "); - topLinkContainer.AddText(string.Join(", ", user.PlayStyles.Select(style => style.GetDescription())), embolden); + + LocalisableString playStylesString = user.PlayStyles[0].GetLocalisableDescription(); + + for (int i = 1; i < user.PlayStyles.Length; i++) + { + playStylesString = new TranslatableString(@"_", @"{0}{1}", playStylesString, CommonStrings.ArrayAndWordsConnector); + playStylesString = new TranslatableString(@"_", @"{0}{1}", playStylesString, user.PlayStyles[i].GetLocalisableDescription()); + } + + topLinkContainer.AddText(playStylesString, embolden); addSpacer(topLinkContainer); } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 2405618917..459405f57d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -21,7 +21,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Input.Bindings; -using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -402,7 +402,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public CancelButton() { - Text = CommonStrings.Cancel; + Text = CommonStrings.ButtonsCancel; Size = new Vector2(80, 20); } } @@ -411,7 +411,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public ClearButton() { - Text = CommonStrings.Clear; + Text = CommonStrings.ButtonsClear; Size = new Vector2(80, 20); } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index b0c9a04285..d8ba07dc3b 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Effects; using osu.Game.Graphics; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; @@ -62,7 +63,7 @@ namespace osu.Game.Overlays.Toolbar switch (state.NewValue) { default: - Text = @"Guest"; + Text = UsersStrings.AnonymousUsername; avatar.User = new APIUser(); break; diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index a22c18b0a4..11cab80a57 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -7,7 +7,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Wiki.Markdown { @@ -46,14 +48,14 @@ namespace osu.Game.Overlays.Wiki.Markdown { Add(new NoticeBox { - Text = "The content on this page is incomplete or outdated. If you are able to help out, please consider updating the article!", + Text = WikiStrings.ShowIncompleteOrOutdated, }); } else if (needsCleanup) { Add(new NoticeBox { - Text = "This page does not meet the standards of the osu! wiki and needs to be cleaned up or rewritten. If you are able to help out, please consider updating the article!", + Text = WikiStrings.ShowNeedsCleanupOrRewrite, }); } } @@ -63,7 +65,7 @@ namespace osu.Game.Overlays.Wiki.Markdown [Resolved] private IMarkdownTextFlowComponent parentFlowComponent { get; set; } - public string Text { get; set; } + public LocalisableString Text { get; set; } [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, OsuColour colour) diff --git a/osu.Game/Overlays/Wiki/WikiSidebar.cs b/osu.Game/Overlays/Wiki/WikiSidebar.cs index ee4e195f3f..da96885fb5 100644 --- a/osu.Game/Overlays/Wiki/WikiSidebar.cs +++ b/osu.Game/Overlays/Wiki/WikiSidebar.cs @@ -3,11 +3,13 @@ using Markdig.Syntax; using Markdig.Syntax.Inlines; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Wiki { @@ -24,7 +26,7 @@ namespace osu.Game.Overlays.Wiki { new OsuSpriteText { - Text = "CONTENTS", + Text = WikiStrings.ShowToc.ToUpper(), Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), Margin = new MarginPadding { Bottom = 5 }, }, diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 9d5d8013b7..78b98a3649 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -17,6 +17,7 @@ using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Edit; using osuTK; using osuTK.Input; @@ -358,7 +359,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (SelectedBlueprints.Count == 1) items.AddRange(SelectedBlueprints[0].ContextMenuItems); - items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, DeleteSelected)); + items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, DeleteSelected)); return items.ToArray(); } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 602b7563ac..30c57733ed 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -29,6 +29,7 @@ using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Components; @@ -252,7 +253,7 @@ namespace osu.Game.Screens.Edit { Items = createFileMenuItems() }, - new MenuItem("Edit") + new MenuItem(CommonStrings.ButtonsEdit) { Items = new[] { diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 75c6a89a66..e799081115 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.Edit.Setup { @@ -27,7 +28,7 @@ namespace osu.Game.Screens.Edit.Setup { circleSizeSlider = new LabelledSliderBar { - Label = "Object Size", + Label = BeatmapsetsStrings.ShowStatsCs, FixedLabelWidth = LABEL_WIDTH, Description = "The size of all hit objects", Current = new BindableFloat(Beatmap.Difficulty.CircleSize) @@ -40,7 +41,7 @@ namespace osu.Game.Screens.Edit.Setup }, healthDrainSlider = new LabelledSliderBar { - Label = "Health Drain", + Label = BeatmapsetsStrings.ShowStatsDrain, FixedLabelWidth = LABEL_WIDTH, Description = "The rate of passive health drain throughout playable time", Current = new BindableFloat(Beatmap.Difficulty.DrainRate) @@ -53,7 +54,7 @@ namespace osu.Game.Screens.Edit.Setup }, approachRateSlider = new LabelledSliderBar { - Label = "Approach Rate", + Label = BeatmapsetsStrings.ShowStatsAr, FixedLabelWidth = LABEL_WIDTH, Description = "The speed at which objects are presented to the player", Current = new BindableFloat(Beatmap.Difficulty.ApproachRate) @@ -66,7 +67,7 @@ namespace osu.Game.Screens.Edit.Setup }, overallDifficultySlider = new LabelledSliderBar { - Label = "Overall Difficulty", + Label = BeatmapsetsStrings.ShowStatsAccuracy, FixedLabelWidth = LABEL_WIDTH, Description = "The harshness of hit windows and difficulty of special objects (ie. spinners)", Current = new BindableFloat(Beatmap.Difficulty.OverallDifficulty) diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 571dfb3f6f..6262b4c18b 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.Edit.Setup { @@ -48,15 +49,15 @@ namespace osu.Game.Screens.Edit.Setup creatorTextBox = createTextBox("Creator", metadata.Author.Username), difficultyTextBox = createTextBox("Difficulty Name", Beatmap.BeatmapInfo.DifficultyName), - sourceTextBox = createTextBox("Source", metadata.Source), - tagsTextBox = createTextBox("Tags", metadata.Tags) + sourceTextBox = createTextBox(BeatmapsetsStrings.ShowInfoSource, metadata.Source), + tagsTextBox = createTextBox(BeatmapsetsStrings.ShowInfoTags, metadata.Tags) }; foreach (var item in Children.OfType()) item.OnCommit += onCommit; } - private TTextBox createTextBox(string label, string initialValue) + private TTextBox createTextBox(LocalisableString label, string initialValue) where TTextBox : LabelledTextBox, new() => new TTextBox { diff --git a/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs index 08a0a3405e..f667a3c1d2 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; @@ -34,7 +35,7 @@ namespace osu.Game.Screens.OnlinePlay.Components private readonly Circle line; private readonly OsuSpriteText details; - public OverlinedHeader(string title) + public OverlinedHeader(LocalisableString title) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 25b36e0774..459b861d96 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -26,6 +26,7 @@ using osu.Game.Online; using osu.Game.Online.Chat; using osu.Game.Online.Rooms; using osu.Game.Overlays.BeatmapSet; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; @@ -449,7 +450,7 @@ namespace osu.Game.Screens.OnlinePlay Size = new Vector2(30, 30), Alpha = AllowEditing ? 1 : 0, Action = () => RequestEdit?.Invoke(Item), - TooltipText = "Edit" + TooltipText = CommonStrings.ButtonsEdit }, removeButton = new PlaylistRemoveButton { diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs index cf7e33fd63..799983342b 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; namespace osu.Game.Screens.OnlinePlay.Match.Components @@ -30,8 +31,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected override IEnumerable GetStatistics(ScoreInfo model) => new[] { - new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", model.DisplayAccuracy), - new LeaderboardScoreStatistic(FontAwesome.Solid.Sync, "Total Attempts", score.TotalAttempts.ToString()), + new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, RankingsStrings.StatAccuracy, model.DisplayAccuracy), + new LeaderboardScoreStatistic(FontAwesome.Solid.Sync, RankingsStrings.StatPlayCount, score.TotalAttempts.ToString()), new LeaderboardScoreStatistic(FontAwesome.Solid.Check, "Completed Beatmaps", score.CompletedBeatmaps.ToString()), }; } diff --git a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index cdd2ae0c9c..1828a072f8 100644 --- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -10,6 +10,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Rooms; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match.Components; using osuTK; @@ -49,7 +50,7 @@ namespace osu.Game.Screens.OnlinePlay.Match { RelativeSizeAxes = Axes.Y, Size = new Vector2(100, 1), - Text = "Edit", + Text = CommonStrings.ButtonsEdit, Action = () => OnEdit?.Invoke() }); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs index 7e442c6568..ef84c4b4fa 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsListHeader.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Game.Online.Multiplayer; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.OnlinePlay.Components; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants @@ -13,7 +14,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants private MultiplayerClient client { get; set; } public ParticipantsListHeader() - : base("Participants") + : base(RankingsStrings.SpotlightParticipants) { } diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 0d759bacf0..4a0bab84fa 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; using osuTK; @@ -158,7 +159,7 @@ namespace osu.Game.Screens.Play { new Drawable[] { - new MetadataLineLabel("Source"), + new MetadataLineLabel(BeatmapsetsStrings.ShowInfoSource), new MetadataLineInfo(metadata.Source) }, new Drawable[] @@ -213,7 +214,7 @@ namespace osu.Game.Screens.Play private class MetadataLineLabel : OsuSpriteText { - public MetadataLineLabel(string text) + public MetadataLineLabel(LocalisableString text) { Anchor = Anchor.TopRight; Origin = Anchor.TopRight; @@ -225,10 +226,10 @@ namespace osu.Game.Screens.Play private class MetadataLineInfo : OsuSpriteText { - public MetadataLineInfo(string text) + public MetadataLineInfo(LocalisableString text) { Margin = new MarginPadding { Left = 5 }; - Text = string.IsNullOrEmpty(text) ? @"-" : text; + Text = string.IsNullOrEmpty(text.ToString()) ? @"-" : text; } } } diff --git a/osu.Game/Screens/Play/Break/BreakInfo.cs b/osu.Game/Screens/Play/Break/BreakInfo.cs index 6349ebd9a7..f95e949920 100644 --- a/osu.Game/Screens/Play/Break/BreakInfo.cs +++ b/osu.Game/Screens/Play/Break/BreakInfo.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; using osuTK; @@ -42,7 +43,7 @@ namespace osu.Game.Screens.Play.Break Direction = FillDirection.Vertical, Children = new Drawable[] { - AccuracyDisplay = new PercentageBreakInfoLine("Accuracy"), + AccuracyDisplay = new PercentageBreakInfoLine(BeatmapsetsStrings.ShowStatsAccuracy), // See https://github.com/ppy/osu/discussions/15185 // RankDisplay = new BreakInfoLine("Rank"), diff --git a/osu.Game/Screens/Play/Break/BreakInfoLine.cs b/osu.Game/Screens/Play/Break/BreakInfoLine.cs index 87f514ffd5..4cae90e50f 100644 --- a/osu.Game/Screens/Play/Break/BreakInfoLine.cs +++ b/osu.Game/Screens/Play/Break/BreakInfoLine.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Play.Break private readonly string prefix; - public BreakInfoLine(string name, string prefix = @"") + public BreakInfoLine(LocalisableString name, string prefix = @"") { this.prefix = prefix; @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Play.Break public class PercentageBreakInfoLine : BreakInfoLine { - public PercentageBreakInfoLine(string name, string prefix = "") + public PercentageBreakInfoLine(LocalisableString name, string prefix = "") : base(name, prefix) { } diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 8b1e0ae83e..019a9f9730 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -19,6 +19,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Judgements; @@ -200,7 +201,7 @@ namespace osu.Game.Screens.Play.HUD { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Text = @"pp", + Text = BeatmapsetsStrings.ShowScoreboardHeaderspp, Font = OsuFont.Numeric.With(size: 8), Padding = new MarginPadding { Bottom = 1.5f }, // align baseline better } diff --git a/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs b/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs index 32de5333e1..90caf6f0f3 100644 --- a/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Configuration; +using osu.Game.Localisation; using osu.Game.Scoring; namespace osu.Game.Screens.Play.PlayerSettings @@ -20,7 +21,7 @@ namespace osu.Game.Screens.Play.PlayerSettings { Children = new Drawable[] { - beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" }, + beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapHitsounds }, new BeatmapOffsetControl { ReferenceScore = { BindTarget = ReferenceScore }, diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index 81950efa9e..a999b32cb4 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Graphics.Sprites; +using osu.Game.Localisation; namespace osu.Game.Screens.Play.PlayerSettings { @@ -23,7 +24,7 @@ namespace osu.Game.Screens.Play.PlayerSettings { new OsuSpriteText { - Text = "Background dim:" + Text = GameplaySettingsStrings.BackgroundDim }, dimSliderBar = new PlayerSliderBar { @@ -31,7 +32,7 @@ namespace osu.Game.Screens.Play.PlayerSettings }, new OsuSpriteText { - Text = "Background blur:" + Text = GameplaySettingsStrings.BackgroundBlur }, blurSliderBar = new PlayerSliderBar { @@ -41,9 +42,9 @@ namespace osu.Game.Screens.Play.PlayerSettings { Text = "Toggles:" }, - showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboard / Video" }, - beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" }, - beatmapColorsToggle = new PlayerCheckbox { LabelText = "Beatmap colours" }, + showStoryboardToggle = new PlayerCheckbox { LabelText = GraphicsSettingsStrings.StoryboardVideo }, + beatmapSkinsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapSkins }, + beatmapColorsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapColours }, }; } diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index f9aff28bef..bb286f41c0 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -9,9 +9,11 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; @@ -127,8 +129,8 @@ namespace osu.Game.Screens.Ranking.Contracted Spacing = new Vector2(0, 5), Children = new[] { - createStatistic("Max Combo", $"x{score.MaxCombo}"), - createStatistic("Accuracy", $"{score.Accuracy.FormatAccuracy()}"), + createStatistic(BeatmapsetsStrings.ShowScoreboardHeadersCombo, $"x{score.MaxCombo}"), + createStatistic(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy, $"{score.Accuracy.FormatAccuracy()}"), } }, new ModFlowDisplay @@ -200,7 +202,7 @@ namespace osu.Game.Screens.Ranking.Contracted private Drawable createStatistic(HitResultDisplayStatistic result) => createStatistic(result.DisplayName, result.MaxCount == null ? $"{result.Count}" : $"{result.Count}/{result.MaxCount}"); - private Drawable createStatistic(string key, string value) => new Container + private Drawable createStatistic(LocalisableString key, string value) => new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs index 476c9fb42f..25a644d8d9 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs @@ -6,6 +6,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.Ranking.Expanded.Accuracy; using osu.Game.Utils; using osuTK; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics /// /// The accuracy to display. public AccuracyStatistic(double accuracy) - : base("accuracy") + : base(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy) { this.accuracy = accuracy; } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs index 0e42ec026a..cb25736f6e 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.Ranking.Expanded.Accuracy; using osuTK; @@ -27,7 +28,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics /// The combo to be displayed. /// The maximum value of . public ComboStatistic(int combo, int? maxCombo) - : base("combo", combo, maxCombo) + : base(BeatmapsetsStrings.ShowScoreboardHeadersCombo, combo, maxCombo) { isPerfect = combo == maxCombo; } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs index d37f6c5e5f..b1c72173da 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics /// The name of the statistic. /// The value to display. /// The maximum value of . Not displayed if null. - public CounterStatistic(string header, int count, int? maxCount = null) + public CounterStatistic(LocalisableString header, int count, int? maxCount = null) : base(header) { this.count = count; diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index 95f017d625..c681946a2f 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; namespace osu.Game.Screens.Ranking.Expanded.Statistics @@ -23,7 +24,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics private RollingCounter counter; public PerformanceStatistic(ScoreInfo score) - : base("PP") + : base(BeatmapsetsStrings.ShowScoreboardHeaderspp) { this.score = score; } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs index 9206c58bc9..c034abc916 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs @@ -3,10 +3,12 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -19,14 +21,14 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics { protected SpriteText HeaderText { get; private set; } - private readonly string header; + private readonly LocalisableString header; private Drawable content; /// /// Creates a new . /// /// The name of the statistic. - protected StatisticDisplay(string header) + protected StatisticDisplay(LocalisableString header) { this.header = header; RelativeSizeAxes = Axes.X; @@ -60,7 +62,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold), - Text = header.ToUpperInvariant(), + Text = header.ToUpper(), } } }, diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index bbe0a37d8e..9ff1574fe4 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -16,6 +16,7 @@ using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.BeatmapSet; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.Select.Details; using osuTK; using osuTK.Graphics; @@ -155,7 +156,7 @@ namespace osu.Game.Screens.Select { new OsuSpriteText { - Text = "Points of Failure", + Text = BeatmapsetsStrings.ShowInfoPointsOfFailure, Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 14), }, failRetryGraph = new FailRetryGraph diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 3576b77ae8..c6037d1bd6 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -24,6 +24,7 @@ using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Graphics; @@ -136,14 +137,7 @@ namespace osu.Game.Screens.Select.Carousel }, new OsuSpriteText { - Text = "mapped by", - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft - }, - new OsuSpriteText - { - Text = $"{beatmapInfo.Metadata.Author.Username}", - Font = OsuFont.GetFont(italics: true), + Text = BeatmapsetsStrings.ShowDetailsMappedBy(beatmapInfo.Metadata.Author.Username), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft }, @@ -235,7 +229,7 @@ namespace osu.Game.Screens.Select.Carousel items.Add(new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested(beatmapInfo))); if (editRequested != null) - items.Add(new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested(beatmapInfo))); + items.Add(new OsuMenuItem(CommonStrings.ButtonsEdit, MenuItemType.Standard, () => editRequested(beatmapInfo))); if (beatmapInfo.OnlineID > 0 && beatmapOverlay != null) items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmapInfo.OnlineID))); @@ -250,7 +244,7 @@ namespace osu.Game.Screens.Select.Carousel } if (hideRequested != null) - items.Add(new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested(beatmapInfo))); + items.Add(new OsuMenuItem(CommonStrings.ButtonsHide, MenuItemType.Destructive, () => hideRequested(beatmapInfo))); return items.ToArray(); } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index adaaa6425c..a6f2520472 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -21,6 +21,7 @@ using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Configuration; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; namespace osu.Game.Screens.Select.Details @@ -63,10 +64,10 @@ namespace osu.Game.Screens.Select.Details Children = new[] { FirstValue = new StatisticRow(), // circle size/key amount - HpDrain = new StatisticRow { Title = "HP Drain" }, - Accuracy = new StatisticRow { Title = "Accuracy" }, - ApproachRate = new StatisticRow { Title = "Approach Rate" }, - starDifficulty = new StatisticRow(10, true) { Title = "Star Difficulty" }, + HpDrain = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsDrain }, + Accuracy = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAccuracy }, + ApproachRate = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAr }, + starDifficulty = new StatisticRow(10, true) { Title = BeatmapsetsStrings.ShowStatsStars }, }, }; } @@ -120,12 +121,12 @@ namespace osu.Game.Screens.Select.Details case 3: // Account for mania differences locally for now // Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes - FirstValue.Title = "Key Count"; + FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania; FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, null); break; default: - FirstValue.Title = "Circle Size"; + FirstValue.Title = BeatmapsetsStrings.ShowStatsCs; FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize); break; } diff --git a/osu.Game/Screens/Select/Filter/SortMode.cs b/osu.Game/Screens/Select/Filter/SortMode.cs index 18c5d713e1..1ab54fa069 100644 --- a/osu.Game/Screens/Select/Filter/SortMode.cs +++ b/osu.Game/Screens/Select/Filter/SortMode.cs @@ -2,36 +2,38 @@ // See the LICENCE file in the repository root for full licence text. using System.ComponentModel; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.Select.Filter { public enum SortMode { - [Description("Artist")] + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingArtist))] Artist, [Description("Author")] Author, - [Description("BPM")] + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatsBpm))] BPM, [Description("Date Added")] DateAdded, - [Description("Difficulty")] + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingDifficulty))] Difficulty, - [Description("Length")] + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.ArtistTracksLength))] Length, - [Description("Rank Achieved")] + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchFiltersRank))] RankAchieved, - [Description("Source")] + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoSource))] Source, - [Description("Title")] + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingTitle))] Title, } } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index b53d64260a..65dde146bb 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Screens.Select.Filter; using osuTK; @@ -139,7 +140,7 @@ namespace osu.Game.Screens.Select }, new OsuSpriteText { - Text = "Sort by", + Text = SortStrings.Default, Font = OsuFont.GetFont(size: 14), Margin = new MarginPadding(5), Anchor = Anchor.BottomRight, diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index b5fdbd225f..1a8b69d859 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -13,6 +13,7 @@ using osuTK.Input; using osu.Game.Graphics.Containers; using osu.Framework.Input.Events; using System.Linq; +using osu.Framework.Localisation; namespace osu.Game.Screens.Select.Options { @@ -63,7 +64,7 @@ namespace osu.Game.Screens.Select.Options /// Colour of the button. /// Icon of the button. /// Binding the button does. - public void AddButton(string firstLine, string secondLine, IconUsage icon, Color4 colour, Action action) + public void AddButton(LocalisableString firstLine, string secondLine, IconUsage icon, Color4 colour, Action action) { var button = new BeatmapOptionsButton { diff --git a/osu.Game/Users/UserStatus.cs b/osu.Game/Users/UserStatus.cs index 21c18413f4..7f275b3b2a 100644 --- a/osu.Game/Users/UserStatus.cs +++ b/osu.Game/Users/UserStatus.cs @@ -1,20 +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 osu.Framework.Localisation; using osuTK.Graphics; using osu.Game.Graphics; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Users { public abstract class UserStatus { - public abstract string Message { get; } + public abstract LocalisableString Message { get; } public abstract Color4 GetAppropriateColour(OsuColour colours); } public class UserStatusOnline : UserStatus { - public override string Message => @"Online"; + public override LocalisableString Message => UsersStrings.StatusOnline; public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenLight; } @@ -25,13 +27,13 @@ namespace osu.Game.Users public class UserStatusOffline : UserStatus { - public override string Message => @"Offline"; + public override LocalisableString Message => UsersStrings.StatusOffline; public override Color4 GetAppropriateColour(OsuColour colours) => Color4.Black; } public class UserStatusDoNotDisturb : UserStatus { - public override string Message => @"Do not disturb"; + public override LocalisableString Message => "Do not disturb"; public override Color4 GetAppropriateColour(OsuColour colours) => colours.RedDark; } } From 0146949ad60c45e788230e6336bfb29aaaa3491e Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 20 Apr 2022 16:52:24 -0700 Subject: [PATCH 126/134] Fix failing string comparison tests --- osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 7ceae0a69b..8af70df48a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; @@ -55,7 +56,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("no mods selected", () => SelectedMods.Value = Array.Empty()); - AddAssert("first bar text is Circle Size", () => advancedStats.ChildrenOfType().First().Text == "Circle Size"); + AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType().First().Text == BeatmapsetsStrings.ShowStatsCs); AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue)); AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.HpDrain)); AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy)); @@ -78,7 +79,7 @@ namespace osu.Game.Tests.Visual.SongSelect StarRating = 8 }); - AddAssert("first bar text is Key Count", () => advancedStats.ChildrenOfType().First().Text == "Key Count"); + AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType().First().Text == BeatmapsetsStrings.ShowStatsCsMania); } [Test] From a9163727db6195bee3a4212ad6d4c3cf8b1d0804 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 13:44:14 +0900 Subject: [PATCH 127/134] Mark selected channel null in test when selector is activated --- osu.Game.Tests/Visual/Online/TestSceneChannelList.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs index d6a1e71f0d..a3bfbd47a3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChannelList.cs @@ -104,6 +104,7 @@ namespace osu.Game.Tests.Visual.Online channelList.SelectorActive.BindValueChanged(change => { selectorText.Text = $"Channel Selector Active: {change.NewValue}"; + selected.Value = null; }, true); selected.BindValueChanged(change => From 9f525ee26743ce70b1f200979c1664841fef0896 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 14:09:51 +0900 Subject: [PATCH 128/134] Revert localisation of spectate/watch button --- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index e036f1d304..9807a65250 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -139,7 +139,7 @@ namespace osu.Game.Overlays.Dashboard new PurpleTriangleButton { RelativeSizeAxes = Axes.X, - Text = CommonStrings.ButtonsWatchTo1, + Text = "Spectate", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), From 848366416e7595b1dce036fa186e0f1369b66c5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 14:10:58 +0900 Subject: [PATCH 129/134] Revert string type changes in `MetadataLineInfo` --- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 4a0bab84fa..e8021d4065 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -226,10 +226,10 @@ namespace osu.Game.Screens.Play private class MetadataLineInfo : OsuSpriteText { - public MetadataLineInfo(LocalisableString text) + public MetadataLineInfo(string text) { Margin = new MarginPadding { Left = 5 }; - Text = string.IsNullOrEmpty(text.ToString()) ? @"-" : text; + Text = string.IsNullOrEmpty(text) ? @"-" : text; } } } From b4c208be63db5babf41d3301de4f07f59379a7c9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 15:34:01 +0900 Subject: [PATCH 130/134] Move Code Quality check to top of checks list --- .github/workflows/ci.yml | 110 +++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e7f9628fc..f2066f27de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,60 @@ on: [push, pull_request] name: Continuous Integration jobs: + inspect-code: + name: Code Quality + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + # FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side. + # https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e + - name: Install .NET 3.1.x LTS + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "3.1.x" + + - name: Install .NET 6.0.x + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "6.0.x" + + - name: Restore Tools + run: dotnet tool restore + + - name: Restore Packages + run: dotnet restore + + - name: Restore inspectcode cache + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/inspectcode + key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }} + + - name: CodeFileSanity + run: | + # TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround. + # FIXME: Suppress warnings from templates project + exit_code=0 + while read -r line; do + if [[ ! -z "$line" ]]; then + echo "::error::$line" + exit_code=1 + fi + done <<< $(dotnet codefilesanity) + exit $exit_code + + # Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded. + # - name: .NET Format (Dry Run) + # run: dotnet format --dry-run --check + + - name: InspectCode + run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN + + - name: NVika + run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors + test: name: Test runs-on: ${{matrix.os.fullname}} @@ -93,58 +147,4 @@ jobs: # cannot accept .sln(f) files as arguments. # Build just the main game for now. - name: Build - run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug - - inspect-code: - name: Code Quality - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - # FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side. - # https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e - - name: Install .NET 3.1.x LTS - uses: actions/setup-dotnet@v1 - with: - dotnet-version: "3.1.x" - - - name: Install .NET 6.0.x - uses: actions/setup-dotnet@v1 - with: - dotnet-version: "6.0.x" - - - name: Restore Tools - run: dotnet tool restore - - - name: Restore Packages - run: dotnet restore - - - name: Restore inspectcode cache - uses: actions/cache@v3 - with: - path: ${{ github.workspace }}/inspectcode - key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }} - - - name: CodeFileSanity - run: | - # TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround. - # FIXME: Suppress warnings from templates project - exit_code=0 - while read -r line; do - if [[ ! -z "$line" ]]; then - echo "::error::$line" - exit_code=1 - fi - done <<< $(dotnet codefilesanity) - exit $exit_code - - # Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded. - # - name: .NET Format (Dry Run) - # run: dotnet format --dry-run --check - - - name: InspectCode - run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN - - - name: NVika - run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors + run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug \ No newline at end of file From 54ca4b9d36f55bd9e3636b1c88eea0ef553753e6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Apr 2022 15:34:14 +0900 Subject: [PATCH 131/134] Remove unused using --- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 9807a65250..a9312e9a3a 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -14,7 +14,6 @@ using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Spectator; -using osu.Game.Resources.Localisation.Web; using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; From 414408140e0e66e394dafc1738c2a2c574496f87 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 21 Apr 2022 10:16:52 +0300 Subject: [PATCH 132/134] Improve AnchorOriginVisualiser --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index 0a4bd1d75f..1860c6006c 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -146,8 +146,10 @@ namespace osu.Game.Skinning.Editor { anchorLine = new Box { - Colour = Color4.Yellow, Height = 2, + Origin = Anchor.CentreLeft, + Colour = Color4.Yellow, + EdgeSmoothness = Vector2.One }, originBox = new Box { From 4d3044b841c4f5d9b73a144aaabf7bd1fc7b1725 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 00:49:15 +0900 Subject: [PATCH 133/134] 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 8b22df418f..6b06305bf9 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0a102510e7..c57acb3b7f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 2f0facea18..d7f89b802b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 832d37b2c2c9b5f2cde6bec90804e6dab36e8e0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 00:52:44 +0900 Subject: [PATCH 134/134] Update screen transition events to use new event args --- .../Background/TestSceneUserDimBackgrounds.cs | 4 ++-- .../Visual/Gameplay/TestScenePause.cs | 4 ++-- .../Navigation/TestScenePerformFromScreen.cs | 8 ++++---- .../Visual/TestMultiplayerComponents.cs | 4 ++-- .../Graphics/Containers/ScalingContainer.cs | 2 +- .../AccountCreation/AccountCreationScreen.cs | 12 +++++------ .../Overlays/AccountCreation/ScreenEntry.cs | 4 ++-- .../Overlays/AccountCreation/ScreenWarning.cs | 4 ++-- .../Maintenance/DirectorySelectScreen.cs | 4 ++-- .../Maintenance/MigrationRunScreen.cs | 8 ++++---- .../StableDirectorySelectScreen.cs | 4 ++-- osu.Game/Screens/BackgroundScreen.cs | 16 +++++++-------- .../Backgrounds/BackgroundScreenBlack.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 16 +++++++-------- .../Screens/Edit/GameplayTest/EditorPlayer.cs | 8 ++++---- .../Edit/GameplayTest/EditorPlayerLoader.cs | 4 ++-- osu.Game/Screens/Import/FileImportScreen.cs | 8 ++++---- osu.Game/Screens/Loader.cs | 4 ++-- osu.Game/Screens/Menu/Disclaimer.cs | 4 ++-- osu.Game/Screens/Menu/IntroCircles.cs | 4 ++-- osu.Game/Screens/Menu/IntroScreen.cs | 12 +++++------ osu.Game/Screens/Menu/IntroTriangles.cs | 8 ++++---- osu.Game/Screens/Menu/IntroWelcome.cs | 4 ++-- osu.Game/Screens/Menu/MainMenu.cs | 18 ++++++++--------- .../Components/OnlinePlayBackgroundScreen.cs | 8 ++++---- .../Lounge/LoungeBackgroundScreen.cs | 2 +- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 16 +++++++-------- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 16 +++++++-------- .../OnlinePlay/Multiplayer/Multiplayer.cs | 6 +++--- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 6 +++--- .../Multiplayer/MultiplayerMatchSubScreen.cs | 6 +++--- .../Multiplayer/MultiplayerPlayerLoader.cs | 6 +++--- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 20 +++++++++---------- .../OnlinePlay/OnlinePlaySongSelect.cs | 4 ++-- .../Screens/OnlinePlay/OnlinePlaySubScreen.cs | 16 +++++++-------- .../OnlinePlay/Playlists/PlaylistsPlayer.cs | 4 ++-- osu.Game/Screens/OsuScreen.cs | 16 +++++++-------- osu.Game/Screens/Play/Player.cs | 12 +++++------ osu.Game/Screens/Play/PlayerLoader.cs | 16 +++++++-------- osu.Game/Screens/Play/ReplayPlayerLoader.cs | 4 ++-- osu.Game/Screens/Play/SoloSpectator.cs | 4 ++-- osu.Game/Screens/Play/SoloSpectatorPlayer.cs | 4 ++-- osu.Game/Screens/Play/SpectatorPlayer.cs | 4 ++-- .../Screens/Play/SpectatorPlayerLoader.cs | 4 ++-- osu.Game/Screens/Play/SubmittingPlayer.cs | 4 ++-- osu.Game/Screens/Ranking/ResultsScreen.cs | 8 ++++---- osu.Game/Screens/ScreenWhiteBox.cs | 12 +++++------ osu.Game/Screens/Select/PlaySongSelect.cs | 4 ++-- osu.Game/Screens/Select/SongSelect.cs | 16 +++++++-------- osu.Game/Tests/Visual/EditorTestScene.cs | 4 ++-- 50 files changed, 194 insertions(+), 194 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 9f708ace70..f7140537ee 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -359,9 +359,9 @@ namespace osu.Game.Tests.Visual.Background protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); ApplyToBackground(b => ReplacesBackground.BindTo(b.StoryboardReplacesBackground)); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index ea0255ab76..a224a78531 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -389,9 +389,9 @@ namespace osu.Game.Tests.Visual.Gameplay public void ExitViaQuickExit() => PerformExit(false); - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); GameplayClockContainer.Stop(); } } diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 559e6ddd89..2ce914ba3d 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -230,7 +230,7 @@ namespace osu.Game.Tests.Visual.Navigation public int ExitAttempts { get; private set; } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { ExitAttempts++; @@ -240,7 +240,7 @@ namespace osu.Game.Tests.Visual.Navigation return true; } - return base.OnExiting(next); + return base.OnExiting(e); } } @@ -257,7 +257,7 @@ namespace osu.Game.Tests.Visual.Navigation SubScreenStack.Push(Blocker = new DialogBlockingScreen()); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (SubScreenStack.CurrentScreen != null) { @@ -265,7 +265,7 @@ namespace osu.Game.Tests.Visual.Navigation return true; } - return base.OnExiting(next); + return base.OnExiting(e); } } } diff --git a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs index c43ed744bd..4ab201ef46 100644 --- a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs +++ b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs @@ -80,10 +80,10 @@ namespace osu.Game.Tests.Visual public override bool OnBackButton() => (screenStack.CurrentScreen as OsuScreen)?.OnBackButton() ?? base.OnBackButton(); - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (screenStack.CurrentScreen == null) - return base.OnExiting(next); + return base.OnExiting(e); screenStack.Exit(); return true; diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index ca8b6f388f..89432595f3 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -209,7 +209,7 @@ namespace osu.Game.Graphics.Containers { protected override bool AllowStoryboardBackground => false; - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { this.FadeInFromZero(4000, Easing.OutQuint); } diff --git a/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs b/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs index 7e2ae405cb..6aef358b2e 100644 --- a/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs +++ b/osu.Game/Overlays/AccountCreation/AccountCreationScreen.cs @@ -8,21 +8,21 @@ namespace osu.Game.Overlays.AccountCreation { public abstract class AccountCreationScreen : Screen { - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); this.FadeOut().Delay(200).FadeIn(200); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); this.FadeIn(200); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); this.FadeOut(200); } } diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index 6cf3fb4267..1be1321d85 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -147,9 +147,9 @@ namespace osu.Game.Overlays.AccountCreation d.Colour = password.Length == 0 ? Color4.White : Interpolation.ValueAt(password.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In); } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); loadingLayer.Hide(); if (host?.OnScreenKeyboardOverlapsGameWindow != true) diff --git a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs index 3d46e9ed94..780a79f8f9 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.AccountCreation private const string help_centre_url = "/help/wiki/Help_Centre#login"; - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { if (string.IsNullOrEmpty(api?.ProvidedUsername) || game?.UseDevelopmentServer == true) { @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.AccountCreation return; } - base.OnEntering(last); + base.OnEntering(e); } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs index 98bc8d88be..c7fd248842 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs @@ -124,9 +124,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance base.LoadComplete(); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); this.FadeOut(250); } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs index 10d6f0aef5..b7b797936e 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs @@ -124,20 +124,20 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected virtual bool PerformMigration() => game?.Migrate(destination.FullName) != false; - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); this.FadeOut().Delay(250).Then().FadeIn(250); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { // block until migration is finished if (migrationTask?.IsCompleted == false) return true; - return base.OnExiting(next); + return base.OnExiting(e); } } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 4aea05fb14..86934ae514 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -30,10 +30,10 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance this.Exit(); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { taskCompletionSource.TrySetCanceled(); - return base.OnExiting(next); + return base.OnExiting(e); } } } diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index a706934cce..6084ec4b01 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens Scale = new Vector2(1 + x_movement_amount / DrawSize.X * 2); } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { if (animateOnEnter) { @@ -59,16 +59,16 @@ namespace osu.Game.Screens this.MoveToX(0, TRANSITION_LENGTH, Easing.InOutQuart); } - base.OnEntering(last); + base.OnEntering(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { this.MoveToX(-x_movement_amount, TRANSITION_LENGTH, Easing.InOutQuart); - base.OnSuspending(next); + base.OnSuspending(e); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (IsLoaded) { @@ -76,14 +76,14 @@ namespace osu.Game.Screens this.MoveToX(x_movement_amount, TRANSITION_LENGTH, Easing.OutExpo); } - return base.OnExiting(next); + return base.OnExiting(e); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { if (IsLoaded) this.MoveToX(0, TRANSITION_LENGTH, Easing.OutExpo); - base.OnResuming(last); + base.OnResuming(e); } } } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs index 9e2559cc56..d946fd41d9 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBlack.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Backgrounds }; } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { Show(); } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 30c57733ed..3fde033587 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -560,16 +560,16 @@ namespace osu.Game.Screens.Edit { } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); dimBackground(); resetTrack(true); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); dimBackground(); } @@ -585,7 +585,7 @@ namespace osu.Game.Screens.Edit }); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (!ExitConfirmed) { @@ -613,12 +613,12 @@ namespace osu.Game.Screens.Edit refetchBeatmap(); - return base.OnExiting(next); + return base.OnExiting(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); clock.Stop(); refetchBeatmap(); } diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs index d9e19df350..f7e450b0e2 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs @@ -44,9 +44,9 @@ namespace osu.Game.Screens.Edit.GameplayTest protected override bool CheckModsAllowFailure() => false; // never fail. - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); // finish alpha transforms on entering to avoid gameplay starting in a half-hidden state. // the finish calls are purposefully not propagated to children to avoid messing up their state. @@ -54,13 +54,13 @@ namespace osu.Game.Screens.Edit.GameplayTest GameplayClockContainer.FinishTransforms(false, nameof(Alpha)); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { musicController.Stop(); editorState.Time = GameplayClockContainer.CurrentTime; editor.RestoreState(editorState); - return base.OnExiting(next); + return base.OnExiting(e); } } } diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs index addc79ba61..c16bb8677c 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs @@ -19,9 +19,9 @@ namespace osu.Game.Screens.Edit.GameplayTest { } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); MetadataInfo.FinishTransforms(true); } diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 09870e0bab..32ce54aa29 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -118,20 +118,20 @@ namespace osu.Game.Screens.Import fileSelector.CurrentPath.BindValueChanged(directoryChanged); } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); contentContainer.ScaleTo(0.95f).ScaleTo(1, duration, Easing.OutQuint); this.FadeInFromZero(duration); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { contentContainer.ScaleTo(0.95f, duration, Easing.OutQuint); this.FadeOut(duration, Easing.OutQuint); - return base.OnExiting(next); + return base.OnExiting(e); } private void directoryChanged(ValueChangedEvent _) diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index a72ba89dfa..52e83c9e98 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -69,9 +69,9 @@ namespace osu.Game.Screens private EFToRealmMigrator realmMigrator; - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); LoadComponentAsync(precompiler = CreateShaderPrecompiler(), AddInternal); diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 22151db0dd..24412cd85e 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -171,9 +171,9 @@ namespace osu.Game.Screens.Menu ((IBindable)currentUser).BindTo(api.LocalUser); } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); icon.RotateTo(10); icon.FadeOut(); diff --git a/osu.Game/Screens/Menu/IntroCircles.cs b/osu.Game/Screens/Menu/IntroCircles.cs index 2792d05f75..00e2de62f0 100644 --- a/osu.Game/Screens/Menu/IntroCircles.cs +++ b/osu.Game/Screens/Menu/IntroCircles.cs @@ -57,10 +57,10 @@ namespace osu.Game.Screens.Menu } } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { this.FadeOut(300); - base.OnSuspending(next); + base.OnSuspending(e); } } } diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 5575d3681f..d4072d6202 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -164,9 +164,9 @@ namespace osu.Game.Screens.Menu } } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); ensureEventuallyArrivingAtMenu(); } @@ -194,7 +194,7 @@ namespace osu.Game.Screens.Menu }, 5000); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { this.FadeIn(300); @@ -237,12 +237,12 @@ namespace osu.Game.Screens.Menu //don't want to fade out completely else we will stop running updates. Game.FadeTo(0.01f, fadeOutTime).OnComplete(_ => this.Exit()); - base.OnResuming(last); + base.OnResuming(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); initialBeatmap = null; } diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index b6b6bf2ad7..ba8314f103 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -89,9 +89,9 @@ namespace osu.Game.Screens.Menu } } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); // ensure the background is shown, even if the TriangleIntroSequence failed to do so. background.ApplyToBackground(b => b.Show()); @@ -100,9 +100,9 @@ namespace osu.Game.Screens.Menu intro.Expire(); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); background.FadeOut(100); } diff --git a/osu.Game/Screens/Menu/IntroWelcome.cs b/osu.Game/Screens/Menu/IntroWelcome.cs index 27eaa7eb3a..9a6c949cad 100644 --- a/osu.Game/Screens/Menu/IntroWelcome.cs +++ b/osu.Game/Screens/Menu/IntroWelcome.cs @@ -106,9 +106,9 @@ namespace osu.Game.Screens.Menu } } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); background.FadeOut(100); } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 08df06816b..c9ce077695 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -176,12 +176,12 @@ namespace osu.Game.Screens.Menu [Resolved] private Storage storage { get; set; } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); buttons.FadeInFromZero(500); - if (last is IntroScreen && musicController.TrackLoaded) + if (e.Last is IntroScreen && musicController.TrackLoaded) { var track = musicController.CurrentTrack; @@ -249,9 +249,9 @@ namespace osu.Game.Screens.Menu seq.OnAbort(_ => buttons.SetOsuLogo(null)); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); buttons.State = ButtonSystemState.EnteringMode; @@ -261,9 +261,9 @@ namespace osu.Game.Screens.Menu sideFlashes.FadeOut(64, Easing.OutQuint); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); ApplyToBackground(b => (b as BackgroundScreenDefault)?.Next()); @@ -273,7 +273,7 @@ namespace osu.Game.Screens.Menu musicController.EnsurePlayingSomething(); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (!exitConfirmed && dialogOverlay != null) { @@ -291,7 +291,7 @@ namespace osu.Game.Screens.Menu songTicker.Hide(); this.FadeOut(3000); - return base.OnExiting(next); + return base.OnExiting(e); } public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset) diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs index 8906bebf0e..9e964de31e 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs @@ -91,15 +91,15 @@ namespace osu.Game.Screens.OnlinePlay.Components AddInternal(background = newBackground); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); this.MoveToX(0, TRANSITION_LENGTH); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - bool result = base.OnExiting(next); + bool result = base.OnExiting(e); this.MoveToX(0); return result; } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs index 926c35c5da..52a902f5da 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge playlist.Clear(); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { // This screen never exits. return true; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index a2d3b7f4fc..ec55ae79ce 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -238,15 +238,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge #endregion - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); onReturning(); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); Debug.Assert(selectionLease != null); @@ -261,16 +261,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge onReturning(); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { onLeaving(); - return base.OnExiting(next); + return base.OnExiting(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { onLeaving(); - base.OnSuspending(next); + base.OnSuspending(e); } protected override void OnFocus(FocusEvent e) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index a382f65d84..cc1f842f8c 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -290,35 +290,35 @@ namespace osu.Game.Screens.OnlinePlay.Match protected void ShowUserModSelect() => userModsSelectOverlay.Show(); - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); beginHandlingTrack(); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { endHandlingTrack(); - base.OnSuspending(next); + base.OnSuspending(e); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); updateWorkingBeatmap(); beginHandlingTrack(); Scheduler.AddOnce(UpdateMods); Scheduler.AddOnce(updateRuleset); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { RoomManager?.PartRoom(); Mods.Value = Array.Empty(); endHandlingTrack(); - return base.OnExiting(next); + return base.OnExiting(e); } protected void StartPlay() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 2482d52492..66f6935bcc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -35,14 +35,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer transitionFromResults(); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); if (client.Room == null) return; - if (!(last is MultiplayerPlayerLoader playerLoader)) + if (!(e.Last is MultiplayerPlayerLoader playerLoader)) return; // If gameplay wasn't finished, then we have a simple path back to the idle state by aborting gameplay. diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 5cdec52bc2..a05f248d3a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -25,13 +25,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); // Upon having left a room, we don't know whether we were the only participant, and whether the room is now closed as a result of leaving it. // To work around this, temporarily remove the room and trigger an immediate listing poll. - if (last is MultiplayerMatchSubScreen match) + if (e.Last is MultiplayerMatchSubScreen match) { RoomManager.RemoveRoom(match.Room); ListingPollingComponent.PollImmediately(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 0dbf73fc5b..769873f74c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -242,14 +242,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private bool exitConfirmed; - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { // the room may not be left immediately after a disconnection due to async flow, // so checking the IsConnected status is also required. if (client.Room == null || !client.IsConnected.Value) { // room has not been created yet; exit immediately. - return base.OnExiting(next); + return base.OnExiting(e); } if (!exitConfirmed && dialogOverlay != null) @@ -268,7 +268,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return true; } - return base.OnExiting(next); + return base.OnExiting(e); } private ModSettingChangeTracker modSettingChangeTracker; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs index 772651727e..53dea83f18 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs @@ -18,10 +18,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); - player = (Player)next; + base.OnSuspending(e); + player = (Player)e.Next; } } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index c56d04d5ac..ff4225e155 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -110,7 +110,7 @@ namespace osu.Game.Screens.OnlinePlay } } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { this.FadeIn(); waves.Show(); @@ -118,35 +118,35 @@ namespace osu.Game.Screens.OnlinePlay Mods.SetDefault(); if (loungeSubScreen.IsCurrentScreen()) - loungeSubScreen.OnEntering(last); + loungeSubScreen.OnEntering(e); else loungeSubScreen.MakeCurrent(); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { this.FadeIn(250); this.ScaleTo(1, 250, Easing.OutSine); Debug.Assert(screenStack.CurrentScreen != null); - screenStack.CurrentScreen.OnResuming(last); + screenStack.CurrentScreen.OnResuming(e); - base.OnResuming(last); + base.OnResuming(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { this.ScaleTo(1.1f, 250, Easing.InSine); this.FadeOut(250); Debug.Assert(screenStack.CurrentScreen != null); - screenStack.CurrentScreen.OnSuspending(next); + screenStack.CurrentScreen.OnSuspending(e); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { var subScreen = screenStack.CurrentScreen as Drawable; - if (subScreen?.IsLoaded == true && screenStack.CurrentScreen.OnExiting(next)) + if (subScreen?.IsLoaded == true && screenStack.CurrentScreen.OnExiting(e)) return true; RoomManager.PartRoom(); @@ -155,7 +155,7 @@ namespace osu.Game.Screens.OnlinePlay this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut(); - base.OnExiting(next); + base.OnExiting(e); return false; } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 7b64784316..6a559dbb2c 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -141,7 +141,7 @@ namespace osu.Game.Screens.OnlinePlay return base.OnBackButton(); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (!itemSelected) { @@ -150,7 +150,7 @@ namespace osu.Game.Screens.OnlinePlay Mods.Value = initialMods; } - return base.OnExiting(next); + return base.OnExiting(e); } protected override ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index 3411c4afb1..07e0f60011 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -27,28 +27,28 @@ namespace osu.Game.Screens.OnlinePlay public const double DISAPPEAR_DURATION = 500; - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); this.FadeInFromZero(APPEAR_DURATION, Easing.OutQuint); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - base.OnExiting(next); + base.OnExiting(e); this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); return false; } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); this.FadeIn(APPEAR_DURATION, Easing.OutQuint); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 5a7762a3d8..5cba8676c5 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -45,9 +45,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods"); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - if (base.OnExiting(next)) + if (base.OnExiting(e)) return true; Exited?.Invoke(); diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index ed4901e1fa..4035d168e1 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -171,7 +171,7 @@ namespace osu.Game.Screens background.ApplyToBackground(action); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { if (PlayResumeSound) sampleExit?.Play(); @@ -183,19 +183,19 @@ namespace osu.Game.Screens if (trackAdjustmentStateAtSuspend != null) musicController.AllowTrackAdjustments = trackAdjustmentStateAtSuspend.Value; - base.OnResuming(last); + base.OnResuming(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); trackAdjustmentStateAtSuspend = musicController.AllowTrackAdjustments; onSuspendingLogo(); } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { applyArrivingDefaults(false); @@ -210,15 +210,15 @@ namespace osu.Game.Screens } background = backgroundStack?.CurrentScreen as BackgroundScreen; - base.OnEntering(last); + base.OnEntering(e); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { if (ValidForResume && logo != null) onExitingLogo(); - if (base.OnExiting(next)) + if (base.OnExiting(e)) return true; if (ownedBackground != null && backgroundStack?.CurrentScreen == ownedBackground) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f99b07c313..2d5a67758a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -917,9 +917,9 @@ namespace osu.Game.Screens.Play #region Screen Logic - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); if (!LoadedBeatmapSuccessfully) return; @@ -985,15 +985,15 @@ namespace osu.Game.Screens.Play GameplayClockContainer.Reset(true); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { screenSuspension?.RemoveAndDisposeImmediately(); fadeOut(); - base.OnSuspending(next); + base.OnSuspending(e); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { screenSuspension?.RemoveAndDisposeImmediately(); failAnimationLayer?.RemoveFilters(); @@ -1024,7 +1024,7 @@ namespace osu.Game.Screens.Play musicController.ResetTrackAdjustments(); fadeOut(); - return base.OnExiting(next); + return base.OnExiting(e); } /// diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index aea39bc8ff..494ab51a10 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -210,9 +210,9 @@ namespace osu.Game.Screens.Play #region Screen handling - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); ApplyToBackground(b => { @@ -236,9 +236,9 @@ namespace osu.Game.Screens.Play showBatteryWarningIfNeeded(); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); Debug.Assert(CurrentPlayer != null); @@ -254,9 +254,9 @@ namespace osu.Game.Screens.Play contentIn(); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); BackgroundBrightnessReduction = false; @@ -268,7 +268,7 @@ namespace osu.Game.Screens.Play highPassFilter.CutoffTo(0); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { cancelLoad(); ContentOut(); @@ -284,7 +284,7 @@ namespace osu.Game.Screens.Play BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); - return base.OnExiting(next); + return base.OnExiting(e); } protected override void LogoArriving(OsuLogo logo, bool resuming) diff --git a/osu.Game/Screens/Play/ReplayPlayerLoader.cs b/osu.Game/Screens/Play/ReplayPlayerLoader.cs index 9eff4cb8fc..e78f700af2 100644 --- a/osu.Game/Screens/Play/ReplayPlayerLoader.cs +++ b/osu.Game/Screens/Play/ReplayPlayerLoader.cs @@ -20,13 +20,13 @@ namespace osu.Game.Screens.Play Score = score.ScoreInfo; } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { // these will be reverted thanks to PlayerLoader's lease. Mods.Value = Score.Mods; Ruleset.Value = Score.Ruleset; - base.OnEntering(last); + base.OnEntering(e); } } } diff --git a/osu.Game/Screens/Play/SoloSpectator.cs b/osu.Game/Screens/Play/SoloSpectator.cs index a0b07fcbd9..202527f308 100644 --- a/osu.Game/Screens/Play/SoloSpectator.cs +++ b/osu.Game/Screens/Play/SoloSpectator.cs @@ -249,10 +249,10 @@ namespace osu.Game.Screens.Play beatmapDownloader.Download(beatmapSet); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { previewTrackManager.StopAnyPlaying(this); - return base.OnExiting(next); + return base.OnExiting(e); } } } diff --git a/osu.Game/Screens/Play/SoloSpectatorPlayer.cs b/osu.Game/Screens/Play/SoloSpectatorPlayer.cs index 969a5bf2b4..5b601083c2 100644 --- a/osu.Game/Screens/Play/SoloSpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SoloSpectatorPlayer.cs @@ -24,11 +24,11 @@ namespace osu.Game.Screens.Play SpectatorClient.OnUserBeganPlaying += userBeganPlaying; } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { SpectatorClient.OnUserBeganPlaying -= userBeganPlaying; - return base.OnExiting(next); + return base.OnExiting(e); } private void userBeganPlaying(int userId, SpectatorState state) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index c0682952c3..09bec9b89f 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -91,11 +91,11 @@ namespace osu.Game.Screens.Play DrawableRuleset?.SetReplayScore(score); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { SpectatorClient.OnNewFrames -= userSentFrames; - return base.OnExiting(next); + return base.OnExiting(e); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/SpectatorPlayerLoader.cs b/osu.Game/Screens/Play/SpectatorPlayerLoader.cs index 10cc36c9a9..9ca5475ee4 100644 --- a/osu.Game/Screens/Play/SpectatorPlayerLoader.cs +++ b/osu.Game/Screens/Play/SpectatorPlayerLoader.cs @@ -20,13 +20,13 @@ namespace osu.Game.Screens.Play Score = score.ScoreInfo; } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { // these will be reverted thanks to PlayerLoader's lease. Mods.Value = Score.Mods; Ruleset.Value = Score.Ruleset; - base.OnEntering(last); + base.OnEntering(e); } } } diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index b1f2bccddf..b62dc1e5a6 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -115,9 +115,9 @@ namespace osu.Game.Screens.Play await submitScore(score).ConfigureAwait(false); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - bool exiting = base.OnExiting(next); + bool exiting = base.OnExiting(e); if (LoadedBeatmapSuccessfully) submitScore(Score.DeepClone()); diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index cb842ce4a0..98514cd846 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -231,9 +231,9 @@ namespace osu.Game.Screens.Ranking lastFetchCompleted = true; }); - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); ApplyToBackground(b => { @@ -244,9 +244,9 @@ namespace osu.Game.Screens.Ranking bottomPanel.FadeTo(1, 250); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - if (base.OnExiting(next)) + if (base.OnExiting(e)) return true; this.FadeOut(100); diff --git a/osu.Game/Screens/ScreenWhiteBox.cs b/osu.Game/Screens/ScreenWhiteBox.cs index 8b38b67f5c..3a9e7b8f18 100644 --- a/osu.Game/Screens/ScreenWhiteBox.cs +++ b/osu.Game/Screens/ScreenWhiteBox.cs @@ -28,25 +28,25 @@ namespace osu.Game.Screens protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg2"); - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { message.TextContainer.MoveTo(new Vector2(DrawSize.X / 16, 0), transition_time, Easing.OutExpo); this.FadeOut(transition_time, Easing.OutExpo); - return base.OnExiting(next); + return base.OnExiting(e); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { - base.OnSuspending(next); + base.OnSuspending(e); message.TextContainer.MoveTo(new Vector2(-(DrawSize.X / 16), 0), transition_time, Easing.OutExpo); this.FadeOut(transition_time, Easing.OutExpo); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); message.TextContainer.MoveTo(Vector2.Zero, transition_time, Easing.OutExpo); this.FadeIn(transition_time, Easing.OutExpo); diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 4a991ffc66..ec8b2e029a 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -109,9 +109,9 @@ namespace osu.Game.Screens.Select } } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); if (playerLoader != null) { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index dc505063d1..928978cd08 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -543,9 +543,9 @@ namespace osu.Game.Screens.Select } } - public override void OnEntering(IScreen last) + public override void OnEntering(ScreenTransitionEvent e) { - base.OnEntering(last); + base.OnEntering(e); this.FadeInFromZero(250); FilterControl.Activate(); @@ -591,9 +591,9 @@ namespace osu.Game.Screens.Select logo.FadeOut(logo_transition / 2, Easing.Out); } - public override void OnResuming(IScreen last) + public override void OnResuming(ScreenTransitionEvent e) { - base.OnResuming(last); + base.OnResuming(e); // required due to https://github.com/ppy/osu-framework/issues/3218 ModSelect.SelectedMods.Disabled = false; @@ -622,7 +622,7 @@ namespace osu.Game.Screens.Select FilterControl.Activate(); } - public override void OnSuspending(IScreen next) + public override void OnSuspending(ScreenTransitionEvent e) { // Handle the case where FinaliseSelection is never called (ie. when a screen is pushed externally). // Without this, it's possible for a transfer to happen while we are not the current screen. @@ -640,12 +640,12 @@ namespace osu.Game.Screens.Select this.FadeOut(250); FilterControl.Deactivate(); - base.OnSuspending(next); + base.OnSuspending(e); } - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { - if (base.OnExiting(next)) + if (base.OnExiting(e)) return true; beatmapInfoWedge.Hide(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index ca12bc73fe..46f31ae53b 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -118,7 +118,7 @@ namespace osu.Game.Tests.Visual public new bool HasUnsavedChanges => base.HasUnsavedChanges; - public override bool OnExiting(IScreen next) + public override bool OnExiting(ScreenExitEvent e) { // For testing purposes allow the screen to exit without saving on second attempt. if (!ExitConfirmed && dialogOverlay?.CurrentDialog is PromptForSaveDialog saveDialog) @@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual return true; } - return base.OnExiting(next); + return base.OnExiting(e); } public TestEditor(EditorLoader loader = null)