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.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);
+ }
+ }
+}
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]
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;
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.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs
index 2312c57af2..1f3736bd9b 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,56 @@ 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);
+ [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);
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 +194,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),
+ }
+ };
}
}
}
diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs
index 540ca85809..6ad538959e 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();
@@ -195,11 +193,8 @@ namespace osu.Game.Graphics.Containers
protected void InvalidateScrollPosition()
{
- Schedule(() =>
- {
- lastKnownScroll = null;
- lastClickedSection = null;
- });
+ lastKnownScroll = null;
+ lastClickedSection = null;
}
protected override void UpdateAfterChildren()
@@ -270,9 +265,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;
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)
{
diff --git a/osu.Game/Localisation/JoystickSettingsStrings.cs b/osu.Game/Localisation/JoystickSettingsStrings.cs
new file mode 100644
index 0000000000..410cd0a6f5
--- /dev/null
+++ b/osu.Game/Localisation/JoystickSettingsStrings.cs
@@ -0,0 +1,24 @@
+// 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
+{
+ 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");
+
+ private static string getKey(string key) => $@"{prefix}:{key}";
+ }
+}
\ No newline at end of file
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)
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();
}
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,
},
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..c136ca6a19
--- /dev/null
+++ b/osu.Game/Overlays/Settings/Sections/Input/JoystickSettings.cs
@@ -0,0 +1,56 @@
+// 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;
+using osu.Framework.Input.Handlers.Joystick;
+using osu.Framework.Localisation;
+using osu.Game.Localisation;
+
+namespace osu.Game.Overlays.Settings.Sections.Input
+{
+ public class JoystickSettings : SettingsSubsection
+ {
+ protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad;
+
+ private readonly JoystickHandler joystickHandler;
+
+ private readonly Bindable enabled = new BindableBool(true);
+
+ private SettingsSlider deadzoneSlider;
+
+ public JoystickSettings(JoystickHandler joystickHandler)
+ {
+ this.joystickHandler = joystickHandler;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Children = new Drawable[]
+ {
+ new SettingsCheckbox
+ {
+ LabelText = CommonStrings.Enabled,
+ Current = enabled
+ },
+ deadzoneSlider = new SettingsSlider
+ {
+ LabelText = JoystickSettingsStrings.DeadzoneThreshold,
+ KeyboardStep = 0.01f,
+ DisplayAsPercentage = true,
+ Current = joystickHandler.DeadzoneThreshold,
+ },
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ enabled.BindTo(joystickHandler.Enabled);
+ enabled.BindValueChanged(e => deadzoneSlider.Current.Disabled = !e.NewValue, true);
+ }
+ }
+}
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;
diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs
index 1c5668479f..f7824d79e7 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 = true;
- 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..b5f3d8e003 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 = 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/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);
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();
}
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;
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 @@
-
+