1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-06 06:57:39 +08:00

Merge branch 'first-run-show-get-started' into first-run-behaviour-screen

This commit is contained in:
Dean Herbert 2022-04-26 16:10:16 +09:00
commit 942f4e8ac2
51 changed files with 1009 additions and 410 deletions

View File

@ -52,7 +52,7 @@
</ItemGroup>
<ItemGroup>
<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 Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->

View File

@ -90,6 +90,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
return new LegacyHitExplosion();
return null;
default:
throw new UnsupportedSkinComponentException(component);
}
}

View File

@ -116,9 +116,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
case ManiaSkinComponents.StageForeground:
return new LegacyStageForeground();
}
break;
default:
throw new UnsupportedSkinComponentException(component);
}
}
return base.GetDrawableComponent(component);

View File

@ -70,7 +70,9 @@ namespace osu.Game.Rulesets.Osu.Tests
var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo());
tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1";
Child = new SkinProvidingContainer(tintingSkin)
var provider = Ruleset.Value.CreateInstance().CreateLegacySkinProvider(tintingSkin, Beatmap.Value.Beatmap);
Child = new SkinProvidingContainer(provider)
{
RelativeSizeAxes = Axes.Both,
Child = dho = new DrawableSlider(prepareObject(new Slider

View File

@ -35,6 +35,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
case OsuSkinComponents.FollowPoint:
return this.GetAnimation(component.LookupName, true, true, true, startAtCurrentTime: false);
case OsuSkinComponents.SliderScorePoint:
return this.GetAnimation(component.LookupName, false, false);
case OsuSkinComponents.SliderFollowCircle:
var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true);
if (followCircle != null)
@ -123,6 +126,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
case OsuSkinComponents.ApproachCircle:
return new LegacyApproachCircle();
default:
throw new UnsupportedSkinComponentException(component);
}
}

View File

@ -57,6 +57,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
case TaikoSkinComponents.DrumRollTick:
return this.GetAnimation("sliderscorepoint", false, false);
case TaikoSkinComponents.Swell:
// todo: support taiko legacy swell (https://github.com/ppy/osu/issues/13601).
return null;
case TaikoSkinComponents.HitTarget:
if (GetTexture("taikobigcircle") != null)
return new TaikoLegacyHitTarget();
@ -119,6 +123,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
case TaikoSkinComponents.Mascot:
return new DrawableTaikoMascot();
default:
throw new UnsupportedSkinComponentException(component);
}
}

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

@ -59,8 +59,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
DismissAnyNotifications();
return player != null;
});
@ -73,12 +72,6 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("key counter did increase", () => keyCounter.CountPresses == 1);
}
private void clickMouseInCentre()
{
InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre);
InputManager.Click(MouseButton.Left);
}
private KeyBindingsSubsection osuBindingSubsection => keyBindingPanel
.ChildrenOfType<VariantBindingsSubsection>()
.FirstOrDefault(s => s.Ruleset.ShortName == "osu");

View File

@ -89,18 +89,11 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
DismissAnyNotifications();
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
});
AddUntilStep("wait for play time active", () => !player.IsBreakTime.Value);
}
private void clickMouseInCentre()
{
InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre);
InputManager.Click(MouseButton.Left);
}
}
}

View File

