From 2769d8e8cfdbded19d1ba4407053efb8c162ae43 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 16:24:21 +0900 Subject: [PATCH 01/47] Add test coverage of `ShearedOverlayContainer` --- .../TestSceneShearedOverlayContainer.cs | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs new file mode 100644 index 0000000000..5a9cafde27 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedOverlayContainer.cs @@ -0,0 +1,102 @@ +// 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.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osu.Game.Overlays.Mods; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.UserInterface +{ + [TestFixture] + public class TestSceneShearedOverlayContainer : OsuManualInputManagerTestScene + { + private TestShearedOverlayContainer overlay; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create overlay", () => + { + Child = overlay = new TestShearedOverlayContainer + { + State = { Value = Visibility.Visible } + }; + }); + } + + [Test] + public void TestClickAwayToExit() + { + AddStep("click inside header", () => + { + InputManager.MoveMouseTo(overlay.ChildrenOfType().First().ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("overlay not dismissed", () => overlay.State.Value == Visibility.Visible); + + AddStep("click inside content", () => + { + InputManager.MoveMouseTo(overlay.ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("overlay not dismissed", () => overlay.State.Value == Visibility.Visible); + + AddStep("click outside header", () => + { + InputManager.MoveMouseTo(new Vector2(overlay.ScreenSpaceDrawQuad.TopLeft.X, overlay.ScreenSpaceDrawQuad.Centre.Y)); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("overlay dismissed", () => overlay.State.Value == Visibility.Hidden); + } + + public class TestShearedOverlayContainer : ShearedOverlayContainer + { + protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green; + + [BackgroundDependencyLoader] + private void load() + { + Header.Title = "Sheared overlay header"; + Header.Description = string.Join(" ", Enumerable.Repeat("This is a description.", 20)); + + MainAreaContent.Child = new InputBlockingContainer + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(0.9f), + Children = new Drawable[] + { + new Box + { + Colour = Color4.Blue, + RelativeSizeAxes = Axes.Both, + }, + new OsuSpriteText + { + Font = OsuFont.Default.With(size: 24), + Text = "Content", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }; + } + } + } +} From 0f4b40ab15b2727d5cf66c2f0a3bf606403e9c51 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 16:15:10 +0900 Subject: [PATCH 02/47] Add better click-to-dismiss logic for sheared overlays --- osu.Game/Graphics/InputBlockingContainer.cs | 21 +++++++++++++++++++ .../UserInterface/ShearedOverlayHeader.cs | 2 +- .../Overlays/Mods/ShearedOverlayContainer.cs | 15 ++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Graphics/InputBlockingContainer.cs diff --git a/osu.Game/Graphics/InputBlockingContainer.cs b/osu.Game/Graphics/InputBlockingContainer.cs new file mode 100644 index 0000000000..d8387b1401 --- /dev/null +++ b/osu.Game/Graphics/InputBlockingContainer.cs @@ -0,0 +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 osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; + +namespace osu.Game.Graphics +{ + /// + /// A simple container which blocks input events from travelling through it. + /// + public class InputBlockingContainer : Container + { + protected override bool OnHover(HoverEvent e) => true; + + protected override bool OnMouseDown(MouseDownEvent e) => true; + + protected override bool OnClick(ClickEvent e) => true; + } +} diff --git a/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs b/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs index 9ed7bb35de..452a1dd394 100644 --- a/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs +++ b/osu.Game/Graphics/UserInterface/ShearedOverlayHeader.cs @@ -66,7 +66,7 @@ namespace osu.Game.Graphics.UserInterface }, Children = new Drawable[] { - underlayContainer = new Container + underlayContainer = new InputBlockingContainer { RelativeSizeAxes = Axes.X, Height = HEIGHT, diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 62ed736dc2..eca192c8e5 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -5,6 +5,8 @@ using osu.Framework.Allocation; 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.UserInterface; @@ -88,7 +90,7 @@ namespace osu.Game.Overlays.Mods Bottom = footer_height + PADDING, } }, - Footer = new Container + Footer = new InputBlockingContainer { RelativeSizeAxes = Axes.X, Depth = float.MinValue, @@ -113,6 +115,17 @@ namespace osu.Game.Overlays.Mods }; } + protected override bool OnClick(ClickEvent e) + { + if (State.Value == Visibility.Visible) + { + Hide(); + return true; + } + + return base.OnClick(e); + } + protected override void PopIn() { const double fade_in_duration = 400; From 0b81ae9de2af8883ceb5931ed964e9949672b85c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Apr 2022 16:51:26 +0900 Subject: [PATCH 03/47] Convert to using sheared overlay container Add better click-to-dismiss logic for sheader overlays --- .../TestSceneFirstRunSetupOverlay.cs | 2 +- osu.Game/Overlays/FirstRunSetupOverlay.cs | 221 ++++++------------ 2 files changed, 75 insertions(+), 148 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index efce4f350b..e925859d71 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -165,7 +165,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("click outside content", () => { - InputManager.MoveMouseTo(overlay.ScreenSpaceDrawQuad.TopLeft - new Vector2(1)); + InputManager.MoveMouseTo(new Vector2(overlay.ScreenSpaceDrawQuad.TopLeft.X, overlay.ScreenSpaceDrawQuad.Centre.Y)); InputManager.Click(MouseButton.Left); }); diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index f73d82b793..d0e721cf46 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -7,10 +7,8 @@ using System; using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; @@ -18,25 +16,22 @@ using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Configuration; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Overlays.FirstRunSetup; +using osu.Game.Overlays.Mods; using osu.Game.Overlays.Notifications; using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Match.Components; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays { [Cached] - public class FirstRunSetupOverlay : OsuFocusedOverlayContainer + public class FirstRunSetupOverlay : ShearedOverlayContainer { - protected override bool StartHidden => true; + protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Purple; [Resolved] private IPerformFromScreenRunner performer { get; set; } = null!; @@ -52,15 +47,10 @@ namespace osu.Game.Overlays public PurpleTriangleButton NextButton = null!; public DangerousTriangleButton BackButton = null!; - [Cached] - private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - private readonly Bindable showFirstRunSetup = new Bindable(); private int? currentStepIndex; - private const float scale_when_hidden = 0.9f; - /// /// The currently displayed screen, if any. /// @@ -76,149 +66,89 @@ namespace osu.Game.Overlays private Bindable? overlayActivationMode; - public FirstRunSetupOverlay() - { - RelativeSizeAxes = Axes.Both; - } + private Container content = null!; [BackgroundDependencyLoader] private void load() { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; + Header.Title = FirstRunSetupOverlayStrings.FirstRunSetup; + Header.Description = FirstRunSetupOverlayStrings.SetupOsuToSuitYou; - RelativeSizeAxes = Axes.Both; - Size = new Vector2(0.95f); - - EdgeEffect = new EdgeEffectParameters + MainAreaContent.AddRange(new Drawable[] { - Type = EdgeEffectType.Shadow, - Radius = 5, - Colour = Color4.Black.Opacity(0.2f), - }; - - Masking = true; - CornerRadius = 10; - - Children = new Drawable[] - { - new Box + content = new Container { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background6, - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + Padding = new MarginPadding { Horizontal = 50 }, + Child = new InputBlockingContainer { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] + Masking = true, + CornerRadius = 14, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Box - { - Colour = colourProvider.Background5, - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding(10), - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = FirstRunSetupOverlayStrings.FirstRunSetup, - Font = OsuFont.Default.With(size: 32), - Colour = colourProvider.Content1, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - new OsuTextFlowContainer - { - Text = FirstRunSetupOverlayStrings.SetupOsuToSuitYou, - Colour = colourProvider.Content2, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - }, - } - }, - } - }, - }, - new Drawable[] - { - stackContainer = new Container + new Box { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(20), + Colour = ColourProvider.Background6, }, - }, - new Drawable[] - { - new Container + stackContainer = new Container { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(20) + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { - Top = 0 // provided by the stack container above. - }, - Child = new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, 10), - new Dimension(), - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new[] - { - BackButton = new DangerousTriangleButton - { - Width = 200, - Text = CommonStrings.Back, - Action = showPreviousStep, - Enabled = { Value = false }, - }, - Empty(), - NextButton = new PurpleTriangleButton - { - RelativeSizeAxes = Axes.X, - Width = 1, - Text = FirstRunSetupOverlayStrings.GetStarted, - Action = showNextStep - } - }, - } + Vertical = 20, + Horizontal = 20, }, } - } - } + }, + }, }, - }; + }); + + FooterContent.Add(new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.98f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 10), + new Dimension(), + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new[] + { + BackButton = new DangerousTriangleButton + { + Width = 200, + Text = CommonStrings.Back, + Action = showPreviousStep, + Enabled = { Value = false }, + }, + Empty(), + NextButton = new PurpleTriangleButton + { + RelativeSizeAxes = Axes.X, + Width = 1, + Text = FirstRunSetupOverlayStrings.GetStarted, + Action = showNextStep + } + }, + } + }); } protected override void LoadComplete() @@ -280,10 +210,8 @@ namespace osu.Game.Overlays { base.PopIn(); - this.ScaleTo(scale_when_hidden) - .ScaleTo(1, 400, Easing.OutElasticHalf); - - this.FadeIn(400, Easing.OutQuint); + content.ScaleTo(0.99f) + .ScaleTo(1, 400, Easing.OutQuint); if (currentStepIndex == null) showFirstStep(); @@ -291,6 +219,10 @@ namespace osu.Game.Overlays protected override void PopOut() { + base.PopOut(); + + content.ScaleTo(0.99f, 400, Easing.OutQuint); + if (overlayActivationMode != null) { // If this is non-null we are guaranteed to have come from the main menu. @@ -316,11 +248,6 @@ namespace osu.Game.Overlays stack?.FadeOut(100) .Expire(); } - - base.PopOut(); - - this.ScaleTo(0.96f, 400, Easing.OutQuint); - this.FadeOut(200, Easing.OutQuint); } private void showFirstStep() From 32722adba90c3b0f3da0a80438693cff8d39c1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Apr 2022 22:59:37 +0200 Subject: [PATCH 04/47] Allow mod panels to be clicked in incompatible state --- .../Mods/IncompatibilityDisplayingModPanel.cs | 39 ++----------------- osu.Game/Overlays/Mods/ModPanel.cs | 20 ++++++---- 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs index 38781455fa..aeb983d352 100644 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs @@ -1,15 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Cursor; -using osu.Framework.Input.Events; using osu.Game.Rulesets.Mods; using osu.Game.Utils; @@ -42,41 +39,13 @@ namespace osu.Game.Overlays.Mods && !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod)); } + protected override Colour4 BackgroundColour => incompatible.Value ? (Colour4)ColourProvider.Background6 : base.BackgroundColour; + protected override Colour4 ForegroundColour => incompatible.Value ? (Colour4)ColourProvider.Background5 : base.ForegroundColour; + protected override void UpdateState() { - Action = incompatible.Value ? () => { } : (Action)Active.Toggle; - - if (incompatible.Value) - { - 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); - - SwitchContainer.ResizeWidthTo(IDLE_SWITCH_WIDTH, TRANSITION_DURATION, Easing.OutQuint); - SwitchContainer.FadeColour(Colour4.Gray, TRANSITION_DURATION, Easing.OutQuint); - MainContentContainer.TransformTo(nameof(Padding), new MarginPadding - { - Left = IDLE_SWITCH_WIDTH, - Right = CORNER_RADIUS - }, TRANSITION_DURATION, Easing.OutQuint); - - TextBackground.FadeColour(textBackgroundColour, TRANSITION_DURATION, Easing.OutQuint); - TextFlow.FadeColour(Colour4.White.Opacity(0.5f), TRANSITION_DURATION, Easing.OutQuint); - return; - } - - SwitchContainer.FadeColour(Colour4.White, TRANSITION_DURATION, Easing.OutQuint); base.UpdateState(); - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - if (incompatible.Value) - return true; // bypasses base call purposely in order to not play out the intermediate state animation. - - return base.OnMouseDown(e); + SwitchContainer.FadeColour(incompatible.Value ? Colour4.Gray : Colour4.White, TRANSITION_DURATION, Easing.OutQuint); } #region IHasCustomTooltip diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index 7ae325bde7..f2a97da3b2 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -203,20 +203,24 @@ namespace osu.Game.Overlays.Mods base.OnMouseUp(e); } + protected virtual Colour4 BackgroundColour => Active.Value ? activeColour.Darken(0.3f) : (Colour4)ColourProvider.Background3; + protected virtual Colour4 ForegroundColour => Active.Value ? activeColour : (Colour4)ColourProvider.Background2; + protected virtual Colour4 TextColour => Active.Value ? (Colour4)ColourProvider.Background6 : Colour4.White; + protected virtual void UpdateState() { float targetWidth = Active.Value ? EXPANDED_SWITCH_WIDTH : IDLE_SWITCH_WIDTH; double transitionDuration = TRANSITION_DURATION; - Colour4 textBackgroundColour = Active.Value ? activeColour : (Colour4)ColourProvider.Background2; - Colour4 mainBackgroundColour = Active.Value ? activeColour.Darken(0.3f) : (Colour4)ColourProvider.Background3; - Colour4 textColour = Active.Value ? (Colour4)ColourProvider.Background6 : Colour4.White; + Colour4 backgroundColour = BackgroundColour; + Colour4 foregroundColour = ForegroundColour; + Colour4 textColour = TextColour; // Hover affects colour of button background if (IsHovered) { - textBackgroundColour = textBackgroundColour.Lighten(0.1f); - mainBackgroundColour = mainBackgroundColour.Lighten(0.1f); + backgroundColour = backgroundColour.Lighten(0.1f); + foregroundColour = foregroundColour.Lighten(0.1f); } // Mouse down adds a halfway tween of the movement @@ -226,15 +230,15 @@ namespace osu.Game.Overlays.Mods transitionDuration *= 4; } - Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(mainBackgroundColour, textBackgroundColour), transitionDuration, Easing.OutQuint); - Background.FadeColour(mainBackgroundColour, transitionDuration, Easing.OutQuint); + Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(backgroundColour, foregroundColour), transitionDuration, Easing.OutQuint); + Background.FadeColour(backgroundColour, transitionDuration, Easing.OutQuint); SwitchContainer.ResizeWidthTo(targetWidth, transitionDuration, Easing.OutQuint); MainContentContainer.TransformTo(nameof(Padding), new MarginPadding { Left = targetWidth, Right = CORNER_RADIUS }, transitionDuration, Easing.OutQuint); - TextBackground.FadeColour(textBackgroundColour, transitionDuration, Easing.OutQuint); + TextBackground.FadeColour(foregroundColour, transitionDuration, Easing.OutQuint); TextFlow.FadeColour(textColour, transitionDuration, Easing.OutQuint); } From b7c11cdb8e2736df2ec96e187bb2adb549507276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Apr 2022 23:34:43 +0200 Subject: [PATCH 05/47] Deselect old incompatible mods if any on user mod select screen --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 27 ++++++++++--------- osu.Game/Overlays/Mods/UserModSelectScreen.cs | 21 +++++++++++++++ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 693c85fafc..8a83071109 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -168,7 +168,7 @@ namespace osu.Game.Overlays.Mods foreach (var column in columnFlow) { - column.SelectedMods.BindValueChanged(_ => updateBindableFromSelection()); + column.SelectedMods.BindValueChanged(updateBindableFromSelection); } customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); @@ -237,33 +237,36 @@ namespace osu.Game.Overlays.Mods TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic); } - private bool selectionBindableSyncInProgress; - private void updateSelectionFromBindable() { - if (selectionBindableSyncInProgress) - return; - - selectionBindableSyncInProgress = true; - + // note that selectionBindableSyncInProgress is purposefully not checked here. + // this is because in the case of mod selection in solo gameplay, a user selection of a mod can actually lead to deselection of other incompatible mods. + // to synchronise state correctly, updateBindableFromSelection() computes the final mods (including incompatibility rules) and updates SelectedMods, + // and this method then runs unconditionally again to make sure the new visual selection accurately reflects the final set of selected mods. + // selectionBindableSyncInProgress ensures that mutual infinite recursion does not happen after that unconditional call. foreach (var column in columnFlow) column.SelectedMods.Value = SelectedMods.Value.Where(mod => mod.Type == column.ModType).ToArray(); - - selectionBindableSyncInProgress = false; } - private void updateBindableFromSelection() + private bool selectionBindableSyncInProgress; + + private void updateBindableFromSelection(ValueChangedEvent> modSelectionChange) { if (selectionBindableSyncInProgress) return; selectionBindableSyncInProgress = true; - SelectedMods.Value = columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray(); + SelectedMods.Value = ComputeNewModsFromSelection( + modSelectionChange.NewValue.Except(modSelectionChange.OldValue), + modSelectionChange.OldValue.Except(modSelectionChange.NewValue)); selectionBindableSyncInProgress = false; } + protected virtual IReadOnlyList ComputeNewModsFromSelection(IEnumerable addedMods, IEnumerable removedMods) + => columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray(); + protected override void PopIn() { const double fade_in_duration = 400; diff --git a/osu.Game/Overlays/Mods/UserModSelectScreen.cs b/osu.Game/Overlays/Mods/UserModSelectScreen.cs index 81943da514..ed0a07521b 100644 --- a/osu.Game/Overlays/Mods/UserModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/UserModSelectScreen.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.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using osu.Game.Rulesets.Mods; +using osu.Game.Utils; using osuTK.Input; namespace osu.Game.Overlays.Mods @@ -11,6 +14,24 @@ namespace osu.Game.Overlays.Mods { protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys); + protected override IReadOnlyList ComputeNewModsFromSelection(IEnumerable addedMods, IEnumerable removedMods) + { + IEnumerable modsAfterRemoval = SelectedMods.Value.Except(removedMods).ToList(); + + // the preference is that all new mods should override potential incompatible old mods. + // in general that's a bit difficult to compute if more than one mod is added at a time, + // so be conservative and just remove all mods that aren't compatible with any one added mod. + foreach (var addedMod in addedMods) + { + if (!ModUtils.CheckCompatibleSet(modsAfterRemoval.Append(addedMod), out var invalidMods)) + modsAfterRemoval = modsAfterRemoval.Except(invalidMods); + + modsAfterRemoval = modsAfterRemoval.Append(addedMod).ToList(); + } + + return modsAfterRemoval.ToList(); + } + private class UserModColumn : ModColumn { public UserModColumn(ModType modType, bool allowBulkSelection, [CanBeNull] Key[] toggleKeys = null) From cd898344cb60663f0c300cd524c2d8e16d3d984c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Apr 2022 23:49:40 +0200 Subject: [PATCH 06/47] Add test coverage of new incompatibility behaviour --- .../UserInterface/TestSceneModSelectScreen.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 4a738cb29d..514538161e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.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 System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -89,6 +90,27 @@ namespace osu.Game.Tests.Visual.UserInterface changeRuleset(3); } + [Test] + public void TestIncompatibilityToggling() + { + createScreen(); + changeRuleset(0); + + AddStep("activate DT", () => getPanelForMod(typeof(OsuModDoubleTime)).TriggerClick()); + AddAssert("DT active", () => SelectedMods.Value.Single().GetType() == typeof(OsuModDoubleTime)); + + AddStep("activate NC", () => getPanelForMod(typeof(OsuModNightcore)).TriggerClick()); + AddAssert("only NC active", () => SelectedMods.Value.Single().GetType() == typeof(OsuModNightcore)); + + AddStep("activate HR", () => getPanelForMod(typeof(OsuModHardRock)).TriggerClick()); + AddAssert("NC+HR active", () => SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModNightcore)) + && SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModHardRock))); + + AddStep("activate MR", () => getPanelForMod(typeof(OsuModMirror)).TriggerClick()); + AddAssert("NC+MR active", () => SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModNightcore)) + && SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModMirror))); + } + [Test] public void TestCustomisationToggleState() { @@ -136,5 +158,8 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert($"customisation toggle is {(disabled ? "" : "not ")}disabled", () => getToggle().Active.Disabled == disabled); AddAssert($"customisation toggle is {(active ? "" : "not ")}active", () => getToggle().Active.Value == active); } + + private ModPanel getPanelForMod(Type modType) + => modSelectScreen.ChildrenOfType().Single(panel => panel.Mod.GetType() == modType); } } From 82a1d1cc2eab41622c5b69a2867a0a25521e97f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 15:22:58 +0900 Subject: [PATCH 07/47] Fix multiplier display not blocking input --- osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 66fd6a202d..1d848fe456 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Mods Height = HEIGHT; AutoSizeAxes = Axes.X; - InternalChild = new Container + InternalChild = new InputBlockingContainer { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, From 14e17c8b7b4892c8485ff907a6790d1d5b041708 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 16:22:12 +0900 Subject: [PATCH 08/47] Update `TestIncompatibilityDisplay` in line with new functionality --- .../Visual/UserInterface/TestSceneModPanel.cs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs index 95323e5dfa..f56d9c8a91 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs @@ -47,12 +47,22 @@ namespace osu.Game.Tests.Visual.UserInterface { IncompatibilityDisplayingModPanel panel = null; - AddStep("create panel with DT", () => Child = panel = new IncompatibilityDisplayingModPanel(new OsuModDoubleTime()) + AddStep("create panel with DT", () => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.None, - Width = 300 + Child = panel = new IncompatibilityDisplayingModPanel(new OsuModDoubleTime()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.None, + Width = 300, + }; + + panel.Active.BindValueChanged(active => + { + SelectedMods.Value = active.NewValue + ? Array.Empty() + : new[] { panel.Mod }; + }); }); clickPanel(); @@ -63,11 +73,6 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("set incompatible mod", () => SelectedMods.Value = new[] { new OsuModHalfTime() }); - clickPanel(); - AddAssert("panel not active", () => !panel.Active.Value); - - AddStep("reset mods", () => SelectedMods.Value = Array.Empty()); - clickPanel(); AddAssert("panel active", () => panel.Active.Value); From bcdd1fb183b0b612290f23c2129c3948d2210262 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 18:37:40 +0900 Subject: [PATCH 09/47] Fix nested song select in first-run dialog fiddling with global audio --- osu.Game/Screens/Select/SongSelect.cs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 928978cd08..2a1ed2a7a8 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -50,6 +50,12 @@ namespace osu.Game.Screens.Select public FilterControl FilterControl { get; private set; } + /// + /// Whether this song select instance should take control of the global track, + /// applying looping and preview offsets. + /// + protected virtual bool ControlGlobalMusic => true; + protected virtual bool ShowFooter => true; protected virtual bool DisplayStableImportPrompt => legacyImportManager?.SupportsImportFromStable == true; @@ -604,15 +610,18 @@ namespace osu.Game.Screens.Select BeatmapDetails.Refresh(); beginLooping(); - music.ResetTrackAdjustments(); if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending) { updateComponentFromBeatmap(Beatmap.Value); - // restart playback on returning to song select, regardless. - // not sure this should be a permanent thing (we may want to leave a user pause paused even on returning) - music.Play(requestedByUser: true); + if (ControlGlobalMusic) + { + // restart playback on returning to song select, regardless. + // not sure this should be a permanent thing (we may want to leave a user pause paused even on returning) + music.ResetTrackAdjustments(); + music.Play(requestedByUser: true); + } } this.FadeIn(250); @@ -663,6 +672,9 @@ namespace osu.Game.Screens.Select private void beginLooping() { + if (!ControlGlobalMusic) + return; + Debug.Assert(!isHandlingLooping); isHandlingLooping = true; @@ -733,6 +745,9 @@ namespace osu.Game.Screens.Select /// private void ensurePlayingSelected() { + if (!ControlGlobalMusic) + return; + ITrack track = music.CurrentTrack; bool isNewTrack = !lastTrack.TryGetTarget(out var last) || last != track; From 5f3ff51f7e1f1c416b2be768b7433aabf7fbb5be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 21:24:58 +0900 Subject: [PATCH 10/47] Revert "Remove one more unnecessary schedule" This reverts commit f282e5b0133785d8c9e6474cbe04bf5c79dc4811. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 823ba33216..540ca85809 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -195,8 +195,11 @@ namespace osu.Game.Graphics.Containers protected void InvalidateScrollPosition() { - lastKnownScroll = null; - lastClickedSection = null; + Schedule(() => + { + lastKnownScroll = null; + lastClickedSection = null; + }); } protected override void UpdateAfterChildren() From 0dc3d1041fe22d4c0bbc245327b6a2cd46f49e36 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 19:23:54 +0300 Subject: [PATCH 11/47] Revert "Revert "Remove one more unnecessary schedule"" This reverts commit 5f3ff51f7e1f1c416b2be768b7433aabf7fbb5be. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 540ca85809..823ba33216 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -195,11 +195,8 @@ namespace osu.Game.Graphics.Containers protected void InvalidateScrollPosition() { - Schedule(() => - { - lastKnownScroll = null; - lastClickedSection = null; - }); + lastKnownScroll = null; + lastClickedSection = null; } protected override void UpdateAfterChildren() From 2961c383f68891bdc954ecab928fda81cad765ba Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 20:04:04 +0300 Subject: [PATCH 12/47] Add failing test case --- .../Visual/Settings/TestSceneSettingsPanel.cs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index cdeaafd828..377873f64a 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -33,18 +33,21 @@ namespace osu.Game.Tests.Visual.Settings State = { Value = Visibility.Visible } }); }); + + AddStep("reset mouse", () => InputManager.MoveMouseTo(settings)); } [Test] - public void TestQuickFiltering() + public void TestFiltering([Values] bool beforeLoad) { - AddStep("set filter", () => - { - settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"; - }); + if (beforeLoad) + AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); AddUntilStep("wait for items to load", () => settings.SectionsContainer.ChildrenOfType().Any()); + if (!beforeLoad) + AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); + AddAssert("ensure all items match filter", () => settings.SectionsContainer .ChildrenOfType().Where(f => f.IsPresent) .All(section => @@ -56,6 +59,15 @@ namespace osu.Game.Tests.Visual.Settings )); AddAssert("ensure section is current", () => settings.CurrentSection.Value is GraphicsSection); + AddAssert("ensure section is placed first", () => settings.CurrentSection.Value.Y == 0); + } + + [Test] + public void TestFilterAfterLoad() + { + AddUntilStep("wait for items to load", () => settings.SectionsContainer.ChildrenOfType().Any()); + + AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); } [Test] From 4c7c611218fbf0b4e73aca928f2e1b2081f9d370 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 20:34:00 +0300 Subject: [PATCH 13/47] Fix settings overlay not invalidating presence on filter change --- osu.Game/Overlays/Settings/SettingsItem.cs | 18 +++++++++++++++-- osu.Game/Overlays/Settings/SettingsSection.cs | 20 ++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 1c5668479f..7795b7b4c5 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -100,9 +100,23 @@ namespace osu.Game.Overlays.Settings public IEnumerable Keywords { get; set; } - public override bool IsPresent => base.IsPresent && MatchingFilter; + private bool matchingFilter; - public bool MatchingFilter { get; set; } = true; + public bool MatchingFilter + { + get => matchingFilter; + set + { + bool wasPresent = IsPresent; + + matchingFilter = value; + + if (IsPresent != wasPresent) + Invalidate(Invalidation.Presence); + } + } + + public override bool IsPresent => base.IsPresent && MatchingFilter; public bool FilteringActive { get; set; } diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 28c42a0e47..694823314e 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -21,8 +21,6 @@ namespace osu.Game.Overlays.Settings protected FillFlowContainer FlowContent; protected override Container Content => FlowContent; - public override bool IsPresent => base.IsPresent && MatchingFilter; - private IBindable selectedSection; private Box dim; @@ -40,7 +38,23 @@ namespace osu.Game.Overlays.Settings private const int header_size = 24; private const int border_size = 4; - public bool MatchingFilter { get; set; } = true; + private bool matchingFilter; + + public bool MatchingFilter + { + get => matchingFilter; + set + { + bool wasPresent = IsPresent; + + matchingFilter = value; + + if (IsPresent != wasPresent) + Invalidate(Invalidation.Presence); + } + } + + public override bool IsPresent => base.IsPresent && MatchingFilter; public bool FilteringActive { get; set; } From 7be25963121626e9f024787d1c24f13409e7952b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 23:50:58 +0300 Subject: [PATCH 14/47] Fix `MatchingFilter` being false by default --- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- osu.Game/Overlays/Settings/SettingsSection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 7795b7b4c5..f7824d79e7 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -100,7 +100,7 @@ namespace osu.Game.Overlays.Settings public IEnumerable Keywords { get; set; } - private bool matchingFilter; + private bool matchingFilter = true; public bool MatchingFilter { diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 694823314e..b5f3d8e003 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Settings private const int header_size = 24; private const int border_size = 4; - private bool matchingFilter; + private bool matchingFilter = true; public bool MatchingFilter { From 56358ef19ed9ebe69a74b25eff50e537b5e63fe2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 00:35:45 +0300 Subject: [PATCH 15/47] Fix legacy skin hit animation lookup falling back to `LookupName` --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index f7d5581621..aed6026d69 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -390,7 +390,7 @@ namespace osu.Game.Skinning return new LegacyJudgementPieceOld(resultComponent.Component, createDrawable); } - break; + return null; } return this.GetAnimation(component.LookupName, false, false); From 69e2e309717382f6ae63d5b24205a249f089b562 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 01:06:35 +0300 Subject: [PATCH 16/47] Remove legacy skin component fallback lookup Has always been a source of hiding actual issues like in https://github.com/ppy/osu/issues/17745 (`sliderfollowcircle` looked up from fallback because there's no transformer), and https://github.com/ppy/osu/issues/17934 (because there's a texture `ok` and that matches the component name `HitResult.Ok`). --- osu.Game/Skinning/LegacySkin.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index aed6026d69..1b3b0eea18 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -391,9 +391,10 @@ namespace osu.Game.Skinning } return null; - } - return this.GetAnimation(component.LookupName, false, false); + default: + throw new ArgumentOutOfRangeException(nameof(component)); + } } private Texture? getParticleTexture(HitResult result) From 61078910a64de149e999f970146837b3334d8bfa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 01:08:53 +0300 Subject: [PATCH 17/47] Handle all skin component types explicitly --- .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 3 +++ .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 5 +++-- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 6 ++++++ .../Skinning/Legacy/TaikoLegacySkinTransformer.cs | 7 +++++++ osu.Game/Skinning/DefaultSkin.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 5 ++++- osu.Game/Skinning/SkinnableSprite.cs | 2 +- .../Skinning/UnsupportedSkinComponentException.cs | 15 +++++++++++++++ 8 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Skinning/UnsupportedSkinComponentException.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index b2a555f89d..04b522b404 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -90,6 +90,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy return new LegacyHitExplosion(); return null; + + default: + throw new UnsupportedSkinComponentException(component); } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 431bd77402..315b4444c2 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -116,9 +116,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy case ManiaSkinComponents.StageForeground: return new LegacyStageForeground(); - } - break; + default: + throw new UnsupportedSkinComponentException(component); + } } return base.GetDrawableComponent(component); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 900ad6f6d3..572185e6e1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -35,6 +35,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case OsuSkinComponents.FollowPoint: return this.GetAnimation(component.LookupName, true, true, true, startAtCurrentTime: false); + case OsuSkinComponents.SliderScorePoint: + return this.GetAnimation(component.LookupName, false, false); + case OsuSkinComponents.SliderFollowCircle: var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true); if (followCircle != null) @@ -123,6 +126,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case OsuSkinComponents.ApproachCircle: return new LegacyApproachCircle(); + + default: + throw new UnsupportedSkinComponentException(component); } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs index bbc8f0abea..af5921b0fb 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs @@ -57,6 +57,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy case TaikoSkinComponents.DrumRollTick: return this.GetAnimation("sliderscorepoint", false, false); + case TaikoSkinComponents.Swell: + // todo: support taiko legacy swell (https://github.com/ppy/osu/issues/13601). + return null; + case TaikoSkinComponents.HitTarget: if (GetTexture("taikobigcircle") != null) return new TaikoLegacyHitTarget(); @@ -119,6 +123,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy case TaikoSkinComponents.Mascot: return new DrawableTaikoMascot(); + + default: + throw new UnsupportedSkinComponentException(component); } } diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 119b0ec9ad..fb24084659 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -155,7 +155,7 @@ namespace osu.Game.Skinning return skinnableTargetWrapper; } - break; + return null; } switch (component.LookupName) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 1b3b0eea18..b65ba8b04c 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -392,8 +392,11 @@ namespace osu.Game.Skinning return null; + case SkinnableSprite.SpriteComponent sprite: + return this.GetAnimation(sprite.LookupName, false, false); + default: - throw new ArgumentOutOfRangeException(nameof(component)); + throw new UnsupportedSkinComponentException(component); } } diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index 4b4d7fe2c6..af9ff0f1d7 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -65,7 +65,7 @@ namespace osu.Game.Skinning public bool UsesFixedAnchor { get; set; } - private class SpriteComponent : ISkinComponent + public class SpriteComponent : ISkinComponent { public string LookupName { get; set; } diff --git a/osu.Game/Skinning/UnsupportedSkinComponentException.cs b/osu.Game/Skinning/UnsupportedSkinComponentException.cs new file mode 100644 index 0000000000..643f29c3db --- /dev/null +++ b/osu.Game/Skinning/UnsupportedSkinComponentException.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Skinning +{ + public class UnsupportedSkinComponentException : Exception + { + public UnsupportedSkinComponentException(ISkinComponent component) + : base($@"Unsupported component type: {component.GetType()}(""{component.LookupName}"").") + { + } + } +} From 89519086c89fd8897e65816677f1ca088e743907 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 03:16:58 +0300 Subject: [PATCH 18/47] Fix `SkinnableTestScene` unnecessarily adding a base raw skin layer --- osu.Game/Tests/Visual/SkinnableTestScene.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 2e1ca09fe4..296ed80e37 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -74,11 +74,15 @@ namespace osu.Game.Tests.Visual createdDrawables.Add(created); - SkinProvidingContainer mainProvider; Container childContainer; OutlineBox outlineBox; SkinProvidingContainer skinProvider; + ISkin provider = skin; + + if (provider is LegacySkin legacyProvider) + provider = Ruleset.Value.CreateInstance().CreateLegacySkinProvider(legacyProvider, beatmap); + var children = new Container { RelativeSizeAxes = Axes.Both, @@ -107,12 +111,10 @@ namespace osu.Game.Tests.Visual Children = new Drawable[] { outlineBox = new OutlineBox(), - (mainProvider = new SkinProvidingContainer(skin)).WithChild( - skinProvider = new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider, beatmap)) - { - Child = created, - } - ) + skinProvider = new SkinProvidingContainer(provider) + { + Child = created, + } } }, } @@ -130,7 +132,7 @@ namespace osu.Game.Tests.Visual { bool autoSize = created.RelativeSizeAxes == Axes.None; - foreach (var c in new[] { mainProvider, childContainer, skinProvider }) + foreach (var c in new[] { childContainer, skinProvider }) { c.RelativeSizeAxes = Axes.None; c.AutoSizeAxes = Axes.None; From 610d61dce9dcc1be01acf3dce37ea254b6125942 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 03:17:18 +0300 Subject: [PATCH 19/47] Fix `TestSceneSliderApplication` not applying legacy skin transformer --- osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index d673b7a6ac..a40ae611d8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -70,7 +70,9 @@ namespace osu.Game.Rulesets.Osu.Tests var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo()); tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1"; - Child = new SkinProvidingContainer(tintingSkin) + var provider = Ruleset.Value.CreateInstance().CreateLegacySkinProvider(tintingSkin, Beatmap.Value.Beatmap); + + Child = new SkinProvidingContainer(provider) { RelativeSizeAxes = Axes.Both, Child = dho = new DrawableSlider(prepareObject(new Slider From 27f3499330536b0ab2e86e600478a751fcb1e9d5 Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 23 Apr 2022 00:17:00 -0300 Subject: [PATCH 20/47] Add joystick/gamepad deadzone setting Also splits joystick/gamepad into a new sub-section. --- .../Localisation/JoystickSettingsStrings.cs | 21 +++++++ .../Sections/Input/JoystickSettings.cs | 62 +++++++++++++++++++ .../Settings/Sections/InputSection.cs | 5 +- 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Localisation/JoystickSettingsStrings.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs diff --git a/osu.Game/Localisation/JoystickSettingsStrings.cs b/osu.Game/Localisation/JoystickSettingsStrings.cs new file mode 100644 index 0000000000..6ae8bb66dc --- /dev/null +++ b/osu.Game/Localisation/JoystickSettingsStrings.cs @@ -0,0 +1,21 @@ +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class JoystickSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.JoystickSettings"; + + /// + /// "Joystick / Gamepad" + /// + public static LocalisableString JoystickGamepad => new TranslatableString(getKey(@"joystick_gamepad"), @"Joystick / Gamepad"); + + /// + /// "Deadzone Threshold" + /// + public static LocalisableString DeadzoneThreshold => new TranslatableString(getKey(@"deadzone_threshold"), @"Deadzone Threshold"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs new file mode 100644 index 0000000000..fd1dbc876a --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -0,0 +1,62 @@ +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Input.Handlers.Joystick; +using osu.Framework.Localisation; +using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; + +namespace osu.Game.Overlays.Settings.Sections.Input +{ + public class JoystickSettings : SettingsSubsection + { + private readonly JoystickHandler joystickHandler; + + protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; + private readonly BindableNumber deadzoneThreshold = new BindableNumber(); + private readonly Bindable enabled = new BindableBool(true); + public JoystickSettings(JoystickHandler joystickHandler) + { + this.joystickHandler = joystickHandler; + } + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = CommonStrings.Enabled, + Current = enabled + }, + new DeadzoneSetting + { + LabelText = JoystickSettingsStrings.DeadzoneThreshold, + Current = deadzoneThreshold + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + enabled.BindTo(joystickHandler.Enabled); + deadzoneThreshold.BindTo(joystickHandler.DeadzoneThreshold); + enabled.BindValueChanged(e => deadzoneThreshold.Disabled = !e.NewValue, true); + } + + private class DeadzoneSetting : SettingsSlider + { + public DeadzoneSetting() + { + KeyboardStep = 0.005f; + TransferValueOnCommit = true; + } + } + + private class DeadzoneSlider : OsuSliderBar + { + public override LocalisableString TooltipText => Current.Disabled ? "" : base.TooltipText; + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index d282ba5318..d2c5d2fcf7 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -68,7 +68,10 @@ namespace osu.Game.Overlays.Settings.Sections break; // whitelist the handlers which should be displayed to avoid any weird cases of users touching settings they shouldn't. - case JoystickHandler _: + case JoystickHandler jh: + section = new JoystickSettings(jh); + break; + case MidiHandler _: section = new HandlerSection(handler); break; From 99f276a570950a93f5fdf9022aad10e356da7ae7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Apr 2022 13:30:23 +0900 Subject: [PATCH 21/47] 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 8d79eb94a8..82dec74855 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 c6c18f6061..325e834fa5 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 64af0d70f3..8775442be2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 23ef1e194db1ef1fdaefe0664622547c59c3caed Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 23 Apr 2022 01:52:59 -0300 Subject: [PATCH 22/47] Code Quality Whoops. --- .../Localisation/JoystickSettingsStrings.cs | 3 + .../Sections/Input/JoystickSettings.cs | 85 ++++++++++--------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/osu.Game/Localisation/JoystickSettingsStrings.cs b/osu.Game/Localisation/JoystickSettingsStrings.cs index 6ae8bb66dc..ee2096ef89 100644 --- a/osu.Game/Localisation/JoystickSettingsStrings.cs +++ b/osu.Game/Localisation/JoystickSettingsStrings.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using osu.Framework.Localisation; namespace osu.Game.Localisation diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs index fd1dbc876a..aa6d602b6d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -11,52 +14,50 @@ namespace osu.Game.Overlays.Settings.Sections.Input public class JoystickSettings : SettingsSubsection { private readonly JoystickHandler joystickHandler; - - protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; - private readonly BindableNumber deadzoneThreshold = new BindableNumber(); - private readonly Bindable enabled = new BindableBool(true); - public JoystickSettings(JoystickHandler joystickHandler) + protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; + private readonly BindableNumber deadzoneThreshold = new BindableNumber(); + private readonly Bindable enabled = new BindableBool(true); + public JoystickSettings(JoystickHandler joystickHandler) + { + this.joystickHandler = joystickHandler; + } + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] { - this.joystickHandler = joystickHandler; - } - [BackgroundDependencyLoader] - private void load() - { - Children = new Drawable[] + new SettingsCheckbox { - new SettingsCheckbox - { - LabelText = CommonStrings.Enabled, - Current = enabled - }, - new DeadzoneSetting - { - LabelText = JoystickSettingsStrings.DeadzoneThreshold, - Current = deadzoneThreshold - }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - enabled.BindTo(joystickHandler.Enabled); - deadzoneThreshold.BindTo(joystickHandler.DeadzoneThreshold); - enabled.BindValueChanged(e => deadzoneThreshold.Disabled = !e.NewValue, true); - } - - private class DeadzoneSetting : SettingsSlider - { - public DeadzoneSetting() + LabelText = CommonStrings.Enabled, + Current = enabled + }, + new DeadzoneSetting { - KeyboardStep = 0.005f; - TransferValueOnCommit = true; - } - } - - private class DeadzoneSlider : OsuSliderBar + LabelText = JoystickSettingsStrings.DeadzoneThreshold, + Current = deadzoneThreshold + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + enabled.BindTo(joystickHandler.Enabled); + deadzoneThreshold.BindTo(joystickHandler.DeadzoneThreshold); + enabled.BindValueChanged(e => deadzoneThreshold.Disabled = !e.NewValue, true); + } + + private class DeadzoneSetting : SettingsSlider + { + public DeadzoneSetting() { - public override LocalisableString TooltipText => Current.Disabled ? "" : base.TooltipText; + KeyboardStep = 0.005f; + TransferValueOnCommit = true; } + } + private class DeadzoneSlider : OsuSliderBar + { + public override LocalisableString TooltipText => Current.Disabled ? "" : base.TooltipText; + } } } \ No newline at end of file From 6a87dfdabb8c56d54fffb2355181a9cde95e4eee Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 23 Apr 2022 02:14:41 -0300 Subject: [PATCH 23/47] More Code Quality Double whoops. --- osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs index aa6d602b6d..aaa8048019 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -17,10 +17,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; private readonly BindableNumber deadzoneThreshold = new BindableNumber(); private readonly Bindable enabled = new BindableBool(true); + public JoystickSettings(JoystickHandler joystickHandler) { this.joystickHandler = joystickHandler; } + [BackgroundDependencyLoader] private void load() { @@ -55,6 +57,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input TransferValueOnCommit = true; } } + private class DeadzoneSlider : OsuSliderBar { public override LocalisableString TooltipText => Current.Disabled ? "" : base.TooltipText; From 0133ee962da47e25fcc6255a94833f34db73955b Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 23 Apr 2022 02:23:07 -0300 Subject: [PATCH 24/47] Change JoystickSettingsStrings.cs text Co-authored-by: Salman Ahmed --- osu.Game/Localisation/JoystickSettingsStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/JoystickSettingsStrings.cs b/osu.Game/Localisation/JoystickSettingsStrings.cs index ee2096ef89..410cd0a6f5 100644 --- a/osu.Game/Localisation/JoystickSettingsStrings.cs +++ b/osu.Game/Localisation/JoystickSettingsStrings.cs @@ -17,7 +17,7 @@ namespace osu.Game.Localisation /// /// "Deadzone Threshold" /// - public static LocalisableString DeadzoneThreshold => new TranslatableString(getKey(@"deadzone_threshold"), @"Deadzone Threshold"); + public static LocalisableString DeadzoneThreshold => new TranslatableString(getKey(@"deadzone_threshold"), @"Deadzone"); private static string getKey(string key) => $@"{prefix}:{key}"; } From 4bf11df57c9ac2ac19646f2ac83a94b75be2279c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 19:11:54 +0300 Subject: [PATCH 25/47] Change `SpriteComponent` accessibility to `internal` --- osu.Game/Skinning/SkinnableSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index af9ff0f1d7..21b34fcd27 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -65,7 +65,7 @@ namespace osu.Game.Skinning public bool UsesFixedAnchor { get; set; } - public class SpriteComponent : ISkinComponent + internal class SpriteComponent : ISkinComponent { public string LookupName { get; set; } From 5addcbf460d569a385c01b1db5a0918d8343d89c Mon Sep 17 00:00:00 2001 From: maromalo <54760464+maromalo@users.noreply.github.com> Date: Sat, 23 Apr 2022 13:16:20 -0300 Subject: [PATCH 26/47] Changed KeyboardStep to 0.01 --- osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs index aaa8048019..ad01c25ac9 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public DeadzoneSetting() { - KeyboardStep = 0.005f; + KeyboardStep = 0.01f; TransferValueOnCommit = true; } } From e89441951cab3b2f3142d3b15c535c6cf867cf45 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 20 Apr 2022 22:00:37 +0200 Subject: [PATCH 27/47] Allow any key to trigger the initial osu! cookie --- osu.Game/Screens/Menu/ButtonSystem.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 885f4903b0..b48aef330a 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -196,11 +196,8 @@ namespace osu.Game.Screens.Menu if (State == ButtonSystemState.Initial) { - if (buttonsTopLevel.Any(b => e.Key == b.TriggerKey)) - { - logo?.TriggerClick(); - return true; - } + logo?.TriggerClick(); + return true; } return base.OnKeyDown(e); From cdfef088464046ee468ca016579d8362441a8c3e Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sat, 23 Apr 2022 18:44:50 +0200 Subject: [PATCH 28/47] Add tests for shortcut keys --- .../UserInterface/TestSceneButtonSystem.cs | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index 1bb5cadc6a..1a879e2e70 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs @@ -10,11 +10,12 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Screens.Menu; using osuTK; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneButtonSystem : OsuTestScene + public class TestSceneButtonSystem : OsuManualInputManagerTestScene { private OsuLogo logo; private ButtonSystem buttons; @@ -64,6 +65,66 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Enter mode", performEnterMode); } + [TestCase(Key.P, true)] + [TestCase(Key.M, true)] + [TestCase(Key.L, true)] + [TestCase(Key.E, false)] + [TestCase(Key.D, false)] + [TestCase(Key.Q, false)] + [TestCase(Key.O, false)] + public void TestShortcutKeys(Key key, bool entersPlay) + { + int activationCount = -1; + AddStep("set up action", () => + { + activationCount = 0; + void action() => activationCount++; + + switch (key) + { + case Key.P: + buttons.OnSolo = action; + break; + + case Key.M: + buttons.OnMultiplayer = action; + break; + + case Key.L: + buttons.OnPlaylists = action; + break; + + case Key.E: + buttons.OnEdit = action; + break; + + case Key.D: + buttons.OnBeatmapListing = action; + break; + + case Key.Q: + buttons.OnExit = action; + break; + + case Key.O: + buttons.OnSettings = action; + break; + } + }); + + AddStep($"press {key}", () => InputManager.Key(key)); + AddAssert("state is top level", () => buttons.State == ButtonSystemState.TopLevel); + + if (entersPlay) + { + AddStep("press P", () => InputManager.Key(Key.P)); + AddAssert("state is play", () => buttons.State == ButtonSystemState.Play); + } + + AddStep($"press {key}", () => InputManager.Key(key)); + AddAssert("action triggered", () => activationCount == 1); + } + private void performEnterMode() { buttons.State = ButtonSystemState.EnteringMode; From 684d88ba7586eb361091f8e235b35c9dc3af54c5 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sat, 23 Apr 2022 19:01:55 +0200 Subject: [PATCH 29/47] Add full OsuGame tests These tests ensure the expected behaviour is not broken in the future. --- .../TestSceneButtonSystemNavigation.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 osu.Game.Tests/Visual/Navigation/TestSceneButtonSystemNavigation.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneButtonSystemNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneButtonSystemNavigation.cs new file mode 100644 index 0000000000..8c96ec699f --- /dev/null +++ b/osu.Game.Tests/Visual/Navigation/TestSceneButtonSystemNavigation.cs @@ -0,0 +1,46 @@ +// 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.Testing; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Select; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Navigation +{ + public class TestSceneButtonSystemNavigation : OsuGameTestScene + { + private ButtonSystem buttons => ((MainMenu)Game.ScreenStack.CurrentScreen).ChildrenOfType().Single(); + + [Test] + public void TestGlobalActionHasPriority() + { + AddAssert("state is initial", () => buttons.State == ButtonSystemState.Initial); + + // triggering the cookie in the initial state with any key should only happen if no other action is bound to that key. + // here, F10 is bound to GlobalAction.ToggleGameplayMouseButtons. + AddStep("press F10", () => InputManager.Key(Key.F10)); + AddAssert("state is initial", () => buttons.State == ButtonSystemState.Initial); + + AddStep("press P", () => InputManager.Key(Key.P)); + AddAssert("state is top level", () => buttons.State == ButtonSystemState.TopLevel); + } + + [Test] + public void TestShortcutKeys() + { + AddAssert("state is initial", () => buttons.State == ButtonSystemState.Initial); + + AddStep("press P", () => InputManager.Key(Key.P)); + AddAssert("state is top level", () => buttons.State == ButtonSystemState.TopLevel); + + AddStep("press P", () => InputManager.Key(Key.P)); + AddAssert("state is play", () => buttons.State == ButtonSystemState.Play); + + AddStep("press P", () => InputManager.Key(Key.P)); + AddAssert("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); + } + } +} From daadc3fc21c35824de511b930ffa06a1fe85c00a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 23:14:14 +0300 Subject: [PATCH 30/47] Simplfiy and share notification dimsiss method across test scenes Might as well inline, but I feel like it will require adding the same comment across all usages, because just `State.Value = Hidden` in an until step feels off, dunno. --- .../TestSceneChangeAndUseGameplayBindings.cs | 9 +-------- .../Navigation/TestSceneMouseWheelVolumeAdjust.cs | 9 +-------- .../Navigation/TestSceneScreenNavigation.cs | 15 +++------------ osu.Game/Tests/Visual/OsuGameTestScene.cs | 7 +++++++ 4 files changed, 12 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs b/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs index 347b4b6c54..b7a74dcd27 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneChangeAndUseGameplayBindings.cs @@ -59,8 +59,7 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for player", () => { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); + DismissAnyNotifications(); return player != null; }); @@ -73,12 +72,6 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("key counter did increase", () => keyCounter.CountPresses == 1); } - private void clickMouseInCentre() - { - InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre); - InputManager.Click(MouseButton.Left); - } - private KeyBindingsSubsection osuBindingSubsection => keyBindingPanel .ChildrenOfType() .FirstOrDefault(s => s.Ruleset.ShortName == "osu"); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs b/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs index 22a00a3e5a..2662b3930c 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs @@ -89,18 +89,11 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for player", () => { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); + DismissAnyNotifications(); return (player = Game.ScreenStack.CurrentScreen as Player) != null; }); AddUntilStep("wait for play time active", () => !player.IsBreakTime.Value); } - - private void clickMouseInCentre() - { - InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre); - InputManager.Click(MouseButton.Left); - } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 0b7242eee0..296a184f4b 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -155,8 +155,7 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for player", () => { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); + DismissAnyNotifications(); return (player = Game.ScreenStack.CurrentScreen as Player) != null; }); @@ -280,8 +279,7 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for player", () => { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); + DismissAnyNotifications(); return (player = Game.ScreenStack.CurrentScreen as Player) != null; }); @@ -596,8 +594,7 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for player", () => { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); + DismissAnyNotifications(); return (player = Game.ScreenStack.CurrentScreen as Player) != null; }); @@ -607,12 +604,6 @@ namespace osu.Game.Tests.Visual.Navigation return () => player; } - private void clickMouseInCentre() - { - InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre); - InputManager.Click(MouseButton.Left); - } - private void pushEscape() => AddStep("Press escape", () => InputManager.Key(Key.Escape)); diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index 7a6f8c8cfb..1b8f8bfd07 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -23,7 +23,9 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Menu; +using osu.Game.Screens.Play; using osuTK.Graphics; +using osuTK.Input; using IntroSequence = osu.Game.Configuration.IntroSequence; namespace osu.Game.Tests.Visual @@ -106,6 +108,11 @@ namespace osu.Game.Tests.Visual protected void ConfirmAtMainMenu() => AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded); + /// + /// Dismisses any notifications pushed which block from interacting with the game (or block screens from loading, e.g. ). + /// + protected void DismissAnyNotifications() => Game.Notifications.State.Value = Visibility.Hidden; + public class TestOsuGame : OsuGame { public new const float SIDE_OVERLAY_OFFSET_RATIO = OsuGame.SIDE_OVERLAY_OFFSET_RATIO; From 6ab188f8f9c52651a64dc26b27534d47a228a133 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 23:15:31 +0300 Subject: [PATCH 31/47] Split skin editor scene library test to own file Rather than stuffing more random cases to `TestSceneScreenNavigation`. --- .../Navigation/TestSceneScreenNavigation.cs | 71 -------------- .../TestSceneSkinEditorSceneLibrary.cs | 97 +++++++++++++++++++ 2 files changed, 97 insertions(+), 71 deletions(-) create mode 100644 osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 296a184f4b..a1f41d4caf 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -8,7 +8,6 @@ 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; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -16,7 +15,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Leaderboards; using osu.Game.Overlays; using osu.Game.Overlays.Mods; -using osu.Game.Overlays.Settings; using osu.Game.Overlays.Toolbar; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; @@ -24,12 +22,10 @@ using osu.Game.Scoring; using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.Play; -using osu.Game.Screens.Play.HUD.HitErrorMeters; using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Screens.Select.Options; -using osu.Game.Skinning.Editor; using osu.Game.Tests.Beatmaps.IO; using osuTK; using osuTK.Input; @@ -71,73 +67,6 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); } - [Test] - public void TestEditComponentDuringGameplay() - { - Screens.Select.SongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestPlaySongSelect()); - AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); - - AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); - - AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); - - SkinEditor skinEditor = null; - - AddStep("open skin editor", () => - { - InputManager.PressKey(Key.ControlLeft); - InputManager.PressKey(Key.ShiftLeft); - InputManager.Key(Key.S); - InputManager.ReleaseKey(Key.ControlLeft); - InputManager.ReleaseKey(Key.ShiftLeft); - }); - - AddUntilStep("get skin editor", () => (skinEditor = Game.ChildrenOfType().FirstOrDefault()) != null); - - AddStep("Click gameplay scene button", () => - { - skinEditor.ChildrenOfType().First(b => b.Text == "Gameplay").TriggerClick(); - }); - - AddUntilStep("wait for player", () => - { - // dismiss any notifications that may appear (ie. muted notification). - clickMouseInCentre(); - return Game.ScreenStack.CurrentScreen is Player; - }); - - BarHitErrorMeter hitErrorMeter = null; - - AddUntilStep("select bar hit error blueprint", () => - { - var blueprint = skinEditor.ChildrenOfType().FirstOrDefault(b => b.Item is BarHitErrorMeter); - - if (blueprint == null) - return false; - - hitErrorMeter = (BarHitErrorMeter)blueprint.Item; - skinEditor.SelectedComponents.Clear(); - skinEditor.SelectedComponents.Add(blueprint.Item); - return true; - }); - - AddAssert("value is default", () => hitErrorMeter.JudgementLineThickness.IsDefault); - - AddStep("hover first slider", () => - { - InputManager.MoveMouseTo( - skinEditor.ChildrenOfType().First() - .ChildrenOfType>().First() - .ChildrenOfType>().First() - ); - }); - - AddStep("adjust slider via keyboard", () => InputManager.Key(Key.Left)); - - AddAssert("value is less than default", () => hitErrorMeter.JudgementLineThickness.Value < hitErrorMeter.JudgementLineThickness.Default); - } - [Test] public void TestRetryCountIncrements() { diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs new file mode 100644 index 0000000000..ed3eae528f --- /dev/null +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs @@ -0,0 +1,97 @@ +// 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.Extensions; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Testing; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD.HitErrorMeters; +using osu.Game.Skinning.Editor; +using osu.Game.Tests.Beatmaps.IO; +using osuTK.Input; +using static osu.Game.Tests.Visual.Navigation.TestSceneScreenNavigation; + +namespace osu.Game.Tests.Visual.Navigation +{ + public class TestSceneSkinEditorSceneLibrary : OsuGameTestScene + { + private SkinEditor skinEditor; + + public override void SetUpSteps() + { + base.SetUpSteps(); + + Screens.Select.SongSelect songSelect = null; + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); + AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); + + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + + AddStep("open skin editor", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.S); + InputManager.ReleaseKey(Key.ControlLeft); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + + AddUntilStep("get skin editor", () => (skinEditor = Game.ChildrenOfType().FirstOrDefault()) != null); + } + + [Test] + public void TestEditComponentDuringGameplay() + { + switchToGameplayScene(); + + BarHitErrorMeter hitErrorMeter = null; + + AddUntilStep("select bar hit error blueprint", () => + { + var blueprint = skinEditor.ChildrenOfType().FirstOrDefault(b => b.Item is BarHitErrorMeter); + + if (blueprint == null) + return false; + + hitErrorMeter = (BarHitErrorMeter)blueprint.Item; + skinEditor.SelectedComponents.Clear(); + skinEditor.SelectedComponents.Add(blueprint.Item); + return true; + }); + + AddAssert("value is default", () => hitErrorMeter.JudgementLineThickness.IsDefault); + + AddStep("hover first slider", () => + { + InputManager.MoveMouseTo( + skinEditor.ChildrenOfType().First() + .ChildrenOfType>().First() + .ChildrenOfType>().First() + ); + }); + + AddStep("adjust slider via keyboard", () => InputManager.Key(Key.Left)); + + AddAssert("value is less than default", () => hitErrorMeter.JudgementLineThickness.Value < hitErrorMeter.JudgementLineThickness.Default); + } + + private void switchToGameplayScene() + { + AddStep("Click gameplay scene button", () => skinEditor.ChildrenOfType().First(b => b.Text == "Gameplay").TriggerClick()); + + AddUntilStep("wait for player", () => + { + DismissAnyNotifications(); + return Game.ScreenStack.CurrentScreen is Player; + }); + } + } +} From 60e76d62c1231ce81e1c7725136fb160f8d4e750 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 23:16:06 +0300 Subject: [PATCH 32/47] Add failing test cases --- .../TestSceneSkinEditorSceneLibrary.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs index ed3eae528f..448d6b1e68 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs @@ -83,6 +83,46 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("value is less than default", () => hitErrorMeter.JudgementLineThickness.Value < hitErrorMeter.JudgementLineThickness.Default); } + [Test] + public void TestAutoplayCompatibleModsRetainedOnEnteringGameplay() + { + AddStep("select DT", () => Game.SelectedMods.Value = new Mod[] { new OsuModDoubleTime() }); + + switchToGameplayScene(); + + AddAssert("DT still selected", () => ((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Single() is OsuModDoubleTime); + } + + [Test] + public void TestAutoplayIncompatibleModsRemovedOnEnteringGameplay() + { + AddStep("select no fail and spun out", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModSpunOut() }); + + switchToGameplayScene(); + + AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any()); + } + + [Test] + public void TestDuplicateAutoplayModRemovedOnEnteringGameplay() + { + AddStep("select autoplay", () => Game.SelectedMods.Value = new Mod[] { new OsuModAutoplay() }); + + switchToGameplayScene(); + + AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any()); + } + + [Test] + public void TestCinemaModRemovedOnEnteringGameplay() + { + AddStep("select cinema", () => Game.SelectedMods.Value = new Mod[] { new OsuModCinema() }); + + switchToGameplayScene(); + + AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any()); + } + private void switchToGameplayScene() { AddStep("Click gameplay scene button", () => skinEditor.ChildrenOfType().First(b => b.Text == "Gameplay").TriggerClick()); From 0ecda46d93261dce4ebae52eaec3da768c16da1e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 23:16:32 +0300 Subject: [PATCH 33/47] Deselect mods incompatible with "Autoplay" when entering gameplay via scene library --- osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs index 4507526806..2124ba9b6d 100644 --- a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs +++ b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.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. +using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -18,6 +20,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens; using osu.Game.Screens.Play; using osu.Game.Screens.Select; +using osu.Game.Utils; using osuTK; namespace osu.Game.Skinning.Editor @@ -34,6 +37,9 @@ namespace osu.Game.Skinning.Editor [Resolved] private IBindable ruleset { get; set; } + [Resolved] + private Bindable> mods { get; set; } + public SkinEditorSceneLibrary() { Height = BUTTON_HEIGHT + padding * 2; @@ -95,6 +101,10 @@ namespace osu.Game.Skinning.Editor return; var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod(); + + if (!ModUtils.CheckCompatibleSet(mods.Value.Append(replayGeneratingMod), out var invalid)) + mods.Value = mods.Value.Except(invalid).ToArray(); + if (replayGeneratingMod != null) screen.Push(new PlayerLoader(() => new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods)))); }, new[] { typeof(Player), typeof(SongSelect) }) From e8cf4466b0caee3478ca109329b237608748b12a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 23:19:03 +0300 Subject: [PATCH 34/47] Make sections container test scene more usable Head busted the moment I opened this test scene. Cleaned it up a bit to make it easier to test changes. --- .../TestSceneSectionsContainer.cs | 146 ++++++++++++++---- 1 file changed, 115 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs index 2312c57af2..fc2b5e5e09 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs @@ -3,45 +3,79 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneSectionsContainer : OsuManualInputManagerTestScene { - private readonly SectionsContainer container; + private SectionsContainer container; private float custom; - private const float header_height = 100; - public TestSceneSectionsContainer() + private const float header_expandable_height = 300; + private const float header_fixed_height = 100; + + [SetUpSteps] + public void SetUpSteps() { - container = new SectionsContainer + AddStep("setup container", () => { - RelativeSizeAxes = Axes.Y, - Width = 300, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - FixedHeader = new Box + container = new SectionsContainer { - Alpha = 0.5f, + RelativeSizeAxes = Axes.Y, Width = 300, - Height = header_height, - Colour = Color4.Red - } - }; - container.SelectedSection.ValueChanged += section => - { - if (section.OldValue != null) - section.OldValue.Selected = false; - if (section.NewValue != null) - section.NewValue.Selected = true; - }; - Add(container); + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + }; + + container.SelectedSection.ValueChanged += section => + { + if (section.OldValue != null) + section.OldValue.Selected = false; + if (section.NewValue != null) + section.NewValue.Selected = true; + }; + + Child = container; + }); + + AddToggleStep("disable expandable header", v => container.ExpandableHeader = v + ? null + : new TestBox(@"Expandable Header") + { + RelativeSizeAxes = Axes.X, + Height = header_expandable_height, + BackgroundColour = new OsuColour().GreySky, + }); + + AddToggleStep("disable fixed header", v => container.FixedHeader = v + ? null + : new TestBox(@"Fixed Header") + { + RelativeSizeAxes = Axes.X, + Height = header_fixed_height, + BackgroundColour = new OsuColour().Red.Opacity(0.5f), + }); + + AddToggleStep("disable footer", v => container.Footer = v + ? null + : new TestBox("Footer") + { + RelativeSizeAxes = Axes.X, + Height = 200, + BackgroundColour = new OsuColour().Green4, + }); } [Test] @@ -71,7 +105,6 @@ namespace osu.Game.Tests.Visual.UserInterface { const int sections_count = 11; float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f }; - AddStep("clear", () => container.Clear()); AddStep("fill with sections", () => { for (int i = 0; i < sections_count; i++) @@ -84,9 +117,9 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[scrollIndex]); AddUntilStep("section top is visible", () => { - float scrollPosition = container.ChildrenOfType().First().Current; - float sectionTop = container.Children[scrollIndex].BoundingBox.Top; - return scrollPosition < sectionTop; + var scrollContainer = container.ChildrenOfType().Single(); + float sectionPosition = scrollContainer.GetChildPosInContent(container.Children[scrollIndex]); + return scrollContainer.Current < sectionPosition; }); } @@ -101,15 +134,27 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[sections_count - 1]); } - private static readonly ColourInfo selected_colour = ColourInfo.GradientVertical(Color4.Yellow, Color4.Gold); + private static readonly ColourInfo selected_colour = ColourInfo.GradientVertical(new OsuColour().Orange2, new OsuColour().Orange3); private static readonly ColourInfo default_colour = ColourInfo.GradientVertical(Color4.White, Color4.DarkGray); private void append(float multiplier) { - container.Add(new TestSection + float fixedHeaderHeight = container.FixedHeader?.Height ?? 0; + float expandableHeaderHeight = container.ExpandableHeader?.Height ?? 0; + + float totalHeaderHeight = expandableHeaderHeight + fixedHeaderHeight; + float effectiveHeaderHeight = totalHeaderHeight; + + // if we're in the "next page" of the sections container, + // height of the expandable header should not be accounted. + var scrollContent = container.ChildrenOfType().Single().ScrollContent; + if (totalHeaderHeight + scrollContent.Height >= Content.DrawHeight) + effectiveHeaderHeight -= expandableHeaderHeight; + + container.Add(new TestSection($"Section #{container.Children.Count + 1}") { Width = 300, - Height = (container.ChildSize.Y - header_height) * multiplier, + Height = (Content.DrawHeight - effectiveHeaderHeight) * multiplier, Colour = default_colour }); } @@ -120,11 +165,50 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.ScrollVerticalBy(direction); } - private class TestSection : Box + private class TestSection : TestBox { public bool Selected { - set => Colour = value ? selected_colour : default_colour; + set => BackgroundColour = value ? selected_colour : default_colour; + } + + public TestSection(string label) + : base(label) + { + BackgroundColour = default_colour; + } + } + + private class TestBox : Container + { + private readonly Box background; + private readonly OsuSpriteText text; + + public ColourInfo BackgroundColour + { + set + { + background.Colour = value; + text.Colour = OsuColour.ForegroundTextColourFor(value.AverageColour); + } + } + + public TestBox(string label) + { + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = label, + Font = OsuFont.Default.With(size: 36), + } + }; } } } From 05736fb84ce8404070d38180efa663a5a3d67dd6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 24 Apr 2022 01:09:24 +0300 Subject: [PATCH 35/47] Add failing test case for navigation via PageUp/PageDown --- .../TestSceneSectionsContainer.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs index fc2b5e5e09..1f3736bd9b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs @@ -134,6 +134,35 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[sections_count - 1]); } + [Test] + public void TestNavigation() + { + AddRepeatStep("add sections", () => append(1f), 3); + AddUntilStep("wait for load", () => container.Children.Any()); + + AddStep("hover sections container", () => InputManager.MoveMouseTo(container)); + AddStep("press page down", () => InputManager.Key(Key.PageDown)); + AddUntilStep("scrolled one page down", () => + { + var scroll = container.ChildrenOfType().First(); + return Precision.AlmostEquals(scroll.Current, Content.DrawHeight - header_fixed_height, 1f); + }); + + AddStep("press page down", () => InputManager.Key(Key.PageDown)); + AddUntilStep("scrolled two pages down", () => + { + var scroll = container.ChildrenOfType().First(); + return Precision.AlmostEquals(scroll.Current, (Content.DrawHeight - header_fixed_height) * 2, 1f); + }); + + AddStep("press page up", () => InputManager.Key(Key.PageUp)); + AddUntilStep("scrolled one page up", () => + { + var scroll = container.ChildrenOfType().First(); + return Precision.AlmostEquals(scroll.Current, Content.DrawHeight - header_fixed_height, 1f); + }); + } + private static readonly ColourInfo selected_colour = ColourInfo.GradientVertical(new OsuColour().Orange2, new OsuColour().Orange3); private static readonly ColourInfo default_colour = ColourInfo.GradientVertical(Color4.White, Color4.DarkGray); From 3700c607d8fc9844d406496a8c6ff0d843aa9d06 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 06:03:54 +0300 Subject: [PATCH 36/47] Fix sections scroll container not handling fixed header in scrolls --- osu.Game/Graphics/Containers/SectionsContainer.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 540ca85809..9fa8b352cf 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -149,13 +149,11 @@ namespace osu.Game.Graphics.Containers { lastKnownScroll = null; - float fixedHeaderSize = FixedHeader?.BoundingBox.Height ?? 0; - // implementation similar to ScrollIntoView but a bit more nuanced. float top = scrollContainer.GetChildPosInContent(target); - float bottomScrollExtent = scrollContainer.ScrollableExtent - fixedHeaderSize; - float scrollTarget = top - fixedHeaderSize - scrollContainer.DisplayableContent * scroll_y_centre; + float bottomScrollExtent = scrollContainer.ScrollableExtent; + float scrollTarget = top - scrollContainer.DisplayableContent * scroll_y_centre; if (scrollTarget > bottomScrollExtent) scrollContainer.ScrollToEnd(); @@ -270,9 +268,13 @@ namespace osu.Game.Graphics.Containers { if (!Children.Any()) return; - var newMargin = originalSectionsMargin; + // if a fixed header is present, apply top padding for it + // to make the scroll container aware of its displayable area. + // (i.e. for page up/down to work properly) + scrollContainer.Padding = new MarginPadding { Top = FixedHeader?.LayoutSize.Y ?? 0 }; - newMargin.Top += (headerHeight ?? 0); + var newMargin = originalSectionsMargin; + newMargin.Top += (ExpandableHeader?.LayoutSize.Y ?? 0); newMargin.Bottom += (footerHeight ?? 0); scrollContentContainer.Margin = newMargin; From 2200067c52759a32eb1adfa12fd79347b7f51200 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 16:31:20 +0900 Subject: [PATCH 37/47] Display deadzone as percentage and simplify surrounding code --- .../Sections/Input/JoystickSettings.cs | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs index ad01c25ac9..c136ca6a19 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs @@ -6,18 +6,20 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Handlers.Joystick; using osu.Framework.Localisation; -using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { public class JoystickSettings : SettingsSubsection { - private readonly JoystickHandler joystickHandler; protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; - private readonly BindableNumber deadzoneThreshold = new BindableNumber(); + + private readonly JoystickHandler joystickHandler; + private readonly Bindable enabled = new BindableBool(true); + private SettingsSlider deadzoneSlider; + public JoystickSettings(JoystickHandler joystickHandler) { this.joystickHandler = joystickHandler; @@ -33,10 +35,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = CommonStrings.Enabled, Current = enabled }, - new DeadzoneSetting + deadzoneSlider = new SettingsSlider { LabelText = JoystickSettingsStrings.DeadzoneThreshold, - Current = deadzoneThreshold + KeyboardStep = 0.01f, + DisplayAsPercentage = true, + Current = joystickHandler.DeadzoneThreshold, }, }; } @@ -44,23 +48,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input protected override void LoadComplete() { base.LoadComplete(); + enabled.BindTo(joystickHandler.Enabled); - deadzoneThreshold.BindTo(joystickHandler.DeadzoneThreshold); - enabled.BindValueChanged(e => deadzoneThreshold.Disabled = !e.NewValue, true); - } - - private class DeadzoneSetting : SettingsSlider - { - public DeadzoneSetting() - { - KeyboardStep = 0.01f; - TransferValueOnCommit = true; - } - } - - private class DeadzoneSlider : OsuSliderBar - { - public override LocalisableString TooltipText => Current.Disabled ? "" : base.TooltipText; + enabled.BindValueChanged(e => deadzoneSlider.Current.Disabled = !e.NewValue, true); } } -} \ No newline at end of file +} From 1b2467d3ed7df7e2c2171947e606b296ce48983c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 16:35:41 +0900 Subject: [PATCH 38/47] Disable first run screen from appearing on startup until it is more complete --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 1 + osu.Game/Overlays/FirstRunSetupOverlay.cs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index e925859d71..31c4d66784 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -66,6 +66,7 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] + [Ignore("Enable when first run setup is being displayed on first run.")] public void TestDoesntOpenOnSecondRun() { AddStep("set first run", () => LocalConfig.SetValue(OsuSetting.ShowFirstRunSetup, true)); diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index c4e3626996..dc1ae2be37 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -157,7 +157,8 @@ namespace osu.Game.Overlays config.BindWith(OsuSetting.ShowFirstRunSetup, showFirstRunSetup); - if (showFirstRunSetup.Value) Show(); + // TODO: uncomment when happy with the whole flow. + // if (showFirstRunSetup.Value) Show(); } public override bool OnPressed(KeyBindingPressEvent e) @@ -289,7 +290,8 @@ namespace osu.Game.Overlays } else { - showFirstRunSetup.Value = false; + // TODO: uncomment when happy with the whole flow. + // showFirstRunSetup.Value = false; currentStepIndex = null; Hide(); } From 999b4505d139ae7779958690de8d130b180a09fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 16:37:11 +0900 Subject: [PATCH 39/47] Remove localisation of "hide" string to fix incorrect case --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index c6037d1bd6..9772b1feb3 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -244,7 +244,7 @@ namespace osu.Game.Screens.Select.Carousel } if (hideRequested != null) - items.Add(new OsuMenuItem(CommonStrings.ButtonsHide, MenuItemType.Destructive, () => hideRequested(beatmapInfo))); + items.Add(new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested(beatmapInfo))); return items.ToArray(); } From 66b47d22d78bc45d2e890e0aaa7ac565b1fb2e82 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 17:11:25 +0900 Subject: [PATCH 40/47] Also fix case of login form username/password --- osu.Game/Overlays/Login/LoginForm.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Login/LoginForm.cs b/osu.Game/Overlays/Login/LoginForm.cs index c31416e078..502f0cd22e 100644 --- a/osu.Game/Overlays/Login/LoginForm.cs +++ b/osu.Game/Overlays/Login/LoginForm.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; @@ -51,14 +52,14 @@ namespace osu.Game.Overlays.Login { username = new OsuTextBox { - PlaceholderText = UsersStrings.LoginUsername, + PlaceholderText = UsersStrings.LoginUsername.ToLower(), RelativeSizeAxes = Axes.X, Text = api?.ProvidedUsername ?? string.Empty, TabbableContentContainer = this }, password = new OsuPasswordTextBox { - PlaceholderText = UsersStrings.LoginPassword, + PlaceholderText = UsersStrings.LoginPassword.ToLower(), RelativeSizeAxes = Axes.X, TabbableContentContainer = this, }, From d92c9056005839fc6410c5de133fb7ee8a9c3533 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 18:15:35 +0900 Subject: [PATCH 41/47] Actually use exposed property --- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index d9a612ea26..ef48d9ced5 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.FirstRunSetup new Drawable[] { new SampleScreenContainer(new PinnedMainMenu()), - new SampleScreenContainer(new PlaySongSelect()), + new SampleScreenContainer(new NestedSongSelect()), }, // TODO: add more screens here in the future (gameplay / results) // requires a bit more consideration to isolate their behaviour from the "parent" game. @@ -95,6 +95,11 @@ namespace osu.Game.Overlays.FirstRunSetup } } + private class NestedSongSelect : PlaySongSelect + { + protected override bool ControlGlobalMusic => false; + } + private class PinnedMainMenu : MainMenu { public override void OnEntering(ScreenTransitionEvent e) From 51aa17eb524e70899f09c1058842e0d55f58109b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Apr 2022 22:17:01 +0900 Subject: [PATCH 42/47] Fix context menus not working --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 18 +++++++++++++++++- osu.Game/Graphics/UserInterface/OsuMenu.cs | 18 +++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index b1d4691938..20fa7d5148 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -130,7 +131,22 @@ namespace osu.Game.Graphics.UserInterface BackgroundColourSelected = SelectionColour }; - protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); + protected override ScrollContainer CreateScrollContainer(Direction direction) => new DropdownScrollContainer(direction); + + // Hotfix for https://github.com/ppy/osu/issues/17961 + public class DropdownScrollContainer : OsuScrollContainer + { + public DropdownScrollContainer(Direction direction) + : base(direction) + { + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + base.OnMouseDown(e); + return true; + } + } #region DrawableOsuDropdownMenuItem diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index a16adcbd57..bfdfd32fb3 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osuTK; @@ -81,7 +82,22 @@ namespace osu.Game.Graphics.UserInterface return new DrawableOsuMenuItem(item); } - protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); + protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuMenuScrollContainer(direction); + + // Hotfix for https://github.com/ppy/osu/issues/17961 + public class OsuMenuScrollContainer : OsuScrollContainer + { + public OsuMenuScrollContainer(Direction direction) + : base(direction) + { + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + base.OnMouseDown(e); + return true; + } + } protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical) { From 7ac3ba132ce70a2ba150c6567decde647cb62f3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Apr 2022 18:55:12 +0900 Subject: [PATCH 43/47] Remove unused using statements --- .../Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs | 1 - osu.Game/Tests/Visual/OsuGameTestScene.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs index 448d6b1e68..d3aeba2c0f 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorSceneLibrary.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Linq; using NUnit.Framework; using osu.Framework.Extensions; diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index 1b8f8bfd07..6e4adb4d4c 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -25,7 +25,6 @@ using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osuTK.Graphics; -using osuTK.Input; using IntroSequence = osu.Game.Configuration.IntroSequence; namespace osu.Game.Tests.Visual From d2780c2c50557b1bdbfc1bec444ae9814dd08e80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Apr 2022 19:06:47 +0900 Subject: [PATCH 44/47] Add a touch more detail to the unsupported skin component exception --- osu.Game/Skinning/UnsupportedSkinComponentException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/UnsupportedSkinComponentException.cs b/osu.Game/Skinning/UnsupportedSkinComponentException.cs index 643f29c3db..7f0dd51d5b 100644 --- a/osu.Game/Skinning/UnsupportedSkinComponentException.cs +++ b/osu.Game/Skinning/UnsupportedSkinComponentException.cs @@ -8,7 +8,7 @@ namespace osu.Game.Skinning public class UnsupportedSkinComponentException : Exception { public UnsupportedSkinComponentException(ISkinComponent component) - : base($@"Unsupported component type: {component.GetType()}(""{component.LookupName}"").") + : base($@"Unsupported component type: {component.GetType()} (lookup: ""{component.LookupName}"").") { } } From 5e48b37569a549cd8388985a6efc7b4eca5be72f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Apr 2022 23:58:12 +0900 Subject: [PATCH 45/47] Show "Get Started" text on first run button --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index dc1ae2be37..87e62cbddf 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -304,12 +304,15 @@ namespace osu.Game.Overlays BackButton.Enabled.Value = currentStepIndex > 0; NextButton.Enabled.Value = currentStepIndex != null; - if (currentStepIndex != null) - { - NextButton.Text = currentStepIndex + 1 < steps.Length - ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description) - : CommonStrings.Finish; - } + if (currentStepIndex == null) + return; + + if (currentStepIndex == 0) + NextButton.Text = FirstRunSetupOverlayStrings.GetStarted; + else if (currentStepIndex < steps.Length - 1) + NextButton.Text = FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description); + else + NextButton.Text = CommonStrings.Finish; } private class FirstRunStep From 37ae39f5feb847926d6842e795523977268326a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 15:48:40 +0900 Subject: [PATCH 46/47] Move "Next" localisation to common strings --- osu.Game/Localisation/CommonStrings.cs | 5 +++++ osu.Game/Localisation/FirstRunSetupOverlayStrings.cs | 5 ----- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 6da2e4d272..52e9811cf7 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -14,6 +14,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Back => new TranslatableString(getKey(@"back"), @"Back"); + /// + /// "Next" + /// + public static LocalisableString Next => new TranslatableString(getKey(@"next"), @"Next"); + /// /// "Finish" /// diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs index 001de93c16..ebce9e1ce1 100644 --- a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -48,11 +48,6 @@ osu! is a very configurable game, and diving straight into the settings can some /// public static LocalisableString UIScaleDescription => new TranslatableString(getKey(@"ui_scale_description"), @"The size of the osu! user interface can be adjusted to your liking."); - /// - /// "Next ({0})" - /// - public static LocalisableString Next(LocalisableString nextStepDescription) => new TranslatableString(getKey(@"next"), @"Next ({0})", nextStepDescription); - private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 87e62cbddf..5700fa3763 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -310,7 +310,7 @@ namespace osu.Game.Overlays if (currentStepIndex == 0) NextButton.Text = FirstRunSetupOverlayStrings.GetStarted; else if (currentStepIndex < steps.Length - 1) - NextButton.Text = FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description); + NextButton.Text = new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Next, steps[currentStepIndex.Value + 1].Description); else NextButton.Text = CommonStrings.Finish; } From a078440012482f4b575f74eab6dbee0484c3541f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 16:03:15 +0900 Subject: [PATCH 47/47] Show last step on back button as well --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 5700fa3763..27a057bf09 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -133,7 +133,7 @@ namespace osu.Game.Overlays { BackButton = new DangerousTriangleButton { - Width = 200, + Width = 300, Text = CommonStrings.Back, Action = showPreviousStep, Enabled = { Value = false }, @@ -307,12 +307,22 @@ namespace osu.Game.Overlays if (currentStepIndex == null) return; - if (currentStepIndex == 0) + bool isFirstStep = currentStepIndex == 0; + bool isLastStep = currentStepIndex == steps.Length - 1; + + if (isFirstStep) + { + BackButton.Text = CommonStrings.Back; NextButton.Text = FirstRunSetupOverlayStrings.GetStarted; - else if (currentStepIndex < steps.Length - 1) - NextButton.Text = new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Next, steps[currentStepIndex.Value + 1].Description); + } else - NextButton.Text = CommonStrings.Finish; + { + BackButton.Text = new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Back, steps[currentStepIndex.Value - 1].Description); + + NextButton.Text = isLastStep + ? CommonStrings.Finish + : new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Next, steps[currentStepIndex.Value + 1].Description); + } } private class FirstRunStep