Merge branch 'master' into scene-library-incompatible-mods

This commit is contained in:
Dean Herbert 2022-04-25 18:54:40 +09:00
commit a0e80cf901
22 changed files with 470 additions and 75 deletions

View File

@ -52,7 +52,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.422.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2022.422.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.421.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2022.423.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Transitive Dependencies"> <ItemGroup Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. --> <!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->

View File

@ -0,0 +1,46 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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<ButtonSystem>().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);
}
}
}

View File

@ -33,18 +33,21 @@ namespace osu.Game.Tests.Visual.Settings
State = { Value = Visibility.Visible } State = { Value = Visibility.Visible }
}); });
}); });
AddStep("reset mouse", () => InputManager.MoveMouseTo(settings));
} }
[Test] [Test]
public void TestQuickFiltering() public void TestFiltering([Values] bool beforeLoad)
{ {
AddStep("set filter", () => if (beforeLoad)
{ AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType<SearchTextBox>().First().Current.Value = "scaling");
settings.SectionsContainer.ChildrenOfType<SearchTextBox>().First().Current.Value = "scaling";
});
AddUntilStep("wait for items to load", () => settings.SectionsContainer.ChildrenOfType<IFilterable>().Any()); AddUntilStep("wait for items to load", () => settings.SectionsContainer.ChildrenOfType<IFilterable>().Any());
if (!beforeLoad)
AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType<SearchTextBox>().First().Current.Value = "scaling");
AddAssert("ensure all items match filter", () => settings.SectionsContainer AddAssert("ensure all items match filter", () => settings.SectionsContainer
.ChildrenOfType<SettingsSection>().Where(f => f.IsPresent) .ChildrenOfType<SettingsSection>().Where(f => f.IsPresent)
.All(section => .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 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<IFilterable>().Any());
AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType<SearchTextBox>().First().Current.Value = "scaling");
} }
[Test] [Test]

View File

@ -10,11 +10,12 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Screens.Menu; using osu.Game.Screens.Menu;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
[TestFixture] [TestFixture]
public class TestSceneButtonSystem : OsuTestScene public class TestSceneButtonSystem : OsuManualInputManagerTestScene
{ {
private OsuLogo logo; private OsuLogo logo;
private ButtonSystem buttons; private ButtonSystem buttons;
@ -64,6 +65,66 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("Enter mode", performEnterMode); 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() private void performEnterMode()
{ {
buttons.State = ButtonSystemState.EnteringMode; buttons.State = ButtonSystemState.EnteringMode;

View File

@ -66,6 +66,7 @@ namespace osu.Game.Tests.Visual.UserInterface
} }
[Test] [Test]
[Ignore("Enable when first run setup is being displayed on first run.")]
public void TestDoesntOpenOnSecondRun() public void TestDoesntOpenOnSecondRun()
{ {
AddStep("set first run", () => LocalConfig.SetValue(OsuSetting.ShowFirstRunSetup, true)); AddStep("set first run", () => LocalConfig.SetValue(OsuSetting.ShowFirstRunSetup, true));

View File

@ -3,45 +3,79 @@
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osuTK.Graphics; using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneSectionsContainer : OsuManualInputManagerTestScene public class TestSceneSectionsContainer : OsuManualInputManagerTestScene
{ {
private readonly SectionsContainer<TestSection> container; private SectionsContainer<TestSection> container;
private float custom; 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<TestSection> AddStep("setup container", () =>
{ {
RelativeSizeAxes = Axes.Y, container = new SectionsContainer<TestSection>
Width = 300,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
FixedHeader = new Box
{ {
Alpha = 0.5f, RelativeSizeAxes = Axes.Y,
Width = 300, Width = 300,
Height = header_height, Origin = Anchor.Centre,
Colour = Color4.Red Anchor = Anchor.Centre,
} };
};
container.SelectedSection.ValueChanged += section => container.SelectedSection.ValueChanged += section =>
{ {
if (section.OldValue != null) if (section.OldValue != null)
section.OldValue.Selected = false; section.OldValue.Selected = false;
if (section.NewValue != null) if (section.NewValue != null)
section.NewValue.Selected = true; section.NewValue.Selected = true;
}; };
Add(container);
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] [Test]
@ -71,7 +105,6 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
const int sections_count = 11; const int sections_count = 11;
float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f }; float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f };
AddStep("clear", () => container.Clear());
AddStep("fill with sections", () => AddStep("fill with sections", () =>
{ {
for (int i = 0; i < sections_count; i++) 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("correct section selected", () => container.SelectedSection.Value == container.Children[scrollIndex]);
AddUntilStep("section top is visible", () => AddUntilStep("section top is visible", () =>
{ {
float scrollPosition = container.ChildrenOfType<UserTrackingScrollContainer>().First().Current; var scrollContainer = container.ChildrenOfType<UserTrackingScrollContainer>().Single();
float sectionTop = container.Children[scrollIndex].BoundingBox.Top; float sectionPosition = scrollContainer.GetChildPosInContent(container.Children[scrollIndex]);
return scrollPosition < sectionTop; 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]); 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<UserTrackingScrollContainer>().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<UserTrackingScrollContainer>().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<UserTrackingScrollContainer>().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 static readonly ColourInfo default_colour = ColourInfo.GradientVertical(Color4.White, Color4.DarkGray);
private void append(float multiplier) 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<UserTrackingScrollContainer>().Single().ScrollContent;
if (totalHeaderHeight + scrollContent.Height >= Content.DrawHeight)
effectiveHeaderHeight -= expandableHeaderHeight;
container.Add(new TestSection($"Section #{container.Children.Count + 1}")
{ {
Width = 300, Width = 300,
Height = (container.ChildSize.Y - header_height) * multiplier, Height = (Content.DrawHeight - effectiveHeaderHeight) * multiplier,
Colour = default_colour Colour = default_colour
}); });
} }
@ -120,11 +194,50 @@ namespace osu.Game.Tests.Visual.UserInterface
InputManager.ScrollVerticalBy(direction); InputManager.ScrollVerticalBy(direction);
} }
private class TestSection : Box private class TestSection : TestBox
{ {
public bool Selected 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),
}
};
} }
} }
} }