@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
@ -16,7 +15,6 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Toolbar;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
@ -24,12 +22,10 @@ using osu.Game.Scoring;
using osu.Game.Screens.Menu;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Screens.Select.Options;
using osu.Game.Skinning.Editor;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
using osuTK.Input;
@ -71,73 +67,6 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
}
[Test]
public void TestEditComponentDuringGameplay()
{
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
SkinEditor skinEditor = null;
AddStep("open skin editor", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.PressKey(Key.ShiftLeft);
InputManager.Key(Key.S);
InputManager.ReleaseKey(Key.ControlLeft);
InputManager.ReleaseKey(Key.ShiftLeft);
});
AddUntilStep("get skin editor", () => (skinEditor = Game.ChildrenOfType<SkinEditor>().FirstOrDefault()) != null);
AddStep("Click gameplay scene button", () =>
{
skinEditor.ChildrenOfType<SkinEditorSceneLibrary.SceneButton>().First(b => b.Text == "Gameplay").TriggerClick();
});
AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
return Game.ScreenStack.CurrentScreen is Player;
});
BarHitErrorMeter hitErrorMeter = null;
AddUntilStep("select bar hit error blueprint", () =>
{
var blueprint = skinEditor.ChildrenOfType<SkinBlueprint>().FirstOrDefault(b => b.Item is BarHitErrorMeter);
if (blueprint == null)
return false;
hitErrorMeter = (BarHitErrorMeter)blueprint.Item;
skinEditor.SelectedComponents.Clear();
skinEditor.SelectedComponents.Add(blueprint.Item);
return true;
});
AddAssert("value is default", () => hitErrorMeter.JudgementLineThickness.IsDefault);
AddStep("hover first slider", () =>
{
InputManager.MoveMouseTo(
skinEditor.ChildrenOfType<SkinSettingsToolbox>().First()
.ChildrenOfType<SettingsSlider<float>>().First()
.ChildrenOfType<SliderBar<float>>().First()
);
});
AddStep("adjust slider via keyboard", () => InputManager.Key(Key.Left));
AddAssert("value is less than default", () => hitErrorMeter.JudgementLineThickness.Value < hitErrorMeter.JudgementLineThickness.Default);
}
[Test]
public void TestRetryCountIncrements()
{
@ -155,8 +84,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
DismissAnyNotifications();
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
});
@ -280,8 +208,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
DismissAnyNotifications();
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
});
@ -596,8 +523,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
DismissAnyNotifications();
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
});
@ -607,12 +533,6 @@ namespace osu.Game.Tests.Visual.Navigation
return () => player;
}
private void clickMouseInCentre()
{
InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre);
InputManager.Click(MouseButton.Left);
}
private void pushEscape() =>
AddStep("Press escape", () => InputManager.Key(Key.Escape));

View File

@ -0,0 +1,136 @@
// 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.Extensions;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
using osu.Game.Skinning.Editor;
using osu.Game.Tests.Beatmaps.IO;
using osuTK.Input;
using static osu.Game.Tests.Visual.Navigation.TestSceneScreenNavigation;
namespace osu.Game.Tests.Visual.Navigation
{
public class TestSceneSkinEditorSceneLibrary : OsuGameTestScene
{
private SkinEditor skinEditor;
public override void SetUpSteps()
{
base.SetUpSteps();
Screens.Select.SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
AddStep("open skin editor", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.PressKey(Key.ShiftLeft);
InputManager.Key(Key.S);
InputManager.ReleaseKey(Key.ControlLeft);
InputManager.ReleaseKey(Key.ShiftLeft);
});
AddUntilStep("get skin editor", () => (skinEditor = Game.ChildrenOfType<SkinEditor>().FirstOrDefault()) != null);
}
[Test]
public void TestEditComponentDuringGameplay()
{
switchToGameplayScene();
BarHitErrorMeter hitErrorMeter = null;
AddUntilStep("select bar hit error blueprint", () =>
{
var blueprint = skinEditor.ChildrenOfType<SkinBlueprint>().FirstOrDefault(b => b.Item is BarHitErrorMeter);
if (blueprint == null)
return false;
hitErrorMeter = (BarHitErrorMeter)blueprint.Item;
skinEditor.SelectedComponents.Clear();
skinEditor.SelectedComponents.Add(blueprint.Item);
return true;
});
AddAssert("value is default", () => hitErrorMeter.JudgementLineThickness.IsDefault);
AddStep("hover first slider", () =>
{
InputManager.MoveMouseTo(
skinEditor.ChildrenOfType<SkinSettingsToolbox>().First()
.ChildrenOfType<SettingsSlider<float>>().First()
.ChildrenOfType<SliderBar<float>>().First()
);
});
AddStep("adjust slider via keyboard", () => InputManager.Key(Key.Left));
AddAssert("value is less than default", () => hitErrorMeter.JudgementLineThickness.Value < hitErrorMeter.JudgementLineThickness.Default);
}
[Test]
public void TestAutoplayCompatibleModsRetainedOnEnteringGameplay()
{
AddStep("select DT", () => Game.SelectedMods.Value = new Mod[] { new OsuModDoubleTime() });
switchToGameplayScene();
AddAssert("DT still selected", () => ((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Single() is OsuModDoubleTime);
}
[Test]
public void TestAutoplayIncompatibleModsRemovedOnEnteringGameplay()
{
AddStep("select no fail and spun out", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModSpunOut() });
switchToGameplayScene();
AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any());
}
[Test]
public void TestDuplicateAutoplayModRemovedOnEnteringGameplay()
{
AddStep("select autoplay", () => Game.SelectedMods.Value = new Mod[] { new OsuModAutoplay() });
switchToGameplayScene();
AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any());
}
[Test]
public void TestCinemaModRemovedOnEnteringGameplay()
{
AddStep("select cinema", () => Game.SelectedMods.Value = new Mod[] { new OsuModCinema() });
switchToGameplayScene();
AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any());
}
private void switchToGameplayScene()
{
AddStep("Click gameplay scene button", () => skinEditor.ChildrenOfType<SkinEditorSceneLibrary.SceneButton>().First(b => b.Text == "Gameplay").TriggerClick());
AddUntilStep("wait for player", () =>
{
DismissAnyNotifications();
return Game.ScreenStack.CurrentScreen is Player;
});
}
}
}

