diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayContainer.cs index e544fb127dd7..388c72d6f2b3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayContainer.cs @@ -19,20 +19,30 @@ namespace osu.Game.Tests.Visual.UserInterface { public partial class TestSceneOverlayContainer : OsuManualInputManagerTestScene { - [SetUp] - public void SetUp() => Schedule(() => Child = new TestOverlay + private TestOverlay createOverlay() => new TestOverlay { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, Size = new Vector2(0.5f) - }); + }; + + private void loadOverlay() => Schedule(() => Child = createOverlay()); + + private void loadBlockingOverlay() => Schedule(() => Child = createOverlay().With(d => + { + d.Child = new BlockingContainer + { + RelativeSizeAxes = Axes.Both, + }; + })); [Test] public void TestScrollBlocked() { OsuScrollContainer scroll = null!; + AddStep("load overlay", loadOverlay); AddStep("add scroll container", () => { Add(scroll = new OsuScrollContainer @@ -60,9 +70,10 @@ public void TestScrollBlocked() [Test] public void TestAltScrollNotBlocked() { - TestGlobalScrollAdjustsVolume volumeAdjust = null!; + TestGlobalAltScrollAdjustsVolume volumeAdjust = null!; - AddStep("add volume control receptor", () => Add(volumeAdjust = new TestGlobalScrollAdjustsVolume + AddStep("load overlay", loadOverlay); + AddStep("add volume control receptor", () => Add(volumeAdjust = new TestGlobalAltScrollAdjustsVolume { RelativeSizeAxes = Axes.Both, Depth = float.MaxValue, @@ -79,7 +90,33 @@ public void TestAltScrollNotBlocked() AddStep("release alt", () => InputManager.ReleaseKey(Key.AltLeft)); } - public partial class TestGlobalScrollAdjustsVolume : GlobalScrollAdjustsVolume + [Test] + public void TestAltScrollBlockedByOptOut() + { + TestGlobalAltScrollAdjustsVolume volumeAdjust = null!; + + AddStep("add blocker and receptor", () => + { + Add(volumeAdjust = new TestGlobalAltScrollAdjustsVolume + { + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue, + }); + }); + AddStep("load blocking overlay", loadBlockingOverlay); + + AddStep("hold alt", () => InputManager.PressKey(Key.AltLeft)); + AddStep("perform scroll", () => + { + InputManager.MoveMouseTo(Content); + InputManager.ScrollVerticalBy(10); + }); + + AddAssert("receptor did not receive scroll input", () => !volumeAdjust.ScrollReceived); + AddStep("release alt", () => InputManager.ReleaseKey(Key.AltLeft)); + } + + public partial class TestGlobalAltScrollAdjustsVolume : GlobalAltScrollAdjustsVolume { public bool ScrollReceived { get; private set; } @@ -90,6 +127,8 @@ protected override bool OnScroll(ScrollEvent e) } } + private partial class BlockingContainer : Container, IBlockGlobalAltScrollVolume; + private partial class TestOverlay : OsuFocusedOverlayContainer { [BackgroundDependencyLoader] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b9a13ac36893..cc23e5537701 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -58,6 +58,7 @@ using osu.Game.Overlays.OSD; using osu.Game.Overlays.SkinEditor; using osu.Game.Overlays.Toolbar; +using osu.Game.Overlays.Volume; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Scoring.Legacy; @@ -1188,6 +1189,7 @@ protected override void LoadComplete() }, topMostOverlayContent.Add); loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add, true); + topMostOverlayContent.Add(new GlobalAltScrollAdjustsVolume()); onScreenDisplay = new OnScreenDisplay(); diff --git a/osu.Game/Overlays/Volume/GlobalAltScrollAdjustsVolume.cs b/osu.Game/Overlays/Volume/GlobalAltScrollAdjustsVolume.cs new file mode 100644 index 000000000000..2943ace10f91 --- /dev/null +++ b/osu.Game/Overlays/Volume/GlobalAltScrollAdjustsVolume.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Input.Events; + +namespace osu.Game.Overlays.Volume +{ + /// + /// Allows adjusting volume via mouse scroll while holding alt, regardless of the current game state. + /// + public partial class GlobalAltScrollAdjustsVolume : GlobalScrollAdjustsVolume + { + protected override bool OnScroll(ScrollEvent e) + { + if (!e.AltPressed || e.ControlPressed || e.ShiftPressed || e.SuperPressed) + return false; + + var hoveredDrawables = GetContainingInputManager()?.HoveredDrawables; + + if (hoveredDrawables?.Any(d => d is IBlockGlobalAltScrollVolume) == true) + return false; + + return base.OnScroll(e); + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Volume/IBlockGlobalAltScrollVolume.cs b/osu.Game/Overlays/Volume/IBlockGlobalAltScrollVolume.cs new file mode 100644 index 000000000000..abad6842c317 --- /dev/null +++ b/osu.Game/Overlays/Volume/IBlockGlobalAltScrollVolume.cs @@ -0,0 +1,12 @@ +// 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.Overlays.Volume +{ + /// + /// Marks hovered drawables that should suppress the global Alt-scroll volume fallback. + /// + public interface IBlockGlobalAltScrollVolume + { + } +} \ No newline at end of file diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs index b483f23d1d58..cfea754586d1 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs @@ -12,11 +12,12 @@ using osu.Framework.Timing; using osu.Framework.Utils; using osu.Game.Graphics.Containers; +using osu.Game.Overlays.Volume; using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public partial class ZoomableScrollContainer : OsuScrollContainer + public partial class ZoomableScrollContainer : OsuScrollContainer, IBlockGlobalAltScrollVolume { /// /// The time to zoom into/out of a point. diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 7b23cc753817..ea3a333bd5cf 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -25,7 +25,6 @@ using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; -using osu.Game.Overlays.Volume; using osu.Game.Rulesets; using osu.Game.Screens.Backgrounds; using osu.Game.Skinning; @@ -184,7 +183,6 @@ bool loadThemedIntro() return UsingThemedIntro = initialBeatmap != null; } - AddInternal(new GlobalScrollAdjustsVolume()); } public override void OnEntering(ScreenTransitionEvent e) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 32062409d0bf..5ff31cacc54f 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -33,7 +33,6 @@ using osu.Game.Overlays; using osu.Game.Overlays.Dialog; using osu.Game.Overlays.SkinEditor; -using osu.Game.Overlays.Volume; using osu.Game.Rulesets; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; @@ -141,7 +140,6 @@ private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings AddRangeInternal(new[] { SeasonalUIConfig.ENABLED ? new MainMenuSeasonalLighting() : Empty(), - new GlobalScrollAdjustsVolume(), buttonsContainer = new ParallaxContainer { ParallaxAmount = 0.01f, diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/ScreenQueue.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/ScreenQueue.cs index e86db69cec57..04a4649d8a82 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/ScreenQueue.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/ScreenQueue.cs @@ -37,7 +37,6 @@ using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay; using osu.Game.Overlays; -using osu.Game.Overlays.Volume; using osu.Game.Rulesets; using osu.Game.Screens.Footer; using osu.Game.Screens.OnlinePlay.Matchmaking.Match; @@ -124,7 +123,6 @@ private void load(AudioManager audio, IAPIProvider api) Children = new Drawable[] { waitingLoop = new DrawableSample(audio.Samples.Get(@"Multiplayer/Matchmaking/waiting-loop")), - new GlobalScrollAdjustsVolume(), mainGrid = new GridContainer { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/RankedPlayScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/RankedPlayScreen.cs index 7feab8e5b89f..13b18790948b 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/RankedPlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/RankedPlayScreen.cs @@ -25,7 +25,6 @@ using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.Dialog; -using osu.Game.Overlays.Volume; using osu.Game.Rulesets; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Matchmaking.Match.Gameplay; @@ -131,7 +130,6 @@ public RankedPlayScreen(MultiplayerRoom room) matchInfo = new RankedPlayMatchInfo(), backgroundMusic = new BackgroundMusicManager(), new RankedPlayBeatmapAvailabilityTracker(), - new GlobalScrollAdjustsVolume(), content = new InverseScalingDrawSizePreservingFillContainer { Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index c31f21f147af..28058c045636 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -30,7 +30,6 @@ using osu.Game.Online.Leaderboards; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; -using osu.Game.Overlays.Volume; using osu.Game.Performance; using osu.Game.Screens.Footer; using osu.Game.Screens.Menu; @@ -206,7 +205,6 @@ private void load(SessionStatics sessionStatics, OsuConfigManager config) InternalChildren = new Drawable[] { - new GlobalScrollAdjustsVolume(), (content = new LogoTrackingContainer { Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index dbf6802f90e4..8eb6024377e4 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -26,7 +26,6 @@ using osu.Game.Localisation; using osu.Game.Online.Placeholders; using osu.Game.Overlays; -using osu.Game.Overlays.Volume; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking.Expanded.Accuracy; @@ -122,7 +121,6 @@ private void load(AudioManager audio) RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new GlobalScrollAdjustsVolume(), StatisticsPanel = new StatisticsPanel { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index f03c9a533427..71dfbd603d11 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -42,7 +42,6 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Mods; -using osu.Game.Overlays.Volume; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -178,7 +177,6 @@ private void load(AudioManager audio, OsuConfigManager config) AddRangeInternal(new Drawable[] { - new GlobalScrollAdjustsVolume(), onlineLookupSource, mainContent = new Container {