View File

@ -149,13 +149,11 @@ namespace osu.Game.Graphics.Containers
{ {
lastKnownScroll = null; lastKnownScroll = null;
float fixedHeaderSize = FixedHeader?.BoundingBox.Height ?? 0;
// implementation similar to ScrollIntoView but a bit more nuanced. // implementation similar to ScrollIntoView but a bit more nuanced.
float top = scrollContainer.GetChildPosInContent(target); float top = scrollContainer.GetChildPosInContent(target);
float bottomScrollExtent = scrollContainer.ScrollableExtent - fixedHeaderSize; float bottomScrollExtent = scrollContainer.ScrollableExtent;
float scrollTarget = top - fixedHeaderSize - scrollContainer.DisplayableContent * scroll_y_centre; float scrollTarget = top - scrollContainer.DisplayableContent * scroll_y_centre;
if (scrollTarget > bottomScrollExtent) if (scrollTarget > bottomScrollExtent)
scrollContainer.ScrollToEnd(); scrollContainer.ScrollToEnd();
@ -195,11 +193,8 @@ namespace osu.Game.Graphics.Containers
protected void InvalidateScrollPosition() protected void InvalidateScrollPosition()
{ {
Schedule(() => lastKnownScroll = null;
{ lastClickedSection = null;
lastKnownScroll = null;
lastClickedSection = null;
});
} }
protected override void UpdateAfterChildren() protected override void UpdateAfterChildren()
@ -270,9 +265,13 @@ namespace osu.Game.Graphics.Containers
{ {
if (!Children.Any()) return; 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); newMargin.Bottom += (footerHeight ?? 0);
scrollContentContainer.Margin = newMargin; scrollContentContainer.Margin = newMargin;

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -130,7 +131,22 @@ namespace osu.Game.Graphics.UserInterface
BackgroundColourSelected = SelectionColour BackgroundColourSelected = SelectionColour
}; };
protected override ScrollContainer<Drawable> CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); protected override ScrollContainer<Drawable> 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 #region DrawableOsuDropdownMenuItem

View File

@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osuTK; using osuTK;
@ -81,7 +82,22 @@ namespace osu.Game.Graphics.UserInterface
return new DrawableOsuMenuItem(item); return new DrawableOsuMenuItem(item);
} }
protected override ScrollContainer<Drawable> CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); protected override ScrollContainer<Drawable> 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) protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical)
{ {

View File

@ -0,0 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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";
/// <summary>
/// "Joystick / Gamepad"
/// </summary>
public static LocalisableString JoystickGamepad => new TranslatableString(getKey(@"joystick_gamepad"), @"Joystick / Gamepad");
/// <summary>
/// "Deadzone Threshold"
/// </summary>
public static LocalisableString DeadzoneThreshold => new TranslatableString(getKey(@"deadzone_threshold"), @"Deadzone");
private static string getKey(string key) => $@"{prefix}:{key}";
}
}

View File

@ -62,7 +62,7 @@ namespace osu.Game.Overlays.FirstRunSetup
new Drawable[] new Drawable[]
{ {
new SampleScreenContainer(new PinnedMainMenu()), new SampleScreenContainer(new PinnedMainMenu()),
new SampleScreenContainer(new PlaySongSelect()), new SampleScreenContainer(new NestedSongSelect()),
}, },
// TODO: add more screens here in the future (gameplay / results) // TODO: add more screens here in the future (gameplay / results)
// requires a bit more consideration to isolate their behaviour from the "parent" game. // 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 private class PinnedMainMenu : MainMenu
{ {
public override void OnEntering(ScreenTransitionEvent e) public override void OnEntering(ScreenTransitionEvent e)

View File

@ -157,7 +157,8 @@ namespace osu.Game.Overlays
config.BindWith(OsuSetting.ShowFirstRunSetup, showFirstRunSetup); 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<GlobalAction> e) public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
@ -289,7 +290,8 @@ namespace osu.Game.Overlays
} }
else else
{ {
showFirstRunSetup.Value = false; // TODO: uncomment when happy with the whole flow.
// showFirstRunSetup.Value = false;
currentStepIndex = null; currentStepIndex = null;
Hide(); Hide();
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
@ -51,14 +52,14 @@ namespace osu.Game.Overlays.Login
{ {
username = new OsuTextBox username = new OsuTextBox
{ {
PlaceholderText = UsersStrings.LoginUsername, PlaceholderText = UsersStrings.LoginUsername.ToLower(),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Text = api?.ProvidedUsername ?? string.Empty, Text = api?.ProvidedUsername ?? string.Empty,
TabbableContentContainer = this TabbableContentContainer = this
}, },
password = new OsuPasswordTextBox password = new OsuPasswordTextBox
{ {
PlaceholderText = UsersStrings.LoginPassword, PlaceholderText = UsersStrings.LoginPassword.ToLower(),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
TabbableContentContainer = this, TabbableContentContainer = this,
}, },

View File

@ -0,0 +1,56 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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<bool> enabled = new BindableBool(true);
private SettingsSlider<float> 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<float>
{
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);
}
}
}

View File

@ -68,7 +68,10 @@ namespace osu.Game.Overlays.Settings.Sections
break; break;
// whitelist the handlers which should be displayed to avoid any weird cases of users touching settings they shouldn't. // 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 _: case MidiHandler _:
section = new HandlerSection(handler); section = new HandlerSection(handler);
break; break;

View File

@ -100,9 +100,23 @@ namespace osu.Game.Overlays.Settings
public IEnumerable<string> Keywords { get; set; } public IEnumerable<string> 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; } public bool FilteringActive { get; set; }

View File

@ -21,8 +21,6 @@ namespace osu.Game.Overlays.Settings
protected FillFlowContainer FlowContent; protected FillFlowContainer FlowContent;
protected override Container<Drawable> Content => FlowContent; protected override Container<Drawable> Content => FlowContent;
public override bool IsPresent => base.IsPresent && MatchingFilter;
private IBindable<SettingsSection> selectedSection; private IBindable<SettingsSection> selectedSection;
private Box dim; private Box dim;
@ -40,7 +38,23 @@ namespace osu.Game.Overlays.Settings
private const int header_size = 24; private const int header_size = 24;
private const int border_size = 4; 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; } public bool FilteringActive { get; set; }

View File

@ -196,11 +196,8 @@ namespace osu.Game.Screens.Menu
if (State == ButtonSystemState.Initial) if (State == ButtonSystemState.Initial)
{ {
if (buttonsTopLevel.Any(b => e.Key == b.TriggerKey)) logo?.TriggerClick();
{ return true;
logo?.TriggerClick();
return true;
}
} }
return base.OnKeyDown(e); return base.OnKeyDown(e);

View File

@ -244,7 +244,7 @@ namespace osu.Game.Screens.Select.Carousel
} }
if (hideRequested != null) 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(); return items.ToArray();
} }

View File

@ -50,6 +50,12 @@ namespace osu.Game.Screens.Select
public FilterControl FilterControl { get; private set; } public FilterControl FilterControl { get; private set; }
/// <summary>
/// Whether this song select instance should take control of the global track,
/// applying looping and preview offsets.
/// </summary>
protected virtual bool ControlGlobalMusic => true;
protected virtual bool ShowFooter => true; protected virtual bool ShowFooter => true;
protected virtual bool DisplayStableImportPrompt => legacyImportManager?.SupportsImportFromStable == true; protected virtual bool DisplayStableImportPrompt => legacyImportManager?.SupportsImportFromStable == true;
@ -604,15 +610,18 @@ namespace osu.Game.Screens.Select
BeatmapDetails.Refresh(); BeatmapDetails.Refresh();
beginLooping(); beginLooping();
music.ResetTrackAdjustments();
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending) if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
{ {
updateComponentFromBeatmap(Beatmap.Value); updateComponentFromBeatmap(Beatmap.Value);
// restart playback on returning to song select, regardless. if (ControlGlobalMusic)
// not sure this should be a permanent thing (we may want to leave a user pause paused even on returning) {
music.Play(requestedByUser: true); // 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); this.FadeIn(250);
@ -663,6 +672,9 @@ namespace osu.Game.Screens.Select
private void beginLooping() private void beginLooping()
{ {
if (!ControlGlobalMusic)
return;
Debug.Assert(!isHandlingLooping); Debug.Assert(!isHandlingLooping);
isHandlingLooping = true; isHandlingLooping = true;
@ -733,6 +745,9 @@ namespace osu.Game.Screens.Select
/// </summary> /// </summary>
private void ensurePlayingSelected() private void ensurePlayingSelected()
{ {
if (!ControlGlobalMusic)
return;
ITrack track = music.CurrentTrack; ITrack track = music.CurrentTrack;
bool isNewTrack = !lastTrack.TryGetTarget(out var last) || last != track; bool isNewTrack = !lastTrack.TryGetTarget(out var last) || last != track;

View File

@ -35,7 +35,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Realm" Version="10.10.0" /> <PackageReference Include="Realm" Version="10.10.0" />
<PackageReference Include="ppy.osu.Framework" Version="2022.421.0" /> <PackageReference Include="ppy.osu.Framework" Version="2022.423.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.422.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2022.422.0" />
<PackageReference Include="Sentry" Version="3.14.1" /> <PackageReference Include="Sentry" Version="3.14.1" />
<PackageReference Include="SharpCompress" Version="0.30.1" /> <PackageReference Include="SharpCompress" Version="0.30.1" />

View File

@ -61,7 +61,7 @@
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.421.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2022.423.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.422.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2022.422.0" />
</ItemGroup> </ItemGroup>
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) --> <!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
@ -84,7 +84,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="ppy.osu.Framework" Version="2022.421.0" /> <PackageReference Include="ppy.osu.Framework" Version="2022.423.0" />
<PackageReference Include="SharpCompress" Version="0.30.1" /> <PackageReference Include="SharpCompress" Version="0.30.1" />
<PackageReference Include="NUnit" Version="3.13.2" /> <PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />