diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index 0169627867..728aa27da2 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Skinning; using osu.Game.Tests.Visual; using osuTK; @@ -68,10 +69,8 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("create slider", () => { - var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo()); - tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1"; - - var provider = Ruleset.Value.CreateInstance().CreateSkinTransformer(tintingSkin, Beatmap.Value.Beatmap); + var skin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo()); + var provider = Ruleset.Value.CreateInstance().CreateSkinTransformer(skin, Beatmap.Value.Beatmap); Child = new SkinProvidingContainer(provider) { @@ -92,10 +91,10 @@ namespace osu.Game.Rulesets.Osu.Tests }); AddStep("set accent white", () => dho.AccentColour.Value = Color4.White); - AddAssert("ball is white", () => dho.ChildrenOfType().Single().AccentColour == Color4.White); + AddAssert("ball is white", () => dho.ChildrenOfType().Single().BallColour == Color4.White); AddStep("set accent red", () => dho.AccentColour.Value = Color4.Red); - AddAssert("ball is red", () => dho.ChildrenOfType().Single().AccentColour == Color4.Red); + AddAssert("ball is red", () => dho.ChildrenOfType().Single().BallColour == Color4.Red); } private Slider prepareObject(Slider slider) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index d58a435728..785d15c15b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -14,12 +14,10 @@ using osu.Game.Audio; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -106,7 +104,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { foreach (var drawableHitObject in NestedHitObjects) drawableHitObject.AccentColour.Value = colour.NewValue; - updateBallTint(); }, true); Tracking.BindValueChanged(updateSlidingSample); @@ -257,22 +254,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables SliderBody?.RecyclePath(); } - protected override void ApplySkin(ISkinSource skin, bool allowFallback) - { - base.ApplySkin(skin, allowFallback); - - updateBallTint(); - } - - private void updateBallTint() - { - if (CurrentSkin == null) - return; - - bool allowBallTint = CurrentSkin.GetConfig(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false; - Ball.AccentColour = allowBallTint ? AccentColour.Value : Color4.White; - } - protected override void CheckForResult(bool userTriggered, double timeOffset) { if (userTriggered || Time.Current < HitObject.EndTime) @@ -331,7 +312,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateHitStateTransforms(state); - const float fade_out_time = 450; + const float fade_out_time = 240; switch (state) { @@ -341,7 +322,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables break; } - this.FadeOut(fade_out_time, Easing.OutQuint).Expire(); + this.FadeOut(fade_out_time).Expire(); } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => SliderBody?.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index a2fe623897..de6ca7dd38 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -11,28 +11,20 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Events; -using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public class DrawableSliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition, IHasAccentColour + public class DrawableSliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition { public const float FOLLOW_AREA = 2.4f; public Func GetInitialHitAction; - public Color4 AccentColour - { - get => ball.Colour; - set => ball.Colour = value; - } - private Drawable followCircleReceptor; private DrawableSlider drawableSlider; private Drawable ball; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 53f4d21975..6ae9d5bc34 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// public readonly IBindable SpinsPerMinute = new BindableDouble(); - private const double fade_out_duration = 160; + private const double fade_out_duration = 240; public DrawableSpinner() : this(null) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs index ffdcba3cdb..36dc8c801d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs @@ -108,18 +108,23 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon { base.LoadComplete(); - accentColour.BindValueChanged(colour => - { - outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4); - outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f)); - innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f)); - flash.Colour = colour.NewValue; - }, true); - indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true); + accentColour.BindValueChanged(colour => + { + // A colour transform is applied. + // Without removing transforms first, when it is rewound it may apply an old colour. + outerGradient.ClearTransforms(targetMember: nameof(Colour)); + outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f)); + + outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4); + innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f)); + flash.Colour = colour.NewValue; + + updateStateTransforms(drawableObject, drawableObject.State.Value); + }, true); + drawableObject.ApplyCustomUpdateState += updateStateTransforms; - updateStateTransforms(drawableObject, drawableObject.State.Value); } private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) @@ -173,11 +178,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon .FadeOut(flash_in_duration); } - // The flash layer starts white to give the wanted brightness, but is almost immediately - // recoloured to the accent colour. This would more correctly be done with two layers (one for the initial flash) - // but works well enough with the colour fade. flash.FadeTo(1, flash_in_duration, Easing.OutQuint); - flash.FlashColour(accentColour.Value, fade_out_time, Easing.OutQuint); this.FadeOut(fade_out_time, Easing.OutQuad); break; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 211411591c..a6e62b83e4 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -134,10 +134,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy switch (state) { case ArmedState.Hit: - CircleSprite.FadeOut(legacy_fade_duration, Easing.Out); + CircleSprite.FadeOut(legacy_fade_duration); CircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); - OverlaySprite.FadeOut(legacy_fade_duration, Easing.Out); + OverlaySprite.FadeOut(legacy_fade_duration); OverlaySprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); if (hasNumber) @@ -146,11 +146,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (legacyVersion >= 2.0m) // legacy skins of version 2.0 and newer only apply very short fade out to the number piece. - hitCircleText.FadeOut(legacy_fade_duration / 4, Easing.Out); + hitCircleText.FadeOut(legacy_fade_duration / 4); else { // old skins scale and fade it normally along other pieces. - hitCircleText.FadeOut(legacy_fade_duration, Easing.Out); + hitCircleText.FadeOut(legacy_fade_duration); hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs index 22944becf3..71c3e4c9f0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs @@ -107,8 +107,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2)) - this.FadeInFromZero(spinner.TimeFadeIn / 2); + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn)) + this.FadeInFromZero(spinner.TimeFadeIn); using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt)) { diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs index 414879f42d..60d71ae843 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -21,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [Resolved(canBeNull: true)] private DrawableHitObject? parentObject { get; set; } + public Color4 BallColour => animationContent.Colour; + private Sprite layerNd = null!; private Sprite layerSpec = null!; @@ -61,6 +64,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy }; } + private readonly IBindable accentColour = new Bindable(); + protected override void LoadComplete() { base.LoadComplete(); @@ -69,6 +74,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { parentObject.ApplyCustomUpdateState += updateStateTransforms; updateStateTransforms(parentObject, parentObject.State.Value); + + if (skin.GetConfig(SkinConfiguration.LegacySetting.AllowSliderBallTint)?.Value == true) + { + accentColour.BindTo(parentObject.AccentColour); + accentColour.BindValueChanged(a => animationContent.Colour = a.NewValue, true); + } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs index 004222ad7a..a817e5f2b7 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs @@ -65,6 +65,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { spin = new Sprite { + Alpha = 0, Anchor = Anchor.TopCentre, Origin = Anchor.Centre, Texture = source.GetTexture("spinner-spin"), @@ -82,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy }, bonusCounter = new LegacySpriteText(LegacyFont.Score) { - Alpha = 0f, + Alpha = 0, Anchor = Anchor.TopCentre, Origin = Anchor.Centre, Scale = new Vector2(SPRITE_SCALE), @@ -179,6 +180,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy spmCounter.MoveToOffset(new Vector2(0, -spm_hide_offset), d.HitObject.TimeFadeIn, Easing.Out); } + using (BeginAbsoluteSequence(d.HitObject.StartTime - d.HitObject.TimeFadeIn / 2)) + spin.FadeInFromZero(d.HitObject.TimeFadeIn / 2); + using (BeginAbsoluteSequence(d.HitObject.StartTime)) ApproachCircle?.ScaleTo(SPRITE_SCALE * 0.1f, d.HitObject.Duration); diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs index 306a1e38b9..1c0a62454b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -9,7 +9,6 @@ namespace osu.Game.Rulesets.Osu.Skinning { SliderBorderSize, SliderPathRadius, - AllowSliderBallTint, CursorCentre, CursorExpand, CursorRotate, diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 5aadd6f56a..917434ae22 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -244,7 +244,10 @@ namespace osu.Game.Tests.Visual.Background public void TestResumeFromPlayer() { performFullSetup(); - AddStep("Move mouse to Visual Settings", () => InputManager.MoveMouseTo(playerLoader.VisualSettingsPos)); + AddStep("Move mouse to Visual Settings location", () => InputManager.MoveMouseTo(playerLoader.ScreenSpaceDrawQuad.TopRight + + new Vector2(-playerLoader.VisualSettingsPos.ScreenSpaceDrawQuad.Width, + playerLoader.VisualSettingsPos.ScreenSpaceDrawQuad.Height / 2 + ))); AddStep("Resume PlayerLoader", () => player.Restart()); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index f31261dc1f..63677ce378 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -97,14 +97,23 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestCurrentItemDoesNotHaveDeleteButton() + public void TestSingleItemDoesNotHaveDeleteButton() + { + AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely()); + AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers); + + assertDeleteButtonVisibility(0, false); + } + + [Test] + public void TestCurrentItemHasDeleteButtonIfNotSingle() { AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely()); AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers); addPlaylistItem(() => API.LocalUser.Value.OnlineID); - assertDeleteButtonVisibility(0, false); + assertDeleteButtonVisibility(0, true); assertDeleteButtonVisibility(1, true); AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely()); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index 6c0191ae27..75c47f0b1b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -178,6 +178,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestKeyboardLocalCursor([Values] bool clickToShow) { + AddStep("Enable cursor hiding", () => globalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = true); AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3].ScreenSpaceDrawQuad.Centre + new Vector2(10, 0))); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check global cursor alpha is 1", () => globalCursorDisplay.MenuCursor.Alpha == 1); @@ -201,6 +202,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestKeyboardUserCursor([Values] bool clickToShow) { + AddStep("Enable cursor hiding", () => globalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = true); AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0])); AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check global cursor alpha is 0", () => !checkVisible(globalCursorDisplay.MenuCursor) && globalCursorDisplay.MenuCursor.ActiveCursor.Alpha == 0); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 75500fbc4e..5f5749dc73 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -355,6 +355,14 @@ namespace osu.Game.Beatmaps.Formats switch (type) { + case LegacyEventType.Sprite: + // Generally, the background is the first thing defined in a beatmap file. + // In some older beatmaps, it is not present and replaced by a storyboard-level background instead. + // Allow the first sprite (by file order) to act as the background in such cases. + if (string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile)) + beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[3]); + break; + case LegacyEventType.Background: beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]); break; diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index 33b0c308cc..af542989ff 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -23,6 +23,21 @@ namespace osu.Game.Graphics.Cursor private readonly IBindable screenshotCursorVisibility = new Bindable(true); public override bool IsPresent => screenshotCursorVisibility.Value && base.IsPresent; + private bool hideCursorOnNonMouseInput; + + public bool HideCursorOnNonMouseInput + { + get => hideCursorOnNonMouseInput; + set + { + if (hideCursorOnNonMouseInput == value) + return; + + hideCursorOnNonMouseInput = value; + updateState(); + } + } + protected override Drawable CreateCursor() => activeCursor = new Cursor(); private Cursor activeCursor = null!; @@ -75,7 +90,7 @@ namespace osu.Game.Graphics.Cursor private void updateState() { - bool combinedVisibility = State.Value == Visibility.Visible && lastInputWasMouse.Value && !isIdle.Value; + bool combinedVisibility = State.Value == Visibility.Visible && (lastInputWasMouse.Value || !hideCursorOnNonMouseInput) && !isIdle.Value; if (visible == combinedVisibility) return; @@ -262,14 +277,19 @@ namespace osu.Game.Graphics.Cursor { switch (e) { - case MouseEvent: + case MouseDownEvent: + case MouseMoveEvent: lastInputWasMouseSource.Value = true; return false; - default: + case KeyDownEvent keyDown when !keyDown.Repeat: + case JoystickPressEvent: + case MidiDownEvent: lastInputWasMouseSource.Value = false; return false; } + + return false; } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b3eaf5cd01..2bdcb57f2a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1333,6 +1333,8 @@ namespace osu.Game OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); API.Activity.BindTo(newOsuScreen.Activity); + GlobalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = newOsuScreen.HideMenuCursorOnNonMouseInput; + if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); else diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 04c9a626b5..6dd9e2a56d 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -40,8 +40,6 @@ namespace osu.Game.Overlays Anchor = Anchor.TopCentre, Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, - AutoSizeDuration = transition_duration, - AutoSizeEasing = Easing.OutQuint, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = 10, Top = 5, Bottom = 10 }, Spacing = new Vector2(0, 15), diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 7d8657a3df..a5739a41b1 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -41,6 +41,11 @@ namespace osu.Game.Screens /// bool HideOverlaysOnEnter { get; } + /// + /// Whether the menu cursor should be hidden when non-mouse input is received. + /// + bool HideMenuCursorOnNonMouseInput { get; } + /// /// Whether overlays should be able to be opened when this screen is current. /// diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs index 39740e650f..ba6b482729 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs @@ -78,9 +78,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist return; bool isItemOwner = Item.OwnerID == api.LocalUser.Value.OnlineID || multiplayerClient.IsHost; + bool isValidItem = isItemOwner && !Item.Expired; - AllowDeletion = isItemOwner && !Item.Expired && Item.ID != multiplayerClient.Room.Settings.PlaylistItemId; - AllowEditing = isItemOwner && !Item.Expired; + AllowDeletion = isValidItem + && (Item.ID != multiplayerClient.Room.Settings.PlaylistItemId // This is an optimisation for the following check. + || multiplayerClient.Room.Playlist.Count(i => !i.Expired) > 1); + + AllowEditing = isValidItem; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 0678a90f71..6be13bbda3 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -40,11 +40,10 @@ namespace osu.Game.Screens public virtual bool AllowExternalScreenChange => false; - /// - /// Whether all overlays should be hidden when this screen is entered or resumed. - /// public virtual bool HideOverlaysOnEnter => false; + public virtual bool HideMenuCursorOnNonMouseInput => false; + /// /// The initial overlay activation mode to use when this screen is entered for the first time. /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 68b623b781..7048f83c09 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -66,6 +66,8 @@ namespace osu.Game.Screens.Play public override bool HideOverlaysOnEnter => true; + public override bool HideMenuCursorOnNonMouseInput => true; + protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; // We are managing our own adjustments (see OnEntering/OnExiting). diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 2d096f1c38..4ff5083107 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -64,6 +64,8 @@ namespace osu.Game.Screens.Play protected Task? DisposalTask { get; private set; } + private OsuScrollContainer settingsScroll = null!; + private bool backgroundBrightnessReduction; private readonly BindableDouble volumeAdjustment = new BindableDouble(1); @@ -168,30 +170,30 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - new OsuScrollContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = SettingsToolboxGroup.CONTAINER_WIDTH + padding * 2, - Padding = new MarginPadding { Vertical = padding }, - Masking = false, - Child = PlayerSettings = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Padding = new MarginPadding { Horizontal = padding }, - Children = new PlayerSettingsGroup[] - { - VisualSettings = new VisualSettings(), - AudioSettings = new AudioSettings(), - new InputSettings() - } - }, - }, - idleTracker = new IdleTracker(750), }), + settingsScroll = new OsuScrollContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = SettingsToolboxGroup.CONTAINER_WIDTH + padding * 2, + Padding = new MarginPadding { Vertical = padding }, + Masking = false, + Child = PlayerSettings = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Padding = new MarginPadding { Horizontal = padding }, + Children = new PlayerSettingsGroup[] + { + VisualSettings = new VisualSettings(), + AudioSettings = new AudioSettings(), + new InputSettings() + } + }, + }, + idleTracker = new IdleTracker(750), lowPassFilter = new AudioFilter(audio.TrackMixer), highPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass) }; @@ -227,6 +229,9 @@ namespace osu.Game.Screens.Play Beatmap.Value.Track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment); + // Start off-screen. + settingsScroll.MoveToX(settingsScroll.DrawWidth); + content.ScaleTo(0.7f); contentIn(); @@ -316,6 +321,16 @@ namespace osu.Game.Screens.Play content.StopTracking(); } + protected override void LogoSuspending(OsuLogo logo) + { + base.LogoSuspending(logo); + content.StopTracking(); + + logo + .FadeOut(CONTENT_OUT_DURATION / 2, Easing.OutQuint) + .ScaleTo(logo.Scale * 0.8f, CONTENT_OUT_DURATION * 2, Easing.OutQuint); + } + #endregion protected override void Update() @@ -394,6 +409,10 @@ namespace osu.Game.Screens.Play content.FadeInFromZero(400); content.ScaleTo(1, 650, Easing.OutQuint).Then().Schedule(prepareNewPlayer); + + settingsScroll.FadeInFromZero(500, Easing.Out) + .MoveToX(0, 500, Easing.OutQuint); + lowPassFilter.CutoffTo(1000, 650, Easing.OutQuint); highPassFilter.CutoffTo(300).Then().CutoffTo(0, 1250); // 1250 is to line up with the appearance of MetadataInfo (750 delay + 500 fade-in) @@ -407,6 +426,10 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, CONTENT_OUT_DURATION * 2, Easing.OutQuint); content.FadeOut(CONTENT_OUT_DURATION, Easing.OutQuint); + + settingsScroll.FadeOut(CONTENT_OUT_DURATION, Easing.OutQuint) + .MoveToX(settingsScroll.DrawWidth, CONTENT_OUT_DURATION * 2, Easing.OutQuint); + lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, CONTENT_OUT_DURATION); highPassFilter.CutoffTo(0, CONTENT_OUT_DURATION); } @@ -435,7 +458,7 @@ namespace osu.Game.Screens.Play ContentOut(); - TransformSequence pushSequence = this.Delay(CONTENT_OUT_DURATION); + TransformSequence pushSequence = this.Delay(0); // only show if the warning was created (i.e. the beatmap needs it) // and this is not a restart of the map (the warning expires after first load). @@ -444,6 +467,7 @@ namespace osu.Game.Screens.Play const double epilepsy_display_length = 3000; pushSequence + .Delay(CONTENT_OUT_DURATION) .Schedule(() => epilepsyWarning.State.Value = Visibility.Visible) .TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint) .Delay(epilepsy_display_length) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index d56b9c23c8..345bd5a134 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -86,16 +86,13 @@ namespace osu.Game.Screens.Play // Generally a timeout would not happen here as APIAccess will timeout first. if (!tcs.Task.Wait(60000)) - handleTokenFailure(new InvalidOperationException("Token retrieval timed out (request never run)")); + req.TriggerFailure(new InvalidOperationException("Token retrieval timed out (request never run)")); return true; void handleTokenFailure(Exception exception) { - // This method may be invoked multiple times due to the Task.Wait call above. - // We only really care about the first error. - if (!tcs.TrySetResult(false)) - return; + tcs.SetResult(false); if (HandleTokenRetrievalFailure(exception)) { diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 04f1286dc7..b80275a1e8 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -46,6 +46,8 @@ namespace osu.Game.Skinning new Color4(242, 24, 57, 255) }; + Configuration.ConfigDictionary[nameof(SkinConfiguration.LegacySetting.AllowSliderBallTint)] = @"true"; + Configuration.LegacyVersion = 2.7m; } } diff --git a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs index 980dee8601..469657c03c 100644 --- a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs +++ b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs @@ -85,10 +85,6 @@ namespace osu.Game.Skinning.Editor { public Action? RequestPlacement; - protected override bool ShouldBeConsideredForInput(Drawable child) => false; - - public override bool PropagateNonPositionalInputSubTree => false; - private readonly Drawable component; private readonly CompositeDrawable? dependencySource; @@ -177,6 +173,10 @@ namespace osu.Game.Skinning.Editor public class DependencyBorrowingContainer : Container { + protected override bool ShouldBeConsideredForInput(Drawable child) => false; + + public override bool PropagateNonPositionalInputSubTree => false; + private readonly CompositeDrawable? donor; public DependencyBorrowingContainer(CompositeDrawable? donor) diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index 0b1159f8fd..4e5d96ccb8 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -38,7 +38,8 @@ namespace osu.Game.Skinning HitCirclePrefix, HitCircleOverlap, AnimationFramerate, - LayeredHitSounds + LayeredHitSounds, + AllowSliderBallTint, } public static List DefaultComboColours { get; } = new List