View File

@ -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<SearchTextBox>().First().Current.Value = "scaling";
});
if (beforeLoad)
AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType<SearchTextBox>().First().Current.Value = "scaling");
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
.ChildrenOfType<SettingsSection>().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<IFilterable>().Any());
AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType<SearchTextBox>().First().Current.Value = "scaling");
}
[Test]

View File

@ -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;

View File

@ -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));
@ -165,7 +166,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);
});

View File

@ -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<Mod>()
: 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<Mod>());
clickPanel();
AddAssert("panel active", () => panel.Active.Value);

View File

@ -1,6 +1,7 @@
// 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;
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<ModPanel>().Single(panel => panel.Mod.GetType() == modType);
}
}

View File

@ -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<TestSection> container;
private SectionsContainer<TestSection> 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<TestSection>
AddStep("setup container", () =>
{
RelativeSizeAxes = Axes.Y,
Width = 300,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
FixedHeader = new Box
container = new SectionsContainer<TestSection>
{
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<UserTrackingScrollContainer>().First().Current;
float sectionTop = container.Children[scrollIndex].BoundingBox.Top;
return scrollPosition < sectionTop;
var scrollContainer = container.ChildrenOfType<UserTrackingScrollContainer>().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<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 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,
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),
}
};
}
}
}

View File

@ -0,0 +1,102 @@
// 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.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<ShearedOverlayHeader>().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,
}
}
};
}
}
}
}

View File

@ -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();
@ -267,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;

View File

@ -0,0 +1,21 @@
// 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.
#nullable enable
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
namespace osu.Game.Graphics
{
/// <summary>
/// A simple container which blocks input events from travelling through it.
/// </summary>
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;
}
}

View File

@ -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<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

View File

