From 2769d8e8cfdbded19d1ba4407053efb8c162ae43 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Apr 2022 16:24:21 +0900 Subject: [PATCH 01/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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 56358ef19ed9ebe69a74b25eff50e537b5e63fe2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Apr 2022 00:35:45 +0300 Subject: [PATCH 09/25] 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 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 10/25] 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 11/25] 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 12/25] 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 13/25] 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 14/25] 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 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 15/25] 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 16/25] 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 17/25] 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 18/25] 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 e8cf4466b0caee3478ca109329b237608748b12a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 22 Apr 2022 23:19:03 +0300 Subject: [PATCH 19/25] 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 20/25] 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 21/25] 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 22/25] 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 23/25] 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 24/25] 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 25/25] 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, },