diff --git a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs index b7d265c448..78de76b981 100644 --- a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs +++ b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs @@ -30,20 +30,14 @@ namespace osu.Game.Overlays.Music [Resolved] private OnScreenDisplay? onScreenDisplay { get; set; } - [Resolved] - private OsuGame game { get; set; } = null!; - public bool OnPressed(KeyBindingPressEvent e) { - if (e.Repeat) + if (e.Repeat || !musicController.AllowTrackControl.Value) return false; switch (e.Action) { case GlobalAction.MusicPlay: - if (game.LocalUserPlaying.Value) - return false; - // use previous state as TogglePause may not update the track's state immediately (state update is run on the audio thread see https://github.com/ppy/osu/issues/9880#issuecomment-674668842) bool wasPlaying = musicController.IsPlaying; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 562140f5cb..665c61edf0 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -40,6 +40,11 @@ namespace osu.Game.Overlays /// public bool UserPauseRequested { get; private set; } + /// + /// Whether control of the global track should be allowed. + /// + public readonly BindableBool AllowTrackControl = new BindableBool(true); + /// /// Fired when the global has changed. /// Includes direction information for display purposes. @@ -92,8 +97,10 @@ namespace osu.Game.Overlays seekDelegate?.Cancel(); seekDelegate = Schedule(() => { - if (!beatmap.Disabled) - CurrentTrack.Seek(position); + if (beatmap.Disabled || !AllowTrackControl.Value) + return; + + CurrentTrack.Seek(position); }); } @@ -107,7 +114,7 @@ namespace osu.Game.Overlays if (CurrentTrack.IsDummyDevice || beatmap.Value.BeatmapSetInfo.DeletePending) { - if (beatmap.Disabled) + if (beatmap.Disabled || !AllowTrackControl.Value) return; Logger.Log($"{nameof(MusicController)} skipping next track to {nameof(EnsurePlayingSomething)}"); @@ -132,6 +139,9 @@ namespace osu.Game.Overlays /// Whether the operation was successful. public bool Play(bool restart = false, bool requestedByUser = false) { + if (requestedByUser && !AllowTrackControl.Value) + return false; + if (requestedByUser) UserPauseRequested = false; @@ -153,6 +163,9 @@ namespace osu.Game.Overlays /// public void Stop(bool requestedByUser = false) { + if (requestedByUser && !AllowTrackControl.Value) + return; + UserPauseRequested |= requestedByUser; if (CurrentTrack.IsRunning) CurrentTrack.StopAsync(); @@ -164,6 +177,9 @@ namespace osu.Game.Overlays /// Whether the operation was successful. public bool TogglePause() { + if (!AllowTrackControl.Value) + return false; + if (CurrentTrack.IsRunning) Stop(true); else @@ -189,7 +205,7 @@ namespace osu.Game.Overlays /// The that indicate the decided action. private PreviousTrackResult prev() { - if (beatmap.Disabled) + if (beatmap.Disabled || !AllowTrackControl.Value) return PreviousTrackResult.None; double currentTrackPosition = CurrentTrack.CurrentTime; @@ -229,7 +245,7 @@ namespace osu.Game.Overlays private bool next() { - if (beatmap.Disabled) + if (beatmap.Disabled || !AllowTrackControl.Value) return false; queuedDirection = TrackChangeDirection.Next; @@ -352,7 +368,7 @@ namespace osu.Game.Overlays private void onTrackCompleted() { - if (!CurrentTrack.Looping && !beatmap.Disabled) + if (!CurrentTrack.Looping && !beatmap.Disabled && AllowTrackControl.Value) NextTrack(); } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 15eefb2d9f..78539fde7a 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -68,6 +68,8 @@ namespace osu.Game.Overlays [Resolved] private OsuColour colours { get; set; } + private Bindable allowTrackControl; + public NowPlayingOverlay() { Width = 400; @@ -220,8 +222,10 @@ namespace osu.Game.Overlays { base.LoadComplete(); - beatmap.BindDisabledChanged(_ => Scheduler.AddOnce(beatmapDisabledChanged)); - beatmapDisabledChanged(); + beatmap.BindDisabledChanged(_ => Scheduler.AddOnce(updateEnabledStates)); + + allowTrackControl = musicController.AllowTrackControl.GetBoundCopy(); + allowTrackControl.BindValueChanged(_ => Scheduler.AddOnce(updateEnabledStates), true); musicController.TrackChanged += trackChanged; trackChanged(beatmap.Value); @@ -334,16 +338,18 @@ namespace osu.Game.Overlays }; } - private void beatmapDisabledChanged() + private void updateEnabledStates() { - bool disabled = beatmap.Disabled; + bool beatmapDisabled = beatmap.Disabled; + bool trackControlDisabled = !musicController.AllowTrackControl.Value; - if (disabled) + if (beatmapDisabled || trackControlDisabled) playlist?.Hide(); - prevButton.Enabled.Value = !disabled; - nextButton.Enabled.Value = !disabled; - playlistButton.Enabled.Value = !disabled; + prevButton.Enabled.Value = !beatmapDisabled && !trackControlDisabled; + nextButton.Enabled.Value = !beatmapDisabled && !trackControlDisabled; + playlistButton.Enabled.Value = !beatmapDisabled && !trackControlDisabled; + playButton.Enabled.Value = !trackControlDisabled; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 756fbb80a7..5b4e2d75f4 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -69,6 +69,12 @@ namespace osu.Game.Screens /// bool? ApplyModTrackAdjustments { get; } + /// + /// Whether control of the global track should be allowed via the music controller / now playing overlay. + /// A value means that the parent screen's value of this setting will be used. + /// + bool? AllowGlobalTrackControl { get; } + /// /// Invoked when the back button has been pressed to close any overlays before exiting this . /// diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 869d14c030..2dc9d5d49d 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -87,6 +87,8 @@ namespace osu.Game.Screens public virtual bool? ApplyModTrackAdjustments => null; + public virtual bool? AllowGlobalTrackControl => null; + public Bindable Beatmap { get; private set; } public Bindable Ruleset { get; private set; } @@ -95,6 +97,8 @@ namespace osu.Game.Screens private OsuScreenDependencies screenDependencies; + private bool? globalMusicControlStateAtSuspend; + private bool? modTrackAdjustmentStateAtSuspend; internal void CreateLeasedDependencies(IReadOnlyDependencyContainer dependencies) => createDependencies(dependencies); @@ -180,6 +184,8 @@ namespace osu.Game.Screens // in such a case there's no need to restore this value. if (modTrackAdjustmentStateAtSuspend != null) musicController.ApplyModTrackAdjustments = modTrackAdjustmentStateAtSuspend.Value; + if (globalMusicControlStateAtSuspend != null) + musicController.AllowTrackControl.Value = globalMusicControlStateAtSuspend.Value; base.OnResuming(e); } @@ -189,6 +195,7 @@ namespace osu.Game.Screens base.OnSuspending(e); modTrackAdjustmentStateAtSuspend = musicController.ApplyModTrackAdjustments; + globalMusicControlStateAtSuspend = musicController.AllowTrackControl.Value; onSuspendingLogo(); } @@ -200,6 +207,9 @@ namespace osu.Game.Screens if (ApplyModTrackAdjustments != null) musicController.ApplyModTrackAdjustments = ApplyModTrackAdjustments.Value; + if (AllowGlobalTrackControl != null) + musicController.AllowTrackControl.Value = AllowGlobalTrackControl.Value; + if (backgroundStack?.Push(ownedBackground = CreateBackground()) != true) { // If the constructed instance was not actually pushed to the background stack, we don't want to track it unnecessarily.