@ -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<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)
{

View File

@ -66,7 +66,7 @@ namespace osu.Game.Graphics.UserInterface
},
Children = new Drawable[]
{
underlayContainer = new Container
underlayContainer = new InputBlockingContainer
{
RelativeSizeAxes = Axes.X,
Height = HEIGHT,

View File

@ -14,6 +14,11 @@ namespace osu.Game.Localisation
/// </summary>
public static LocalisableString Back => new TranslatableString(getKey(@"back"), @"Back");
/// <summary>
/// "Next"
/// </summary>
public static LocalisableString Next => new TranslatableString(getKey(@"next"), @"Next");
/// <summary>
/// "Finish"
/// </summary>

View File

@ -49,11 +49,6 @@ osu! is a very configurable game, and diving straight into the settings can some
/// </summary>
public static LocalisableString UIScaleDescription => new TranslatableString(getKey(@"ui_scale_description"), @"The size of the osu! user interface can be adjusted to your liking.");
/// <summary>
/// "Next ({0})"
/// </summary>
public static LocalisableString Next(LocalisableString nextStepDescription) => new TranslatableString(getKey(@"next"), @"Next ({0})", nextStepDescription);
/// <summary>
/// "Behaviour"
/// </summary>

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 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)

View File

@ -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<bool> showFirstRunSetup = new Bindable<bool>();
private int? currentStepIndex;
private const float scale_when_hidden = 0.9f;
/// <summary>
/// The currently displayed screen, if any.
/// </summary>
@ -77,149 +67,89 @@ namespace osu.Game.Overlays
private Bindable<OverlayActivation>? overlayActivationMode;
public FirstRunSetupOverlay()
{
RelativeSizeAxes = Axes.Both;
}
private Container content = null!;
[BackgroundDependencyLoader]
private void load()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Header.Title = FirstRunSetupOverlayStrings.FirstRunSetupTitle;
Header.Description = FirstRunSetupOverlayStrings.FirstRunSetupDescription;
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.FirstRunSetupTitle,
Font = OsuFont.Default.With(size: 32),
Colour = colourProvider.Content1,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
new OsuTextFlowContainer
{
Text = FirstRunSetupOverlayStrings.FirstRunSetupDescription,
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 = 300,
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()
@ -228,7 +158,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<GlobalAction> e)
@ -281,10 +212,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();
@ -292,6 +221,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.
@ -317,11 +250,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()
@ -363,7 +291,8 @@ namespace osu.Game.Overlays
}
else
{
showFirstRunSetup.Value = false;
// TODO: uncomment when happy with the whole flow.
// showFirstRunSetup.Value = false;
currentStepIndex = null;
Hide();
}
@ -376,11 +305,24 @@ namespace osu.Game.Overlays
BackButton.Enabled.Value = currentStepIndex > 0;
NextButton.Enabled.Value = currentStepIndex != null;
if (currentStepIndex != null)
if (currentStepIndex == null)
return;
bool isFirstStep = currentStepIndex == 0;
bool isLastStep = currentStepIndex == steps.Length - 1;
if (isFirstStep)
{
NextButton.Text = currentStepIndex + 1 < steps.Length
? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description)
: CommonStrings.Finish;
BackButton.Text = CommonStrings.Back;
NextButton.Text = FirstRunSetupOverlayStrings.GetStarted;
}
else
{
BackButton.Text = new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Back, steps[currentStepIndex.Value - 1].Description);
NextButton.Text = isLastStep
? CommonStrings.Finish
: new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Next, steps[currentStepIndex.Value + 1].Description);
}
}

View File

@ -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,
},

View File

@ -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,

View File

@ -1,15 +1,12 @@
// 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;
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

View File

@ -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);
}

View File

@ -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<IReadOnlyList<Mod>> 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<Mod> ComputeNewModsFromSelection(IEnumerable<Mod> addedMods, IEnumerable<Mod> removedMods)
=> columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray();
protected override void PopIn()
{
const double fade_in_duration = 400;

View File

@ -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;

View File

@ -1,8 +1,11 @@
// 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.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<Mod> ComputeNewModsFromSelection(IEnumerable<Mod> addedMods, IEnumerable<Mod> removedMods)
{
IEnumerable<Mod> 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)

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;
// 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;

View File

@ -116,9 +116,23 @@ namespace osu.Game.Overlays.Settings
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; }

View File

@ -22,8 +22,6 @@ namespace osu.Game.Overlays.Settings
protected FillFlowContainer FlowContent;
protected override Container<Drawable> Content => FlowContent;
public override bool IsPresent => base.IsPresent && MatchingFilter;
private IBindable<SettingsSection> selectedSection;
private Box dim;
@ -41,7 +39,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; }

View File

@ -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);

View File

@ -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();
}

View File

@ -50,6 +50,12 @@ namespace osu.Game.Screens.Select
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 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
/// </summary>
private void ensurePlayingSelected()
{
if (!ControlGlobalMusic)
return;
ITrack track = music.CurrentTrack;
bool isNewTrack = !lastTrack.TryGetTarget(out var last) || last != track;

View File

@ -155,7 +155,7 @@ namespace osu.Game.Skinning
return skinnableTargetWrapper;
}
break;
return null;
}
switch (component.LookupName)

View File

@ -1,6 +1,8 @@
// 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.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -18,6 +20,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Utils;
using osuTK;
namespace osu.Game.Skinning.Editor
@ -34,6 +37,9 @@ namespace osu.Game.Skinning.Editor
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
[Resolved]
private Bindable<IReadOnlyList<Mod>> mods { get; set; }
public SkinEditorSceneLibrary()
{
Height = BUTTON_HEIGHT + padding * 2;
@ -95,6 +101,10 @@ namespace osu.Game.Skinning.Editor
return;
var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod();
if (!ModUtils.CheckCompatibleSet(mods.Value.Append(replayGeneratingMod), out var invalid))
mods.Value = mods.Value.Except(invalid).ToArray();
if (replayGeneratingMod != null)
screen.Push(new PlayerLoader(() => new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods))));
}, new[] { typeof(Player), typeof(SongSelect) })

View File

@ -390,10 +390,14 @@ namespace osu.Game.Skinning
return new LegacyJudgementPieceOld(resultComponent.Component, createDrawable);
}
break;
}
return null;
return this.GetAnimation(component.LookupName, false, false);
case SkinnableSprite.SpriteComponent sprite:
return this.GetAnimation(sprite.LookupName, false, false);
default:
throw new UnsupportedSkinComponentException(component);
}
}
private Texture? getParticleTexture(HitResult result)

View File

@ -65,7 +65,7 @@ namespace osu.Game.Skinning
public bool UsesFixedAnchor { get; set; }
private class SpriteComponent : ISkinComponent
internal class SpriteComponent : ISkinComponent
{
public string LookupName { get; set; }

View File

@ -0,0 +1,15 @@
// 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;
namespace osu.Game.Skinning
{
public class UnsupportedSkinComponentException : Exception
{
public UnsupportedSkinComponentException(ISkinComponent component)
: base($@"Unsupported component type: {component.GetType()} (lookup: ""{component.LookupName}"").")
{
}
}
}

View File

@ -23,6 +23,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osuTK.Graphics;
using IntroSequence = osu.Game.Configuration.IntroSequence;
@ -106,6 +107,11 @@ namespace osu.Game.Tests.Visual
protected void ConfirmAtMainMenu() => AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
/// <summary>
/// Dismisses any notifications pushed which block from interacting with the game (or block screens from loading, e.g. <see cref="Player"/>).
/// </summary>
protected void DismissAnyNotifications() => Game.Notifications.State.Value = Visibility.Hidden;
public class TestOsuGame : OsuGame
{
public new const float SIDE_OVERLAY_OFFSET_RATIO = OsuGame.SIDE_OVERLAY_OFFSET_RATIO;

View File

@ -74,11 +74,15 @@ namespace osu.Game.Tests.Visual
createdDrawables.Add(created);
SkinProvidingContainer mainProvider;
Container childContainer;
OutlineBox outlineBox;
SkinProvidingContainer skinProvider;
ISkin provider = skin;
if (provider is LegacySkin legacyProvider)
provider = Ruleset.Value.CreateInstance().CreateLegacySkinProvider(legacyProvider, beatmap);
var children = new Container
{
RelativeSizeAxes = Axes.Both,
@ -107,12 +111,10 @@ namespace osu.Game.Tests.Visual
Children = new Drawable[]
{
outlineBox = new OutlineBox(),
(mainProvider = new SkinProvidingContainer(skin)).WithChild(
skinProvider = new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider, beatmap))
{
Child = created,
}
)
skinProvider = new SkinProvidingContainer(provider)
{
Child = created,
}
}
},
}
@ -130,7 +132,7 @@ namespace osu.Game.Tests.Visual
{
bool autoSize = created.RelativeSizeAxes == Axes.None;
foreach (var c in new[] { mainProvider, childContainer, skinProvider })
foreach (var c in new[] { childContainer, skinProvider })
{
c.RelativeSizeAxes = Axes.None;
c.AutoSizeAxes = Axes.None;

View File

@ -35,7 +35,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<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="Sentry" Version="3.14.1" />
<PackageReference Include="SharpCompress" Version="0.30.1" />

View File

@ -61,7 +61,7 @@
<Reference Include="System.Net.Http" />
</ItemGroup>
<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" />
</ItemGroup>
<!-- 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.Core" Version="5.0.14" />
<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="NUnit" Version="3.13.2" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />