From 7258a097489655632d89def6cdbac09cd54f3d3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 18:04:07 +0900 Subject: [PATCH 01/63] Allow `SettingsSection`s to exit without a parent `SettingsPanel` --- osu.Game/Overlays/Settings/SettingsSection.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 28c42a0e47..dc3af9104f 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -44,7 +45,7 @@ namespace osu.Game.Overlays.Settings public bool FilteringActive { get; set; } - [Resolved] + [Resolved(canBeNull: true)] private SettingsPanel settingsPanel { get; set; } protected SettingsSection() @@ -117,7 +118,7 @@ namespace osu.Game.Overlays.Settings }, }); - selectedSection = settingsPanel.CurrentSection.GetBoundCopy(); + selectedSection = settingsPanel?.CurrentSection.GetBoundCopy() ?? new Bindable(this); selectedSection.BindValueChanged(_ => updateContentFade(), true); } @@ -138,7 +139,10 @@ namespace osu.Game.Overlays.Settings protected override bool OnClick(ClickEvent e) { if (!isCurrentSection) + { + Debug.Assert(settingsPanel != null); settingsPanel.SectionsContainer.ScrollTo(this); + } return base.OnClick(e); } From 2a043aa6de7ae64990a6069e7cb0402d4e8f06fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 18:04:43 +0900 Subject: [PATCH 02/63] Add a method of finding and applying settings with classic default value --- osu.Game/Overlays/Settings/ISettingsItem.cs | 6 ++++ osu.Game/Overlays/Settings/SettingsItem.cs | 36 ++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/ISettingsItem.cs b/osu.Game/Overlays/Settings/ISettingsItem.cs index e7afa48502..e6b1e68741 100644 --- a/osu.Game/Overlays/Settings/ISettingsItem.cs +++ b/osu.Game/Overlays/Settings/ISettingsItem.cs @@ -9,5 +9,11 @@ namespace osu.Game.Overlays.Settings public interface ISettingsItem : IDrawable, IDisposable { event Action SettingChanged; + + /// + /// Apply the default values of a setting item, if the setting item specifies a "classic" default via . + /// + /// Whether to apply the classic value. If false, the standard default is applied. + void ApplyClassicDefault(bool useClassicDefault); } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 1c5668479f..845a7f5cdf 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -30,6 +30,8 @@ namespace osu.Game.Overlays.Settings /// public object SettingSourceObject { get; internal set; } + public const string CLASSIC_DEFAULT_SEARCH_TERM = @"has-classic-default"; + private IHasCurrentValue controlWithCurrent => Control as IHasCurrentValue; protected override Container Content => FlowContent; @@ -96,7 +98,23 @@ namespace osu.Game.Overlays.Settings set => controlWithCurrent.Current = value; } - public virtual IEnumerable FilterTerms => Keywords == null ? new[] { LabelText.ToString() } : new List(Keywords) { LabelText.ToString() }.ToArray(); + public virtual IEnumerable FilterTerms + { + get + { + var keywords = new List(Keywords ?? Array.Empty()) + { + LabelText.ToString() + }; + + if (GetClassicDefault != null) + { + keywords.Add(CLASSIC_DEFAULT_SEARCH_TERM); + } + + return keywords; + } + } public IEnumerable Keywords { get; set; } @@ -108,6 +126,22 @@ namespace osu.Game.Overlays.Settings public event Action SettingChanged; + /// + /// An action which when invoked will apply a classic default value to this setting. + /// + public Func GetClassicDefault { get; set; } + + public void ApplyClassicDefault(bool useClassicDefault) + { + if (GetClassicDefault != null) + { + if (useClassicDefault) + Current.Value = GetClassicDefault(); + else + Current.SetDefault(); + } + } + protected SettingsItem() { RelativeSizeAxes = Axes.X; From e0b9ab022de2f610b294a53c065ae07d4e81f999 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 18:05:24 +0900 Subject: [PATCH 03/63] Add classic default values against setting controls --- osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs | 1 + osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 1 + .../Overlays/Settings/Sections/UserInterface/GeneralSettings.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index a4c0381d16..33db74c530 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Osu.UI }, new SettingsCheckbox { + GetClassicDefault = () => false, LabelText = "Snaking out sliders", Current = config.GetBindable(OsuRulesetSetting.SnakingOutSliders) }, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index d4e4fd571d..3ca677ef7b 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -21,6 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { new SettingsEnumDropdown { + GetClassicDefault = () => ScoringMode.Classic, LabelText = GameplaySettingsStrings.ScoreDisplayMode, Current = config.GetBindable(OsuSetting.ScoreDisplayMode), Keywords = new[] { "scoring" } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 6e1558f7d7..06f2c9794c 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -37,6 +37,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface }, new SettingsSlider { + GetClassicDefault = () => 0, LabelText = UserInterfaceStrings.HoldToConfirmActivationTime, Current = config.GetBindable(OsuSetting.UIHoldActivationDelay), Keywords = new[] { @"delay" }, From 1caee3876802ca5ac8aa81c9bb932f8a558192ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Apr 2022 18:05:52 +0900 Subject: [PATCH 04/63] Add first-run "behaviour" screen to allow users a choice of more familiar UX --- .../TestSceneFirstRunScreenBehaviour.cs | 19 ++++ .../FirstRunSetupOverlayStrings.cs | 28 ++++- .../Overlays/FirstRunSetup/ScreenBehaviour.cs | 104 ++++++++++++++++++ osu.Game/Overlays/FirstRunSetupOverlay.cs | 1 + 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs create mode 100644 osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs new file mode 100644 index 0000000000..d7f0c5ba03 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Screens; +using osu.Game.Overlays.FirstRunSetup; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneFirstRunScreenBehaviour : OsuManualInputManagerTestScene + { + public TestSceneFirstRunScreenBehaviour() + { + AddStep("load screen", () => + { + Child = new ScreenStack(new ScreenBehaviour()); + }); + } + } +} diff --git a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs index 001de93c16..15b8d0ba67 100644 --- a/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupOverlayStrings.cs @@ -17,7 +17,8 @@ namespace osu.Game.Localisation /// /// "Click to resume first-run setup at any point" /// - public static LocalisableString ClickToResumeFirstRunSetupAtAnyPoint => new TranslatableString(getKey(@"click_to_resume_first_run_setup_at_any_point"), @"Click to resume first-run setup at any point"); + public static LocalisableString ClickToResumeFirstRunSetupAtAnyPoint => + new TranslatableString(getKey(@"click_to_resume_first_run_setup_at_any_point"), @"Click to resume first-run setup at any point"); /// /// "First-run setup" @@ -53,6 +54,31 @@ osu! is a very configurable game, and diving straight into the settings can some /// public static LocalisableString Next(LocalisableString nextStepDescription) => new TranslatableString(getKey(@"next"), @"Next ({0})", nextStepDescription); + /// + /// "Behaviour" + /// + public static LocalisableString Behaviour => new TranslatableString(getKey(@"behaviour"), @"Behaviour"); + + /// + /// "Some new defaults for game behaviours have been implemented, with the aim of improving the game experience and making it more accessible to everyone. + /// + /// We recommend you give the new defaults a try, but if you'd like to have things feel more like classic versions of osu!, you can easily apply some sane defaults below." + /// + public static LocalisableString BehaviourDescription => new TranslatableString(getKey(@"behaviour_description"), + @"Some new defaults for game behaviours have been implemented, with the aim of improving the game experience and making it more accessible to everyone. + +We recommend you give the new defaults a try, but if you'd like to have things feel more like classic versions of osu!, you can easily apply some sane defaults below."); + + /// + /// "New defaults" + /// + public static LocalisableString NewDefaults => new TranslatableString(getKey(@"new_defaults"), @"New defaults"); + + /// + /// "Classic defaults" + /// + public static LocalisableString ClassicDefaults => new TranslatableString(getKey(@"classic_defaults"), @"Classic defaults"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs new file mode 100644 index 0000000000..61177c821b --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs @@ -0,0 +1,104 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; +using osu.Game.Overlays.Settings; +using osu.Game.Overlays.Settings.Sections; + +namespace osu.Game.Overlays.FirstRunSetup +{ + public class ScreenBehaviour : FirstRunSetupScreen + { + private SearchContainer searchContainer; + + [BackgroundDependencyLoader] + private void load() + { + Content.Children = new Drawable[] + { + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24)) + { + Text = FirstRunSetupOverlayStrings.BehaviourDescription, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 10), + new Dimension(), + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new[] + { + new TriangleButton + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + Text = FirstRunSetupOverlayStrings.NewDefaults, + RelativeSizeAxes = Axes.X, + Action = applyStandard, + }, + Empty(), + new DangerousTriangleButton + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Text = FirstRunSetupOverlayStrings.ClassicDefaults, + RelativeSizeAxes = Axes.X, + Action = applyClassic + } + }, + }, + }, + searchContainer = new SearchContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new SettingsSection[] + { + new GeneralSection(), + new SkinSection(), + new UserInterfaceSection(), + new GameplaySection(), + new RulesetSection(), + new AudioSection(), + new GraphicsSection(), + new OnlineSection(), + new MaintenanceSection(), + new DebugSection(), + }, + SearchTerm = SettingsItem.CLASSIC_DEFAULT_SEARCH_TERM, + } + }; + } + + private void applyClassic() + { + foreach (var i in searchContainer.ChildrenOfType()) + i.ApplyClassicDefault(true); + } + + private void applyStandard() + { + foreach (var i in searchContainer.ChildrenOfType()) + i.ApplyClassicDefault(false); + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index a12fec4507..26b1eb2d45 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -70,6 +70,7 @@ namespace osu.Game.Overlays { new FirstRunStep(typeof(ScreenWelcome), FirstRunSetupOverlayStrings.WelcomeTitle), new FirstRunStep(typeof(ScreenUIScale), GraphicsSettingsStrings.UIScaling), + new FirstRunStep(typeof(ScreenBehaviour), FirstRunSetupOverlayStrings.Behaviour), }; private Container stackContainer = null!; From d97dc22e79021f94249a99cae827d1d25003bac7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Apr 2022 21:23:41 +0900 Subject: [PATCH 05/63] Add missing dependencies for behaviour screen test --- .../Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs index d7f0c5ba03..9747b5cc53 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBehaviour.cs @@ -1,13 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Screens; +using osu.Game.Overlays; using osu.Game.Overlays.FirstRunSetup; namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneFirstRunScreenBehaviour : OsuManualInputManagerTestScene { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + public TestSceneFirstRunScreenBehaviour() { AddStep("load screen", () => From 5d0842ac447f2912872e6e37d029305baf69b0b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Apr 2022 21:53:47 +0900 Subject: [PATCH 06/63] Add a few more classic defaults to missed settings --- osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs | 1 + osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs | 1 + .../Settings/Sections/UserInterface/SongSelectSettings.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs index 5029c6a617..6978c77aad 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { + GetClassicDefault = () => false, LabelText = GameplaySettingsStrings.AlwaysPlayFirstComboBreak, Current = config.GetBindable(OsuSetting.AlwaysPlayFirstComboBreak) } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs index ba9779d650..285cdfa3f8 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs @@ -30,6 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { + GetClassicDefault = () => false, LabelText = GameplaySettingsStrings.ShowHealthDisplayWhenCantFail, Current = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail), Keywords = new[] { "hp", "bar" } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs index 6290046987..213234db4f 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs @@ -32,6 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { new SettingsCheckbox { + GetClassicDefault = () => true, LabelText = UserInterfaceStrings.RightMouseScroll, Current = config.GetBindable(OsuSetting.SongSelectRightMouseScroll), }, From ebc8429495c1cde762d2273ed2a7c2fb37476f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Apr 2022 19:13:19 +0200 Subject: [PATCH 07/63] Dim offscreen columns & scroll to them if clicked --- osu.Game/Overlays/Mods/ModColumn.cs | 3 + osu.Game/Overlays/Mods/ModSelectScreen.cs | 138 ++++++++++++++++++---- 2 files changed, 115 insertions(+), 26 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 1157c0c0c6..fed2f9aff6 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -53,6 +53,9 @@ namespace osu.Game.Overlays.Mods } public Bindable> SelectedMods = new Bindable>(Array.Empty()); + public Bindable Active = new BindableBool(true); + + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => base.ReceivePositionalInputAtSubTree(screenSpacePos) && Active.Value; protected virtual ModPanel CreateModPanel(Mod mod) => new ModPanel(mod); diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 8a83071109..ca3609aa75 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -13,7 +13,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Layout; +using osu.Framework.Utils; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; @@ -59,7 +61,8 @@ namespace osu.Game.Overlays.Mods private DifficultyMultiplierDisplay? multiplierDisplay; private ModSettingsArea modSettingsArea = null!; - private FillFlowContainer columnFlow = null!; + private OsuScrollContainer columnScroll = null!; + private ColumnFlowContainer columnFlow = null!; [BackgroundDependencyLoader] private void load() @@ -83,6 +86,9 @@ namespace osu.Game.Overlays.Mods } }); + // initialise the scroll early, as we will want to pass it to its children in the hierarchy initialisation below. + columnScroll = new OsuScrollContainer(Direction.Horizontal); + MainAreaContent.AddRange(new Drawable[] { new Container @@ -95,13 +101,13 @@ namespace osu.Game.Overlays.Mods RelativePositionAxes = Axes.Both, Children = new Drawable[] { - new OsuScrollContainer(Direction.Horizontal) + columnScroll.With(col => { - RelativeSizeAxes = Axes.Both, - Masking = false, - ClampExtension = 100, - ScrollbarOverlapsContent = false, - Child = columnFlow = new ModColumnContainer + col.RelativeSizeAxes = Axes.Both; + col.Masking = false; + col.ClampExtension = 100; + col.ScrollbarOverlapsContent = false; + col.Child = columnFlow = new ColumnFlowContainer { Direction = FillDirection.Horizontal, Shear = new Vector2(SHEAR, 0), @@ -111,14 +117,14 @@ namespace osu.Game.Overlays.Mods Margin = new MarginPadding { Right = 70 }, Children = new[] { - CreateModColumn(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), - CreateModColumn(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), - CreateModColumn(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), - CreateModColumn(ModType.Conversion), - CreateModColumn(ModType.Fun) + createModColumnContent(columnScroll, ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), + createModColumnContent(columnScroll, ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), + createModColumnContent(columnScroll, ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), + createModColumnContent(columnScroll, ModType.Conversion), + createModColumnContent(columnScroll, ModType.Fun) } - } - } + }; + }) } } }); @@ -153,6 +159,13 @@ namespace osu.Game.Overlays.Mods } } + private ColumnDimContainer createModColumnContent(OsuScrollContainer scroll, ModType modType, Key[]? toggleKeys = null) + => new ColumnDimContainer(scroll, CreateModColumn(modType, toggleKeys)) + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y + }; + protected override void LoadComplete() { base.LoadComplete(); @@ -166,7 +179,7 @@ namespace osu.Game.Overlays.Mods updateSelectionFromBindable(); }, true); - foreach (var column in columnFlow) + foreach (var column in columnFlow.Columns) { column.SelectedMods.BindValueChanged(updateBindableFromSelection); } @@ -191,7 +204,7 @@ namespace osu.Game.Overlays.Mods private void updateAvailableMods() { - foreach (var column in columnFlow) + foreach (var column in columnFlow.Columns) column.Filter = isValidMod; } @@ -244,7 +257,7 @@ namespace osu.Game.Overlays.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) + foreach (var column in columnFlow.Columns) column.SelectedMods.Value = SelectedMods.Value.Where(mod => mod.Type == column.ModType).ToArray(); } @@ -265,7 +278,7 @@ namespace osu.Game.Overlays.Mods } protected virtual IReadOnlyList ComputeNewModsFromSelection(IEnumerable addedMods, IEnumerable removedMods) - => columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray(); + => columnFlow.Columns.SelectMany(column => column.SelectedMods.Value).ToArray(); protected override void PopIn() { @@ -280,7 +293,8 @@ namespace osu.Game.Overlays.Mods for (int i = 0; i < columnFlow.Count; i++) { - columnFlow[i].TopLevelContent + columnFlow[i].Column + .TopLevelContent .Delay(i * 30) .MoveToY(0, fade_in_duration, Easing.OutQuint) .FadeIn(fade_in_duration, Easing.OutQuint); @@ -301,27 +315,30 @@ namespace osu.Game.Overlays.Mods { const float distance = 700; - columnFlow[i].TopLevelContent + columnFlow[i].Column + .TopLevelContent .MoveToY(i % 2 == 0 ? -distance : distance, fade_out_duration, Easing.OutQuint) .FadeOut(fade_out_duration, Easing.OutQuint); } } - private class ModColumnContainer : FillFlowContainer + private class ColumnFlowContainer : FillFlowContainer { + public IEnumerable Columns => Children.Select(dimWrapper => dimWrapper.Column); + private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize); - public ModColumnContainer() + public ColumnFlowContainer() { AddLayout(drawSizeLayout); } - public override void Add(ModColumn column) + public override void Add(ColumnDimContainer dimContainer) { - base.Add(column); + base.Add(dimContainer); - Debug.Assert(column != null); - column.Shear = Vector2.Zero; + Debug.Assert(dimContainer != null); + dimContainer.Column.Shear = Vector2.Zero; } protected override void Update() @@ -341,6 +358,75 @@ namespace osu.Game.Overlays.Mods } } + private class ColumnDimContainer : Container + { + public ModColumn Column { get; } + + private readonly ScrollContainer parentScroll; + private readonly Bindable isFullyOnScreen = new BindableBool(true); + + [Resolved] + private OsuColour colours { get; set; } = null!; + + public ColumnDimContainer(ScrollContainer parentScroll, ModColumn column) + { + this.parentScroll = parentScroll; + Child = Column = column; + column.Active.BindTo(isFullyOnScreen); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + isFullyOnScreen.BindValueChanged(_ => updateDim(), true); + FinishTransforms(); + } + + private void updateDim() + { + Colour4 targetColour; + + if (isFullyOnScreen.Value) + targetColour = Colour4.White; + else + targetColour = IsHovered ? colours.GrayC : colours.Gray8; + + this.FadeColour(targetColour, 300, Easing.OutQuint); + } + + protected override void Update() + { + float leftX = DrawPosition.X; + // DrawWidth does not include shear effects, and we want to know the full extents of the columns post-shear, + // so we have to manually compensate. + float rightX = DrawPosition.X + DrawWidth + DrawHeight * SHEAR; + + isFullyOnScreen.Value = Precision.AlmostBigger(leftX, parentScroll.Current) + && Precision.DefinitelyBigger(parentScroll.Current + parentScroll.DrawWidth, rightX); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (!isFullyOnScreen.Value) + parentScroll.ScrollTo(DrawPosition.X - 70); + + return true; + } + + protected override bool OnHover(HoverEvent e) + { + base.OnHover(e); + updateDim(); + return isFullyOnScreen.Value; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + updateDim(); + } + } + private class ClickToReturnContainer : Container { public BindableBool HandleMouse { get; } = new BindableBool(); From 09c08248c4a54bd89582247c3dfee500186b2908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Apr 2022 19:20:50 +0200 Subject: [PATCH 08/63] Fix bounce when scrolling to first/last column --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index ca3609aa75..f12f55a3a7 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -408,7 +408,7 @@ namespace osu.Game.Overlays.Mods protected override bool OnMouseDown(MouseDownEvent e) { if (!isFullyOnScreen.Value) - parentScroll.ScrollTo(DrawPosition.X - 70); + parentScroll.ScrollTo(Math.Clamp(DrawPosition.X - 70, 0, parentScroll.ScrollableExtent)); return true; } From e384e074fb9243521974d6af45051e8567aa6c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Apr 2022 19:27:27 +0200 Subject: [PATCH 09/63] Fix asymmetric margin of column scroll --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index f12f55a3a7..324fc68885 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -114,7 +114,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Spacing = new Vector2(10, 0), - Margin = new MarginPadding { Right = 70 }, + Margin = new MarginPadding { Horizontal = 70 }, Children = new[] { createModColumnContent(columnScroll, ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), From e13d0d02ae7db2a6808c2d1834de6f19261a80d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Apr 2022 19:27:51 +0200 Subject: [PATCH 10/63] Use better way of calculating whether columns are fully on screen --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 324fc68885..11cffe7959 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -396,13 +396,13 @@ namespace osu.Game.Overlays.Mods protected override void Update() { - float leftX = DrawPosition.X; - // DrawWidth does not include shear effects, and we want to know the full extents of the columns post-shear, + // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, // so we have to manually compensate. - float rightX = DrawPosition.X + DrawWidth + DrawHeight * SHEAR; + var topLeft = ToSpaceOfOtherDrawable(new Vector2(-DrawHeight * SHEAR, 0), parentScroll); + var topRight = ToSpaceOfOtherDrawable(new Vector2(DrawWidth, 0), parentScroll); - isFullyOnScreen.Value = Precision.AlmostBigger(leftX, parentScroll.Current) - && Precision.DefinitelyBigger(parentScroll.Current + parentScroll.DrawWidth, rightX); + isFullyOnScreen.Value = Precision.AlmostBigger(topLeft.X, 0) + && Precision.DefinitelyBigger(parentScroll.DrawWidth, topRight.X); } protected override bool OnMouseDown(MouseDownEvent e) From 24a1eb8003c4bcddce3ea0e076d09c15a8bfe6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Apr 2022 23:41:40 +0200 Subject: [PATCH 11/63] Add test coverage for column dimming behaviour --- .../UserInterface/TestSceneModSelectScreen.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index 514538161e..d7ae131372 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -111,6 +111,35 @@ namespace osu.Game.Tests.Visual.UserInterface && SelectedMods.Value.Any(mod => mod.GetType() == typeof(OsuModMirror))); } + [Test] + public void TestDimmedState() + { + createScreen(); + changeRuleset(0); + + AddUntilStep("any column dimmed", () => this.ChildrenOfType().Any(column => !column.Active.Value)); + + ModColumn firstDimmed = null; + ModPanel firstPanel = null; + + AddStep("click first panel on dimmed column", () => + { + firstDimmed = this.ChildrenOfType().First(column => !column.Active.Value); + firstPanel = firstDimmed.ChildrenOfType().First(); + InputManager.MoveMouseTo(firstPanel); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("column undimmed", () => firstDimmed.Active.Value); + AddAssert("panel not selected", () => !firstPanel.Active.Value); + + AddStep("click panel again", () => + { + InputManager.MoveMouseTo(firstPanel); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("panel selected", () => firstPanel.Active.Value); + } + [Test] public void TestCustomisationToggleState() { From b41e273086df007bb12b1521df7e31cd6bc53a04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 15:06:27 +0900 Subject: [PATCH 12/63] Convert function weirdness to property + bool --- .../UI/OsuSettingsSubsection.cs | 2 +- .../Sections/Gameplay/AudioSettings.cs | 2 +- .../Sections/Gameplay/GeneralSettings.cs | 2 +- .../Settings/Sections/Gameplay/HUDSettings.cs | 2 +- .../Sections/UserInterface/GeneralSettings.cs | 2 +- .../UserInterface/SongSelectSettings.cs | 2 +- osu.Game/Overlays/Settings/SettingsItem.cs | 29 +++++++++++-------- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index 33db74c530..a638019e69 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.UI }, new SettingsCheckbox { - GetClassicDefault = () => false, + ClassicDefault = false, LabelText = "Snaking out sliders", Current = config.GetBindable(OsuRulesetSetting.SnakingOutSliders) }, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs index 6978c77aad..e2e00813bd 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { - GetClassicDefault = () => false, + ClassicDefault = false, LabelText = GameplaySettingsStrings.AlwaysPlayFirstComboBreak, Current = config.GetBindable(OsuSetting.AlwaysPlayFirstComboBreak) } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 3ca677ef7b..5231ce1211 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { new SettingsEnumDropdown { - GetClassicDefault = () => ScoringMode.Classic, + ClassicDefault = ScoringMode.Classic, LabelText = GameplaySettingsStrings.ScoreDisplayMode, Current = config.GetBindable(OsuSetting.ScoreDisplayMode), Keywords = new[] { "scoring" } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs index 285cdfa3f8..829977e9b6 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { - GetClassicDefault = () => false, + ClassicDefault = false, LabelText = GameplaySettingsStrings.ShowHealthDisplayWhenCantFail, Current = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail), Keywords = new[] { "hp", "bar" } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 06f2c9794c..7fc049915e 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface }, new SettingsSlider { - GetClassicDefault = () => 0, + ClassicDefault = 0, LabelText = UserInterfaceStrings.HoldToConfirmActivationTime, Current = config.GetBindable(OsuSetting.UIHoldActivationDelay), Keywords = new[] { @"delay" }, diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs index 213234db4f..b91b5c5243 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { new SettingsCheckbox { - GetClassicDefault = () => true, + ClassicDefault = true, LabelText = UserInterfaceStrings.RightMouseScroll, Current = config.GetBindable(OsuSetting.SongSelectRightMouseScroll), }, diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 845a7f5cdf..0b9a553c2c 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -107,10 +107,8 @@ namespace osu.Game.Overlays.Settings LabelText.ToString() }; - if (GetClassicDefault != null) - { + if (hasClassicDefault) keywords.Add(CLASSIC_DEFAULT_SEARCH_TERM); - } return keywords; } @@ -126,20 +124,27 @@ namespace osu.Game.Overlays.Settings public event Action SettingChanged; + private T classicDefault; + private bool hasClassicDefault; + /// - /// An action which when invoked will apply a classic default value to this setting. + /// A "classic" default value for this setting. /// - public Func GetClassicDefault { get; set; } + public T ClassicDefault + { + set + { + classicDefault = value; + hasClassicDefault = true; + } + } public void ApplyClassicDefault(bool useClassicDefault) { - if (GetClassicDefault != null) - { - if (useClassicDefault) - Current.Value = GetClassicDefault(); - else - Current.SetDefault(); - } + if (hasClassicDefault && useClassicDefault) + Current.Value = classicDefault; + else + Current.SetDefault(); } protected SettingsItem() From 71aa7c813e24fe7636b7c938cd2e9ada83038240 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 15:33:39 +0900 Subject: [PATCH 13/63] Fix regressed logic for applying classic defaults --- osu.Game/Overlays/Settings/SettingsItem.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 0b9a553c2c..db9fbfcbb4 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -141,7 +141,10 @@ namespace osu.Game.Overlays.Settings public void ApplyClassicDefault(bool useClassicDefault) { - if (hasClassicDefault && useClassicDefault) + if (!hasClassicDefault) + return; + + if (useClassicDefault) Current.Value = classicDefault; else Current.SetDefault(); From 2c836e2d6312fc0a162f322163385c484cb19a94 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 15:55:32 +0900 Subject: [PATCH 14/63] Fix scrollbar overlapping content --- osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index eb4b97069c..8fcfdbf689 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -22,6 +22,7 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuScrollContainer(Direction.Vertical) { RelativeSizeAxes = Axes.Both, + ScrollbarOverlapsContent = false, Child = Content = new FillFlowContainer { Spacing = new Vector2(20), From 01fdb771ee4844ddd724548f1188bbdcd4705241 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Apr 2022 16:22:41 +0900 Subject: [PATCH 15/63] Add header text to first run setup screens --- .../FirstRunSetup/FirstRunSetupScreen.cs | 30 +++++++++++++++---- .../Overlays/FirstRunSetup/ScreenUIScale.cs | 1 + .../Overlays/FirstRunSetup/ScreenWelcome.cs | 6 +++- osu.Game/Overlays/FirstRunSetupOverlay.cs | 24 ++++----------- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index eb4b97069c..282ba52ddc 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -1,10 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osuTK; namespace osu.Game.Overlays.FirstRunSetup @@ -15,19 +19,33 @@ namespace osu.Game.Overlays.FirstRunSetup protected FillFlowContainer Content { get; private set; } - protected FirstRunSetupScreen() + [BackgroundDependencyLoader] + private void load(OverlayColourProvider overlayColourProvider) { + const float header_size = 40; + const float spacing = 20; + InternalChildren = new Drawable[] { new OsuScrollContainer(Direction.Vertical) { RelativeSizeAxes = Axes.Both, - Child = Content = new FillFlowContainer + Children = new Drawable[] { - Spacing = new Vector2(20), - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, + new OsuSpriteText + { + Text = this.GetLocalisableDescription(), + Font = OsuFont.Default.With(size: header_size), + Colour = overlayColourProvider.Light1, + }, + Content = new FillFlowContainer + { + Y = header_size + spacing, + Spacing = new Vector2(spacing), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + } }, } }; diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index ef48d9ced5..1bd82f6d99 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -27,6 +27,7 @@ using osuTK; namespace osu.Game.Overlays.FirstRunSetup { + [LocalisableDescription(typeof(GraphicsSettingsStrings), nameof(GraphicsSettingsStrings.UIScaling))] public class ScreenUIScale : FirstRunSetupScreen { [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs index 39da180f40..10e15a7555 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -1,16 +1,20 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Localisation; namespace osu.Game.Overlays.FirstRunSetup { + [LocalisableDescription(typeof(FirstRunSetupOverlayStrings), nameof(FirstRunSetupOverlayStrings.WelcomeTitle))] public class ScreenWelcome : FirstRunSetupScreen { - public ScreenWelcome() + [BackgroundDependencyLoader] + private void load() { Content.Children = new Drawable[] { diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index dc1ae2be37..7d5f4acee7 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -7,12 +7,12 @@ using System; using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Configuration; using osu.Game.Graphics; @@ -56,10 +56,10 @@ namespace osu.Game.Overlays /// public FirstRunSetupScreen? CurrentScreen => (FirstRunSetupScreen?)stack?.CurrentScreen; - private readonly FirstRunStep[] steps = + private readonly Type[] steps = { - new FirstRunStep(typeof(ScreenWelcome), FirstRunSetupOverlayStrings.WelcomeTitle), - new FirstRunStep(typeof(ScreenUIScale), GraphicsSettingsStrings.UIScaling), + typeof(ScreenWelcome), + typeof(ScreenUIScale) }; private Container stackContainer = null!; @@ -286,7 +286,7 @@ namespace osu.Game.Overlays if (currentStepIndex < steps.Length) { - stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value].ScreenType)); + stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value])); } else { @@ -307,21 +307,9 @@ namespace osu.Game.Overlays if (currentStepIndex != null) { NextButton.Text = currentStepIndex + 1 < steps.Length - ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].Description) + ? FirstRunSetupOverlayStrings.Next(steps[currentStepIndex.Value + 1].GetLocalisableDescription()) : CommonStrings.Finish; } } - - private class FirstRunStep - { - public readonly Type ScreenType; - public readonly LocalisableString Description; - - public FirstRunStep(Type screenType, LocalisableString description) - { - ScreenType = screenType; - Description = description; - } - } } } From b0d6e16658a17299663ca3451de1b094918429bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 21:57:19 +0200 Subject: [PATCH 16/63] Invert data flow in column dim implementation --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 82 ++++++++++++----------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 11cffe7959..ccd160a534 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -86,9 +86,6 @@ namespace osu.Game.Overlays.Mods } }); - // initialise the scroll early, as we will want to pass it to its children in the hierarchy initialisation below. - columnScroll = new OsuScrollContainer(Direction.Horizontal); - MainAreaContent.AddRange(new Drawable[] { new Container @@ -101,13 +98,13 @@ namespace osu.Game.Overlays.Mods RelativePositionAxes = Axes.Both, Children = new Drawable[] { - columnScroll.With(col => + columnScroll = new OsuScrollContainer(Direction.Horizontal) { - col.RelativeSizeAxes = Axes.Both; - col.Masking = false; - col.ClampExtension = 100; - col.ScrollbarOverlapsContent = false; - col.Child = columnFlow = new ColumnFlowContainer + RelativeSizeAxes = Axes.Both, + Masking = false, + ClampExtension = 100, + ScrollbarOverlapsContent = false, + Child = columnFlow = new ColumnFlowContainer { Direction = FillDirection.Horizontal, Shear = new Vector2(SHEAR, 0), @@ -117,14 +114,14 @@ namespace osu.Game.Overlays.Mods Margin = new MarginPadding { Horizontal = 70 }, Children = new[] { - createModColumnContent(columnScroll, ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), - createModColumnContent(columnScroll, ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), - createModColumnContent(columnScroll, ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), - createModColumnContent(columnScroll, ModType.Conversion), - createModColumnContent(columnScroll, ModType.Fun) + createModColumnContent(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }), + createModColumnContent(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }), + createModColumnContent(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }), + createModColumnContent(ModType.Conversion), + createModColumnContent(ModType.Fun) } - }; - }) + } + } } } }); @@ -159,11 +156,12 @@ namespace osu.Game.Overlays.Mods } } - private ColumnDimContainer createModColumnContent(OsuScrollContainer scroll, ModType modType, Key[]? toggleKeys = null) - => new ColumnDimContainer(scroll, CreateModColumn(modType, toggleKeys)) + private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null) + => new ColumnDimContainer(CreateModColumn(modType, toggleKeys)) { AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y + RelativeSizeAxes = Axes.Y, + RequestScroll = column => columnScroll.ScrollTo(Math.Clamp(column.DrawPosition.X - 70, 0, columnScroll.ScrollableExtent)) }; protected override void LoadComplete() @@ -322,6 +320,22 @@ namespace osu.Game.Overlays.Mods } } + protected override void Update() + { + base.Update(); + + foreach (var column in columnFlow) + { + // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, + // so we have to manually compensate. + var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), columnScroll); + var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), columnScroll); + + column.Active.Value = Precision.AlmostBigger(topLeft.X, 0) + && Precision.DefinitelyBigger(columnScroll.DrawWidth, topRight.X); + } + } + private class ColumnFlowContainer : FillFlowContainer { public IEnumerable Columns => Children.Select(dimWrapper => dimWrapper.Column); @@ -362,23 +376,22 @@ namespace osu.Game.Overlays.Mods { public ModColumn Column { get; } - private readonly ScrollContainer parentScroll; - private readonly Bindable isFullyOnScreen = new BindableBool(true); + public readonly Bindable Active = new BindableBool(); + public Action? RequestScroll { get; set; } [Resolved] private OsuColour colours { get; set; } = null!; - public ColumnDimContainer(ScrollContainer parentScroll, ModColumn column) + public ColumnDimContainer(ModColumn column) { - this.parentScroll = parentScroll; Child = Column = column; - column.Active.BindTo(isFullyOnScreen); + column.Active.BindTo(Active); } protected override void LoadComplete() { base.LoadComplete(); - isFullyOnScreen.BindValueChanged(_ => updateDim(), true); + Active.BindValueChanged(_ => updateDim(), true); FinishTransforms(); } @@ -386,7 +399,7 @@ namespace osu.Game.Overlays.Mods { Colour4 targetColour; - if (isFullyOnScreen.Value) + if (Active.Value) targetColour = Colour4.White; else targetColour = IsHovered ? colours.GrayC : colours.Gray8; @@ -394,21 +407,10 @@ namespace osu.Game.Overlays.Mods this.FadeColour(targetColour, 300, Easing.OutQuint); } - protected override void Update() - { - // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, - // so we have to manually compensate. - var topLeft = ToSpaceOfOtherDrawable(new Vector2(-DrawHeight * SHEAR, 0), parentScroll); - var topRight = ToSpaceOfOtherDrawable(new Vector2(DrawWidth, 0), parentScroll); - - isFullyOnScreen.Value = Precision.AlmostBigger(topLeft.X, 0) - && Precision.DefinitelyBigger(parentScroll.DrawWidth, topRight.X); - } - protected override bool OnMouseDown(MouseDownEvent e) { - if (!isFullyOnScreen.Value) - parentScroll.ScrollTo(Math.Clamp(DrawPosition.X - 70, 0, parentScroll.ScrollableExtent)); + if (!Active.Value) + RequestScroll?.Invoke(this); return true; } @@ -417,7 +419,7 @@ namespace osu.Game.Overlays.Mods { base.OnHover(e); updateDim(); - return isFullyOnScreen.Value; + return Active.Value; } protected override void OnHoverLost(HoverLostEvent e) From 13a36f2e5124cb152490017f529beec799165989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 22:01:24 +0200 Subject: [PATCH 17/63] Scroll to column when mouse is released rather than pressed --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index ccd160a534..4299bc05f1 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -407,7 +407,7 @@ namespace osu.Game.Overlays.Mods this.FadeColour(targetColour, 300, Easing.OutQuint); } - protected override bool OnMouseDown(MouseDownEvent e) + protected override bool OnClick(ClickEvent e) { if (!Active.Value) RequestScroll?.Invoke(this); From e9c9c764ca457277e39e2d965ef0e11f1a59ce32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 22:15:58 +0200 Subject: [PATCH 18/63] Fix column dim state changing when scrolling past extent --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 4299bc05f1..828ed93ca8 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -324,15 +324,22 @@ namespace osu.Game.Overlays.Mods { base.Update(); + // the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space. + // note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns. + float leftScrollBound = Math.Clamp(columnScroll.Current, 0, columnScroll.ScrollableExtent); + float rightScrollBound = leftScrollBound + columnScroll.DrawWidth; + foreach (var column in columnFlow) { // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, // so we have to manually compensate. - var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), columnScroll); - var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), columnScroll); + // additionally note that columnFlow.Parent is not columnScroll, but rather it is the scroll's internal container. + // this is intentional in order to include margin of the columnFlow in calculations correctly and operate in the "scroll internal content space". + var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), columnFlow.Parent); + var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), columnFlow.Parent); - column.Active.Value = Precision.AlmostBigger(topLeft.X, 0) - && Precision.DefinitelyBigger(columnScroll.DrawWidth, topRight.X); + column.Active.Value = Precision.AlmostBigger(topLeft.X, leftScrollBound) + && Precision.DefinitelyBigger(rightScrollBound, topRight.X); } } From 94d07e147f565fa89552b27d2ba89a2acda6c5ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 22:35:18 +0200 Subject: [PATCH 19/63] Move dimming logic to custom scroll container --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 44 +++++++++++++---------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 828ed93ca8..3aaec5edfd 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Mods private DifficultyMultiplierDisplay? multiplierDisplay; private ModSettingsArea modSettingsArea = null!; - private OsuScrollContainer columnScroll = null!; + private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; [BackgroundDependencyLoader] @@ -98,7 +98,7 @@ namespace osu.Game.Overlays.Mods RelativePositionAxes = Axes.Both, Children = new Drawable[] { - columnScroll = new OsuScrollContainer(Direction.Horizontal) + columnScroll = new ColumnScrollContainer { RelativeSizeAxes = Axes.Both, Masking = false, @@ -320,26 +320,32 @@ namespace osu.Game.Overlays.Mods } } - protected override void Update() + private class ColumnScrollContainer : OsuScrollContainer { - base.Update(); - - // the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space. - // note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns. - float leftScrollBound = Math.Clamp(columnScroll.Current, 0, columnScroll.ScrollableExtent); - float rightScrollBound = leftScrollBound + columnScroll.DrawWidth; - - foreach (var column in columnFlow) + public ColumnScrollContainer() + : base(Direction.Horizontal) { - // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, - // so we have to manually compensate. - // additionally note that columnFlow.Parent is not columnScroll, but rather it is the scroll's internal container. - // this is intentional in order to include margin of the columnFlow in calculations correctly and operate in the "scroll internal content space". - var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), columnFlow.Parent); - var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), columnFlow.Parent); + } - column.Active.Value = Precision.AlmostBigger(topLeft.X, leftScrollBound) - && Precision.DefinitelyBigger(rightScrollBound, topRight.X); + protected override void Update() + { + base.Update(); + + // the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space. + // note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns. + float leftScrollBound = Math.Clamp(Current, 0, ScrollableExtent); + float rightScrollBound = leftScrollBound + DrawWidth; + + foreach (var column in Child) + { + // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, + // so we have to manually compensate. + var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), ScrollContent); + var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), ScrollContent); + + column.Active.Value = Precision.AlmostBigger(topLeft.X, leftScrollBound) + && Precision.DefinitelyBigger(rightScrollBound, topRight.X); + } } } From 21377d2a4d96f5265043ef4b360c6b2ab5754067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 22:43:58 +0200 Subject: [PATCH 20/63] Fix inactive columns flickering on and off mid-scroll --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 3aaec5edfd..d0d487d1ad 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -333,8 +333,13 @@ namespace osu.Game.Overlays.Mods // the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space. // note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns. - float leftScrollBound = Math.Clamp(Current, 0, ScrollableExtent); - float rightScrollBound = leftScrollBound + DrawWidth; + float leftVisibleBound = Math.Clamp(Current, 0, ScrollableExtent); + float rightVisibleBound = leftVisibleBound + DrawWidth; + + // if a movement is occurring at this time, the bounds below represent the full range of columns that the scroll movement will encompass. + // this will be used to ensure that columns do not change state from active to inactive back and forth until they are fully scrolled past. + float leftMovementBound = Math.Min(Current, Target); + float rightMovementBound = Math.Max(Current, Target) + DrawWidth; foreach (var column in Child) { @@ -343,8 +348,12 @@ namespace osu.Game.Overlays.Mods var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), ScrollContent); var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), ScrollContent); - column.Active.Value = Precision.AlmostBigger(topLeft.X, leftScrollBound) - && Precision.DefinitelyBigger(rightScrollBound, topRight.X); + bool isCurrentlyVisible = Precision.AlmostBigger(topLeft.X, leftVisibleBound) + && Precision.DefinitelyBigger(rightVisibleBound, topRight.X); + bool isBeingScrolledToward = Precision.AlmostBigger(topLeft.X, leftMovementBound) + && Precision.DefinitelyBigger(rightMovementBound, topRight.X); + + column.Active.Value = isCurrentlyVisible || isBeingScrolledToward; } } } From 921e8af3b039b7c1f3ee10d23aaea2fcf3807d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 22:54:54 +0200 Subject: [PATCH 21/63] Use more lenient column bounds for checking active state --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index d0d487d1ad..76f4a8cdd4 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -345,13 +345,13 @@ namespace osu.Game.Overlays.Mods { // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, // so we have to manually compensate. - var topLeft = column.ToSpaceOfOtherDrawable(new Vector2(-column.DrawHeight * SHEAR, 0), ScrollContent); - var topRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth, 0), ScrollContent); + var topLeft = column.ToSpaceOfOtherDrawable(Vector2.Zero, ScrollContent); + var bottomRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth - column.DrawHeight * SHEAR, 0), ScrollContent); bool isCurrentlyVisible = Precision.AlmostBigger(topLeft.X, leftVisibleBound) - && Precision.DefinitelyBigger(rightVisibleBound, topRight.X); + && Precision.DefinitelyBigger(rightVisibleBound, bottomRight.X); bool isBeingScrolledToward = Precision.AlmostBigger(topLeft.X, leftMovementBound) - && Precision.DefinitelyBigger(rightMovementBound, topRight.X); + && Precision.DefinitelyBigger(rightMovementBound, bottomRight.X); column.Active.Value = isCurrentlyVisible || isBeingScrolledToward; } From a849bfcf60523da4b7b69d1ce92325cd75a71f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Apr 2022 23:11:38 +0200 Subject: [PATCH 22/63] Rewrite dim test to pass headless Unfortunately neuters the test a touch, but alas. --- .../UserInterface/TestSceneModSelectScreen.cs | 28 +++++++++---------- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index d7ae131372..ec6e962c6a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -119,25 +119,23 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("any column dimmed", () => this.ChildrenOfType().Any(column => !column.Active.Value)); - ModColumn firstDimmed = null; - ModPanel firstPanel = null; + ModColumn lastColumn = null; - AddStep("click first panel on dimmed column", () => + AddAssert("last column dimmed", () => !this.ChildrenOfType().Last().Active.Value); + AddStep("request scroll to last column", () => { - firstDimmed = this.ChildrenOfType().First(column => !column.Active.Value); - firstPanel = firstDimmed.ChildrenOfType().First(); - InputManager.MoveMouseTo(firstPanel); + var lastDimContainer = this.ChildrenOfType().Last(); + lastColumn = lastDimContainer.Column; + lastDimContainer.RequestScroll?.Invoke(lastDimContainer); + }); + AddUntilStep("column undimmed", () => lastColumn.Active.Value); + + AddStep("click panel", () => + { + InputManager.MoveMouseTo(lastColumn.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); - AddUntilStep("column undimmed", () => firstDimmed.Active.Value); - AddAssert("panel not selected", () => !firstPanel.Active.Value); - - AddStep("click panel again", () => - { - InputManager.MoveMouseTo(firstPanel); - InputManager.Click(MouseButton.Left); - }); - AddUntilStep("panel selected", () => firstPanel.Active.Value); + AddUntilStep("panel selected", () => lastColumn.ChildrenOfType().First().Active.Value); } [Test] diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 76f4a8cdd4..e28b46436c 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -394,7 +394,7 @@ namespace osu.Game.Overlays.Mods } } - private class ColumnDimContainer : Container + internal class ColumnDimContainer : Container { public ModColumn Column { get; } From 0343687b85fd00349056b06da0ef3fda63317e4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 15:59:39 +0900 Subject: [PATCH 23/63] Make apply default methods more explicit in behaviour --- .../Overlays/FirstRunSetup/ScreenBehaviour.cs | 9 +++++---- osu.Game/Overlays/Settings/ISettingsItem.cs | 15 +++++++++++--- osu.Game/Overlays/Settings/SettingsItem.cs | 20 +++++++++---------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs index 61177c821b..1316d14302 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -91,14 +92,14 @@ namespace osu.Game.Overlays.FirstRunSetup private void applyClassic() { - foreach (var i in searchContainer.ChildrenOfType()) - i.ApplyClassicDefault(true); + foreach (var i in searchContainer.ChildrenOfType().Where(s => s.HasClassicDefault)) + i.ApplyClassicDefault(); } private void applyStandard() { - foreach (var i in searchContainer.ChildrenOfType()) - i.ApplyClassicDefault(false); + foreach (var i in searchContainer.ChildrenOfType().Where(s => s.HasClassicDefault)) + i.ApplyDefault(); } } } diff --git a/osu.Game/Overlays/Settings/ISettingsItem.cs b/osu.Game/Overlays/Settings/ISettingsItem.cs index e6b1e68741..61191dcacf 100644 --- a/osu.Game/Overlays/Settings/ISettingsItem.cs +++ b/osu.Game/Overlays/Settings/ISettingsItem.cs @@ -11,9 +11,18 @@ namespace osu.Game.Overlays.Settings event Action SettingChanged; /// - /// Apply the default values of a setting item, if the setting item specifies a "classic" default via . + /// Whether this setting has a classic default (ie. a different default which better aligns with osu-stable expectations). /// - /// Whether to apply the classic value. If false, the standard default is applied. - void ApplyClassicDefault(bool useClassicDefault); + bool HasClassicDefault { get; } + + /// + /// Apply the classic default value of the associated setting. Will throw if is false. + /// + void ApplyClassicDefault(); + + /// + /// Apply the default value of the associated setting. + /// + void ApplyDefault(); } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 3c755ab570..afcd41af22 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.Settings LabelText.ToString() }; - if (hasClassicDefault) + if (HasClassicDefault) keywords.Add(CLASSIC_DEFAULT_SEARCH_TERM); return keywords; @@ -139,7 +139,8 @@ namespace osu.Game.Overlays.Settings public event Action SettingChanged; private T classicDefault; - private bool hasClassicDefault; + + public bool HasClassicDefault { get; private set; } /// /// A "classic" default value for this setting. @@ -149,21 +150,20 @@ namespace osu.Game.Overlays.Settings set { classicDefault = value; - hasClassicDefault = true; + HasClassicDefault = true; } } - public void ApplyClassicDefault(bool useClassicDefault) + public void ApplyClassicDefault() { - if (!hasClassicDefault) - return; + if (!HasClassicDefault) + throw new InvalidOperationException($"Cannot apply a classic default to a setting which doesn't have one defined via {nameof(ClassicDefault)}."); - if (useClassicDefault) - Current.Value = classicDefault; - else - Current.SetDefault(); + Current.Value = classicDefault; } + public void ApplyDefault() => Current.SetDefault(); + protected SettingsItem() { RelativeSizeAxes = Axes.X; From 0354dd5ce623830c0061e5414f59ba0372fe270e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 16:02:39 +0900 Subject: [PATCH 24/63] Add comment regarding section initialisation in `ScreenBehaviour` --- osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs | 2 ++ osu.Game/Overlays/SettingsOverlay.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs index 1316d14302..db7659e87a 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs @@ -74,8 +74,10 @@ namespace osu.Game.Overlays.FirstRunSetup AutoSizeAxes = Axes.Y, Children = new SettingsSection[] { + // This list should be kept in sync with SettingsOverlay. new GeneralSection(), new SkinSection(), + // InputSection is intentionally omitted for now due to its sub-panel being a pain to set up. new UserInterfaceSection(), new GameplaySection(), new RulesetSection(), diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index c84cba8189..7cd8fc6d66 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -23,6 +23,7 @@ namespace osu.Game.Overlays protected override IEnumerable CreateSections() => new SettingsSection[] { + // This list should be kept in sync with ScreenBehaviour. new GeneralSection(), new SkinSection(), new InputSection(createSubPanel(new KeyBindingPanel())), From b29af28028a172ed3ad756f5272a2286eecd833c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 16:55:15 +0900 Subject: [PATCH 25/63] Fix mod panels not ignoring super key presses Most other usages have this included. Noticed that the panel was changing state when exiting the game using cmd-w. Would probably be nice to have an exposed `HasAnyModifierPressed` helper property. --- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 1157c0c0c6..6a2c2adcba 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -441,7 +441,7 @@ namespace osu.Game.Overlays.Mods protected override bool OnKeyDown(KeyDownEvent e) { - if (e.ControlPressed || e.AltPressed) return false; + if (e.ControlPressed || e.AltPressed || e.SuperPressed) return false; if (toggleKeys == null) return false; int index = Array.IndexOf(toggleKeys, e.Key); From 1a345c06c6c92c20593bb1f413e267498e2f2bfa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 17:10:27 +0900 Subject: [PATCH 26/63] Fix regression in nested scroll behaviour due to the top level scroll container's type changing --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 4 ++-- osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index e28b46436c..a47a399a23 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -320,7 +320,7 @@ namespace osu.Game.Overlays.Mods } } - private class ColumnScrollContainer : OsuScrollContainer + internal class ColumnScrollContainer : OsuScrollContainer { public ColumnScrollContainer() : base(Direction.Horizontal) @@ -358,7 +358,7 @@ namespace osu.Game.Overlays.Mods } } - private class ColumnFlowContainer : FillFlowContainer + internal class ColumnFlowContainer : FillFlowContainer { public IEnumerable Columns => Children.Select(dimWrapper => dimWrapper.Column); diff --git a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs index aba47d5423..d27f97f3d2 100644 --- a/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs +++ b/osu.Game/Overlays/Mods/NestedVerticalScrollContainer.cs @@ -14,13 +14,13 @@ namespace osu.Game.Overlays.Mods /// public class NestedVerticalScrollContainer : OsuScrollContainer { - private OsuScrollContainer? parentScrollContainer; + private ModSelectScreen.ColumnScrollContainer? parentScrollContainer; protected override void LoadComplete() { base.LoadComplete(); - parentScrollContainer = this.FindClosestParent(); + parentScrollContainer = this.FindClosestParent(); } protected override bool OnScroll(ScrollEvent e) From 20a50ddb6e0639adc5980a4e2c6c85ad2c4d13aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 18:45:40 +0900 Subject: [PATCH 27/63] Add missing `OverlayColourProvider` in test scene --- .../Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs index 5ca09b34aa..64ad4ff119 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenUIScale.cs @@ -1,13 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Screens; +using osu.Game.Overlays; using osu.Game.Overlays.FirstRunSetup; namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneFirstRunScreenUIScale : OsuManualInputManagerTestScene { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + public TestSceneFirstRunScreenUIScale() { AddStep("load screen", () => From e9ec406046033632a19d062e6b4cd7ae2bd86514 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 19:20:08 +0900 Subject: [PATCH 28/63] Remove weird code --- osu.Game/Beatmaps/Drawables/DownloadProgressBar.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DownloadProgressBar.cs b/osu.Game/Beatmaps/Drawables/DownloadProgressBar.cs index 54dcdc55e3..ad0ff876e8 100644 --- a/osu.Game/Beatmaps/Drawables/DownloadProgressBar.cs +++ b/osu.Game/Beatmaps/Drawables/DownloadProgressBar.cs @@ -28,11 +28,6 @@ namespace osu.Game.Beatmaps.Drawables }, downloadTracker = new BeatmapDownloadTracker(beatmapSet), }; - AddInternal(progressBar = new ProgressBar(false) - { - Height = 0, - Alpha = 0, - }); AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; From f3a0e2ed55ba0c99feaec64911d92575bbb3d72c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 14:59:39 +0900 Subject: [PATCH 29/63] Increase fade duration --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index a47a399a23..03a66575c4 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -426,7 +426,7 @@ namespace osu.Game.Overlays.Mods else targetColour = IsHovered ? colours.GrayC : colours.Gray8; - this.FadeColour(targetColour, 300, Easing.OutQuint); + this.FadeColour(targetColour, 800, Easing.OutQuint); } protected override bool OnClick(ClickEvent e) From edeefc28501a12c7719025a63925975b39e4bd3c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 11:10:14 +0300 Subject: [PATCH 30/63] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 82dec74855..ff6499631d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 325e834fa5..26891ad978 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 8775442be2..d261e13ade 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From c8665dc93b40835dd08687c51ec4384354e83584 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 11:12:50 +0300 Subject: [PATCH 31/63] Update `TimeSpan` localisation to use `ToLocalisableString` --- osu.Game/Extensions/TimeDisplayExtensions.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs index 54af6a5942..98633958ee 100644 --- a/osu.Game/Extensions/TimeDisplayExtensions.cs +++ b/osu.Game/Extensions/TimeDisplayExtensions.cs @@ -3,6 +3,7 @@ using System; using Humanizer; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; @@ -42,12 +43,12 @@ namespace osu.Game.Extensions public static LocalisableString ToFormattedDuration(this TimeSpan timeSpan) { if (timeSpan.TotalDays >= 1) - return new LocalisableFormattableString(timeSpan, @"dd\:hh\:mm\:ss"); + return timeSpan.ToLocalisableString(@"dd\:hh\:mm\:ss"); if (timeSpan.TotalHours >= 1) - return new LocalisableFormattableString(timeSpan, @"hh\:mm\:ss"); + return timeSpan.ToLocalisableString(@"hh\:mm\:ss"); - return new LocalisableFormattableString(timeSpan, @"mm\:ss"); + return timeSpan.ToLocalisableString(@"mm\:ss"); } /// From be2d616e345e7e37ff5698b1b57c435209874d83 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 11:19:30 +0300 Subject: [PATCH 32/63] Fix localisation use cases not falling back to `default(LocalisableString)` Previously such use cases have been returning `null` values just fine since `GetLocalisableString` was returning `LocalisableFormattableString`, and these null values were wrapped in `LocalisableString`s implicitly, therefore not requiring an explicit fallback string and `null` doesn't break anything. --- .../Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs | 2 +- .../Overlays/Profile/Header/Components/LevelProgressBar.cs | 2 +- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 2 +- osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index ec795cf6b2..9ee002fd9d 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -119,7 +119,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x"); ppColumn.Alpha = value.BeatmapInfo.Status.GrantsPerformancePoints() ? 1 : 0; - ppColumn.Text = value.PP?.ToLocalisableString(@"N0"); + ppColumn.Text = value.PP?.ToLocalisableString(@"N0") ?? default(LocalisableString); statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(createStatisticsColumn); modsColumn.Mods = value.Mods; diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index 8f6b935128..ec9cb55042 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void updateProgress(APIUser user) { levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0; - levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'"); + levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'") ?? default(LocalisableString); } } } diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index dfa45cc543..48a4c31f30 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Rankings startDateColumn.Value = dateToString(response.Spotlight.StartDate); endDateColumn.Value = dateToString(response.Spotlight.EndDate); mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString(@"N0"); - participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString(@"N0"); + participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString(@"N0") ?? default(LocalisableString); } private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString(@"yyyy-MM-dd"); diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 17c17b1f1a..bdbd2942d1 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; @@ -24,7 +25,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new RowText { Text = item.PP?.ToLocalisableString(@"N0"), } + new RowText { Text = item.PP?.ToLocalisableString(@"N0") ?? default(LocalisableString), } }; } } From 92cac0b74e67616aec5845d11c9adb7deb3d6b2a Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 28 Apr 2022 17:30:24 +0900 Subject: [PATCH 33/63] Fix TabControls using the wrong sample --- .../BeatmapListing/BeatmapListingCardSizeTabControl.cs | 3 ++- osu.Game/Overlays/OverlaySortTabControl.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs index e4fda9d9c3..1f9a63e3b9 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs @@ -72,7 +72,8 @@ namespace osu.Game.Overlays.BeatmapListing Size = new Vector2(12), Icon = getIconForCardSize(Value) } - } + }, + new HoverClickSounds(HoverSampleSet.TabSelect) }; } diff --git a/osu.Game/Overlays/OverlaySortTabControl.cs b/osu.Game/Overlays/OverlaySortTabControl.cs index d4dde0db3f..5f5cfce344 100644 --- a/osu.Game/Overlays/OverlaySortTabControl.cs +++ b/osu.Game/Overlays/OverlaySortTabControl.cs @@ -149,7 +149,7 @@ namespace osu.Game.Overlays } }); - AddInternal(new HoverClickSounds()); + AddInternal(new HoverClickSounds(HoverSampleSet.TabSelect)); } protected override void LoadComplete() From 37816ebc41abc2679c667a848865382872aa01b2 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 28 Apr 2022 17:33:51 +0900 Subject: [PATCH 34/63] Add audio feedback to `SwitchButton` checkbox --- .../Graphics/UserInterfaceV2/SwitchButton.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs index c6477d1781..f483e67b27 100644 --- a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs @@ -4,6 +4,8 @@ #nullable enable using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -31,6 +33,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 private Color4 enabledColour; private Color4 disabledColour; + private Sample? sampleChecked; + private Sample? sampleUnchecked; + public SwitchButton() { Size = new Vector2(45, 20); @@ -70,13 +75,16 @@ namespace osu.Game.Graphics.UserInterfaceV2 } [BackgroundDependencyLoader(true)] - private void load(OverlayColourProvider? colourProvider, OsuColour colours) + private void load(OverlayColourProvider? colourProvider, OsuColour colours, AudioManager audio) { enabledColour = colourProvider?.Highlight1 ?? colours.BlueDark; disabledColour = colourProvider?.Background3 ?? colours.Gray3; switchContainer.Colour = enabledColour; fill.Colour = disabledColour; + + sampleChecked = audio.Samples.Get(@"UI/check-on"); + sampleUnchecked = audio.Samples.Get(@"UI/check-off"); } protected override void LoadComplete() @@ -107,6 +115,16 @@ namespace osu.Game.Graphics.UserInterfaceV2 base.OnHoverLost(e); } + protected override void OnUserChange(bool value) + { + base.OnUserChange(value); + + if (value) + sampleChecked?.Play(); + else + sampleUnchecked?.Play(); + } + private void updateBorder() { circularContainer.TransformBorderTo((Current.Value ? enabledColour : disabledColour).Lighten(IsHovered ? 0.3f : 0)); From 691bec6f16845c94fa79ba26ff87372e988c43d3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 11:38:40 +0300 Subject: [PATCH 35/63] Use `LocalisableString.Interpolate` instead of invalid `TranslatableString`s --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 4 ++-- osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 27a057bf09..735de670e3 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -317,11 +317,11 @@ namespace osu.Game.Overlays } else { - BackButton.Text = new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Back, steps[currentStepIndex.Value - 1].Description); + BackButton.Text = LocalisableString.Interpolate($@"{CommonStrings.Back} ({steps[currentStepIndex.Value - 1].Description})"); NextButton.Text = isLastStep ? CommonStrings.Finish - : new TranslatableString(@"_", @"{0} ({1})", CommonStrings.Next, steps[currentStepIndex.Value + 1].Description); + : LocalisableString.Interpolate($@"{CommonStrings.Next} ({steps[currentStepIndex.Value + 1].Description})"); } } diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 42091c521f..1662ca399f 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -99,8 +99,8 @@ namespace osu.Game.Screens.Play.PlayerSettings { public override LocalisableString TooltipText => Current.Value == 0 - ? new TranslatableString("_", @"{0} ms", base.TooltipText) - : new TranslatableString("_", @"{0} ms {1}", base.TooltipText, getEarlyLateText(Current.Value)); + ? LocalisableString.Interpolate($@"{base.TooltipText} ms") + : LocalisableString.Interpolate($@"{base.TooltipText} ms {getEarlyLateText(Current.Value)}"); private LocalisableString getEarlyLateText(double value) { From 93db6c6bb0a82b85cf1a4429bcf8e7ff165bc75f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 17:46:00 +0900 Subject: [PATCH 36/63] Move `FastRandom` to `LegacyRandom` in `osu.Game` project --- .../Beatmaps/CatchBeatmapProcessor.cs | 8 +- .../Beatmaps/ManiaBeatmapConverter.cs | 8 +- .../Legacy/DistanceObjectPatternGenerator.cs | 4 +- .../Legacy/EndTimeObjectPatternGenerator.cs | 4 +- .../Legacy/HitObjectPatternGenerator.cs | 4 +- .../Patterns/Legacy/PatternGenerator.cs | 6 +- .../MathUtils/FastRandom.cs | 95 ------------------- .../Utils/LegacyRandom.cs | 37 +++++--- 8 files changed, 40 insertions(+), 126 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs rename osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs => osu.Game/Utils/LegacyRandom.cs (79%) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 346a09cac8..ab61b14ac4 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.MathUtils; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Utils; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps public void ApplyPositionOffsets(IBeatmap beatmap) { - var rng = new FastRandom(RNG_SEED); + var rng = new LegacyRandom(RNG_SEED); float? lastPosition = null; double lastStartTime = 0; @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps initialiseHyperDash(beatmap); } - private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) + private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, LegacyRandom rng) { float offsetPosition = hitObject.OriginalX; double startTime = hitObject.StartTime; @@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps /// The position which the offset should be applied to. /// The maximum offset, cannot exceed 20px. /// The random number generator. - private static void applyRandomOffset(ref float position, double maxOffset, FastRandom rng) + private static void applyRandomOffset(ref float position, double maxOffset, LegacyRandom rng) { bool right = rng.NextBool(); float rand = Math.Min(20, (float)rng.Next(0, Math.Max(0, maxOffset))); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 47e0e6d7b1..207c6907c8 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -11,8 +11,8 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Beatmaps.Patterns; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; +using osu.Game.Utils; using osuTK; namespace osu.Game.Rulesets.Mania.Beatmaps @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private readonly int originalTargetColumns; // Internal for testing purposes - internal FastRandom Random { get; private set; } + internal LegacyRandom Random { get; private set; } private Pattern lastPattern = new Pattern(); @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps IBeatmapDifficultyInfo difficulty = original.Difficulty; int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate); - Random = new FastRandom(seed); + Random = new LegacyRandom(seed); return base.ConvertBeatmap(original, cancellationToken); } @@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator { - public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public SpecificBeatmapPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 5f8b58d94d..dafe65f415 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -8,12 +8,12 @@ using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Game.Audio; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private PatternType convertType; - public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public DistanceObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { convertType = PatternType.None; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index f816a70ab3..2265d3d347 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -2,13 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using System.Linq; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly int endTime; private readonly PatternType convertType; - public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public EndTimeObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { endTime = (int)((HitObject as IHasDuration)?.EndTime ?? 0); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 53b059b4e2..41d4c9322b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -9,11 +9,11 @@ using osuTK; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly PatternType convertType; - public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, + public HitObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index eaf0ea0f2b..d5689c047a 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -5,8 +5,8 @@ using System; using System.Linq; using JetBrains.Annotations; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -23,14 +23,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// The random number generator to use. /// - protected readonly FastRandom Random; + protected readonly LegacyRandom Random; /// /// The beatmap which is being converted from. /// protected readonly IBeatmap OriginalBeatmap; - protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + protected PatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(hitObject, beatmap, previousPattern) { if (random == null) throw new ArgumentNullException(nameof(random)); diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs deleted file mode 100644 index a9cd7f2476..0000000000 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; - -namespace osu.Game.Rulesets.Mania.MathUtils -{ - /// - /// A PRNG specified in http://heliosphan.org/fastrandom.html. - /// - internal class FastRandom - { - private const double int_to_real = 1.0 / (int.MaxValue + 1.0); - private const uint int_mask = 0x7FFFFFFF; - private const uint y = 842502087; - private const uint z = 3579807591; - private const uint w = 273326509; - - internal uint X { get; private set; } - internal uint Y { get; private set; } = y; - internal uint Z { get; private set; } = z; - internal uint W { get; private set; } = w; - - public FastRandom(int seed) - { - X = (uint)seed; - } - - public FastRandom() - : this(Environment.TickCount) - { - } - - /// - /// Generates a random unsigned integer within the range [, ). - /// - /// The random value. - public uint NextUInt() - { - uint t = X ^ (X << 11); - X = Y; - Y = Z; - Z = W; - return W = W ^ (W >> 19) ^ t ^ (t >> 8); - } - - /// - /// Generates a random integer value within the range [0, ). - /// - /// The random value. - public int Next() => (int)(int_mask & NextUInt()); - - /// - /// Generates a random integer value within the range [0, ). - /// - /// The upper bound. - /// The random value. - public int Next(int upperBound) => (int)(NextDouble() * upperBound); - - /// - /// Generates a random integer value within the range [, ). - /// - /// The lower bound of the range. - /// The upper bound of the range. - /// The random value. - public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); - - /// - /// Generates a random double value within the range [0, 1). - /// - /// The random value. - public double NextDouble() => int_to_real * Next(); - - private uint bitBuffer; - private int bitIndex = 32; - - /// - /// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls. - /// - /// The random value. - public bool NextBool() - { - if (bitIndex == 32) - { - bitBuffer = NextUInt(); - bitIndex = 1; - - return (bitBuffer & 1) == 1; - } - - bitIndex++; - return ((bitBuffer >>= 1) & 1) == 1; - } - } -} diff --git a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs b/osu.Game/Utils/LegacyRandom.cs similarity index 79% rename from osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs rename to osu.Game/Utils/LegacyRandom.cs index 46e427e1b7..cf731aa91f 100644 --- a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs +++ b/osu.Game/Utils/LegacyRandom.cs @@ -2,27 +2,36 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Utils; -namespace osu.Game.Rulesets.Catch.MathUtils +namespace osu.Game.Utils { /// /// A PRNG specified in http://heliosphan.org/fastrandom.html. + /// Should only be used to match legacy behaviour. See for a newer alternative. /// - public class FastRandom + /// + /// Known in osu-stable code as `FastRandom`. + /// + public class LegacyRandom { private const double int_to_real = 1.0 / (int.MaxValue + 1.0); private const uint int_mask = 0x7FFFFFFF; - private const uint y_initial = 842502087; - private const uint z_initial = 3579807591; - private const uint w_initial = 273326509; - private uint x, y = y_initial, z = z_initial, w = w_initial; + private const uint y = 842502087; + private const uint z = 3579807591; + private const uint w = 273326509; - public FastRandom(int seed) + public uint X { get; private set; } + public uint Y { get; private set; } = y; + public uint Z { get; private set; } = z; + public uint W { get; private set; } = w; + + public LegacyRandom(int seed) { - x = (uint)seed; + X = (uint)seed; } - public FastRandom() + public LegacyRandom() : this(Environment.TickCount) { } @@ -33,11 +42,11 @@ namespace osu.Game.Rulesets.Catch.MathUtils /// The random value. public uint NextUInt() { - uint t = x ^ (x << 11); - x = y; - y = z; - z = w; - return w = w ^ (w >> 19) ^ t ^ (t >> 8); + uint t = X ^ (X << 11); + X = Y; + Y = Z; + Z = W; + return W = W ^ (W >> 19) ^ t ^ (t >> 8); } /// From d052321eee722155c2635560819b60a3a8a41089 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 28 Apr 2022 12:11:06 +0300 Subject: [PATCH 37/63] Revert "Fix context menus not working" This reverts commit 51aa17eb524e70899f09c1058842e0d55f58109b. --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 18 +----------------- osu.Game/Graphics/UserInterface/OsuMenu.cs | 18 +----------------- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 20fa7d5148..b1d4691938 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -12,7 +12,6 @@ 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; @@ -131,22 +130,7 @@ namespace osu.Game.Graphics.UserInterface BackgroundColourSelected = SelectionColour }; - protected override ScrollContainer CreateScrollContainer(Direction direction) => new DropdownScrollContainer(direction); - - // Hotfix for https://github.com/ppy/osu/issues/17961 - public class DropdownScrollContainer : OsuScrollContainer - { - public DropdownScrollContainer(Direction direction) - : base(direction) - { - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - base.OnMouseDown(e); - return true; - } - } + protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); #region DrawableOsuDropdownMenuItem diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index bfdfd32fb3..a16adcbd57 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -9,7 +9,6 @@ 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; @@ -82,22 +81,7 @@ namespace osu.Game.Graphics.UserInterface return new DrawableOsuMenuItem(item); } - protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuMenuScrollContainer(direction); - - // Hotfix for https://github.com/ppy/osu/issues/17961 - public class OsuMenuScrollContainer : OsuScrollContainer - { - public OsuMenuScrollContainer(Direction direction) - : base(direction) - { - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - base.OnMouseDown(e); - return true; - } - } + protected override ScrollContainer CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction); protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical) { From 99d2d7b80592777fbde57bca6aab8f8d2b49fdcb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 18:57:10 +0900 Subject: [PATCH 38/63] Add very basic implementation of bundled beatmap downloader --- .../TestSceneBundledBeatmapDownloader.cs | 24 ++ .../Drawables/BundledBeatmapDownloader.cs | 227 ++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs create mode 100644 osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs b/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs new file mode 100644 index 0000000000..895524e79a --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps.Drawables; + +namespace osu.Game.Tests.Visual.Online +{ + [Ignore("Only for visual testing")] + public class TestSceneBundledBeatmapDownloader : OsuTestScene + { + private BundledBeatmapDownloader downloader; + + [Test] + public void TestDownloader() + { + AddStep("Create downloader", () => + { + downloader?.Expire(); + Add(downloader = new BundledBeatmapDownloader()); + }); + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs new file mode 100644 index 0000000000..6b14ed760d --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -0,0 +1,227 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using System.Text.RegularExpressions; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Beatmaps.Drawables +{ + public class BundledBeatmapDownloader : CompositeDrawable + { + [Resolved] + private BeatmapModelDownloader beatmapDownloader { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + foreach (string filename in bundled_beatmap_filenames.OrderBy(_ => RNG.NextSingle()).Take(10)) + { + var match = Regex.Match(filename, @"([0-9]*) (.*) - (.*)\.osz"); + + beatmapDownloader.Download(new APIBeatmapSet + { + OnlineID = int.Parse(match.Groups[1].Value), + Artist = match.Groups[2].Value, + Title = match.Groups[3].Value, + }); + } + } + + private static readonly string[] bundled_beatmap_filenames = new[] + { + "682286 Yuyoyuppe - Emerald Galaxy.osz", + "682287 baker - For a Dead Girl+.osz", + "682289 Hige Driver - I Wanna Feel Your Love (feat. shully).osz", + "682290 Hige Driver - Miracle Sugite Yabai (feat. shully).osz", + "682416 Hige Driver - Palette.osz", + "682595 baker - Kimi ga Kimi ga -vocanico remix-.osz", + "716211 yuki. - Spring Signal.osz", + "716213 dark cat - BUBBLE TEA (feat. juu & cinders).osz", + "716215 LukHash - CLONED.osz", + "716219 IAHN - Snowdrop.osz", + "716249 *namirin - Senaka Awase no Kuukyo (with Kakichoco).osz", + "716390 sakuraburst - SHA.osz", + "716441 Fractal Dreamers - Paradigm Shift.osz", + "729808 Thaehan - Leprechaun.osz", + "751771 Cranky - Hanaarashi.osz", + "751772 Cranky - Ran.osz", + "751773 Cranky - Feline, the White....osz", + "751774 Function Phantom - Variable.osz", + "751779 Rin - Daishibyo set 14 ~ Sado no Futatsuiwa.osz", + "751782 Fractal Dreamers - Fata Morgana.osz", + "751785 Cranky - Chandelier - King.osz", + "751846 Fractal Dreamers - Celestial Horizon.osz", + "751866 Rin - Moriya set 08 ReEdit ~ Youkai no Yama.osz", + "751894 Fractal Dreamers - Blue Haven.osz", + "751896 Cranky - Rave 2 Rave.osz", + "751932 Cranky - La fuite des jours.osz", + "751972 Cranky - CHASER.osz", + "779173 Thaehan - Superpower.osz", + "780932 VINXIS - A Centralized View.osz", + "785572 S3RL - I'll See You Again (feat. Chi Chi).osz", + "785650 yuki. feat. setsunan - Hello! World.osz", + "785677 Dictate - Militant.osz", + "785731 S3RL - Catchit (Radio Edit).osz", + "785774 LukHash - GLITCH.osz", + "786498 Trial & Error - Tokoyami no keiyaku KEGARETA-SHOUJO feat. GUMI.osz", + "789374 Pulse - LP.osz", + "789528 James Portland - Sky.osz", + "789529 Lexurus - Gravity.osz", + "789544 Andromedik - Invasion.osz", + "789905 Gourski x Himmes - Silence.osz", + "791667 cYsmix - Babaroque (Short Ver.).osz", + "791798 cYsmix - Behind the Walls.osz", + "791845 cYsmix - Little Knight.osz", + "792241 cYsmix - Eden.osz", + "792396 cYsmix - The Ballad of a Mindless Girl.osz", + "795432 Phonetic - Journey.osz", + "831322 DJ'TEKINA//SOMETHING - Hidamari no Uta.osz", + "847764 Cranky - Crocus.osz", + "847776 Culprate & Joe Ford - Gaucho.osz", + "847812 J. Pachelbel - Canon (Cranky Remix).osz", + "847900 Cranky - Time Alter.osz", + "847930 LukHash - 8BIT FAIRY TALE.osz", + "848003 Culprate - Aurora.osz", + "848068 nanobii - popsicle beach.osz", + "848090 Trial & Error - DAI*TAN SENSATION feat. Nanahira, Mii, Aitsuki Nakuru (Short Ver.).osz", + "848259 Culprate & Skorpion - Jester.osz", + "848976 Dictate - Treason.osz", + "851543 Culprate - Florn.osz", + "864748 Thaehan - Angry Birds Epic (Remix).osz", + "873667 OISHII - ONIGIRI FREEWAY.osz", + "876227 Culprate, Keota & Sophie Meiers - Mechanic Heartbeat.osz", + "880487 cYsmix - Peer Gynt.osz", + "883088 Wisp X - Somewhere I'd Rather Be.osz", + "891333 HyuN - White Aura.osz", + "891334 HyuN - Wild Card.osz", + "891337 HyuN feat. LyuU - Cross Over.osz", + "891338 HyuN & Ritoru - Apocalypse in Love.osz", + "891339 HyuN feat. Ato - Asu wa Ame ga Yamukara.osz", + "891345 HyuN - Infinity Heaven.osz", + "891348 HyuN - Guitian.osz", + "891356 HyuN - Legend of Genesis.osz", + "891366 HyuN - Illusion of Inflict.osz", + "891417 HyuN feat. Yu-A - My life is for you.osz", + "891441 HyuN - You'Re aRleAdY dEAd.osz", + "891632 HyuN feat. YURI - Disorder.osz", + "891712 HyuN - Tokyo's Starlight.osz", + "901091 *namirin - Ciel etoile.osz", + "916990 *namirin - Koishiteiku Planet.osz", + "929284 tieff - Sense of Nostalgia.osz", + "933940 Ben Briggs - Yes (Maybe).osz", + "934415 Ben Briggs - Fearless Living.osz", + "934627 Ben Briggs - New Game Plus.osz", + "934666 Ben Briggs - Wave Island.osz", + "936126 siromaru + cranky - conflict.osz", + "940377 onumi - ARROGANCE.osz", + "940597 tieff - Take Your Swimsuit.osz", + "941085 tieff - Our Story.osz", + "949297 tieff - Sunflower.osz", + "952380 Ben Briggs - Why Are We Yelling.osz", + "954272 *namirin - Kanzen Shouri*Esper Girl.osz", + "955866 KIRA & Heartbreaker - B.B.F (feat. Hatsune Miku & Kagamine Rin).osz", + "961320 Kuba Oms - All In All.osz", + "964553 The Flashbulb - You Take the World's Weight Away.osz", + "965651 Fractal Dreamers - Ad Astra.osz", + "966225 The Flashbulb - Passage D.osz", + "966324 DJ'TEKINA//SOMETHING - Hidamari no Uta.osz", + "972810 James Landino & Kabuki - Birdsong.osz", + "972932 James Landino - Hide And Seek.osz", + "977276 The Flashbulb - Mellann.osz", + "981616 *namirin - Mizutamari Tobikoete (with Nanahira).osz", + "985788 Loki - Wizard's Tower.osz", + "996628 OISHII - ONIGIRI FREEWAY.osz", + "996898 HyuN - White Aura.osz", + "1003554 yuki. - Nadeshiko Sensation.osz", + "1014936 Thaehan - Bwa !.osz", + "1019827 UNDEAD CORPORATION - Sad Dream.osz", + "1020213 Creo - Idolize.osz", + "1021450 Thaehan - Chiptune & Baroque.osz", + "707824 Fractal Dreamers - Fortuna Redux.osz", + "789553 Cranky - Ran.osz", + "827822 Function Phantom - Neuronecia.osz", + "847323 Nakanojojo - Bittersweet (feat. Kuishinboakachan a.k.a Kiato).osz", + "847433 Trial & Error - Tokoyami no keiyaku KEGARETA-SHOUJO feat. GUMI.osz", + "847576 dark cat - hot chocolate.osz", + "847957 Wisp X - Final Moments.osz", + "876282 VINXIS - Greetings.osz", + "876648 Thaehan - Angry Birds Epic (Remix).osz", + "877069 IAHN - Transform (Original Mix).osz", + "877496 Thaehan - Leprechaun.osz", + "877935 Thaehan - Overpowered.osz", + "878344 yuki. - Be Your Light.osz", + "918446 VINXIS - Facade.osz", + "918903 LukHash - Ghosts.osz", + "919251 *namirin - Hitokoto no Kyori.osz", + "919704 S3RL - I Will Pick You Up (feat. Tamika).osz", + "921535 SOOOO - Raven Haven.osz", + "927206 *namirin - Kanzen Shouri*Esper Girl.osz", + "927544 Camellia feat. Nanahira - Kansoku Eisei.osz", + "930806 Nakanojojo - Pararara (feat. Amekoya).osz", + "931741 Camellia - Quaoar.osz", + "935699 Rin - Mythic set ~ Heart-Stirring Urban Legends.osz", + "935732 Thaehan - Yuujou.osz", + "941145 Function Phantom - Euclid.osz", + "942334 Dictate - Cauldron.osz", + "946540 nanobii - astral blast.osz", + "948844 Rin - Kishinjou set 01 ~ Mist Lake.osz", + "949122 Wisp X - Petal.osz", + "951618 Rin - Kishinjou set 02 ~ Mermaid from the Uncharted Land.osz", + "957412 Rin - Lunatic set 16 ~ The Space Shrine Maiden Returns Home.osz", + "961335 Thaehan - Insert Coin.osz", + "965178 The Flashbulb - DIDJ PVC.osz", + "966087 The Flashbulb - Creep.osz", + "966277 The Flashbulb - Amen Iraq.osz", + "966407 LukHash - ROOM 12.osz", + "966451 The Flashbulb - Six Acid Strings.osz", + "972301 BilliumMoto - four veiled stars.osz", + "973173 nanobii - popsicle beach.osz", + "973954 BilliumMoto - Rocky Buinne (Short Ver.).osz", + "975435 BilliumMoto - life flashes before weeb eyes.osz", + "978759 L. V. Beethoven - Moonlight Sonata (Cranky Remix).osz", + "982559 BilliumMoto - HDHR.osz", + "984361 The Flashbulb - Ninedump.osz", + "1023681 Inferi - The Ruin of Mankind.osz", + "1034358 ALEPH - The Evil Spirit.osz", + "1037567 ALEPH - Scintillations.osz", + "554256 Helblinde - When Time Sleeps.osz", + "693123 yuki. - Nadeshiko Sensation.osz", + "767009 OISHII - PIZZA PLAZA.osz", + "767346 Thaehan - Bwa !.osz", + "815162 VINXIS - Greetings.osz", + "840964 cYsmix - Breeze.osz", + "932657 Wisp X - Eventide.osz", + "933700 onumi - CONFUSION PART ONE.osz", + "933984 onumi - PERSONALITY.osz", + "934785 onumi - FAKE.osz", + "936545 onumi - REGRET PART ONE.osz", + "943803 Fractal Dreamers - Everything for a Dream.osz", + "943876 S3RL - I Will Pick You Up (feat. Tamika).osz", + "946773 Trial & Error - DREAMING COLOR (Short Ver.).osz", + "955808 Trial & Error - Tokoyami no keiyaku KEGARETA-SHOUJO feat. GUMI (Short Ver.).osz", + "957808 Fractal Dreamers - Module_410.osz", + "957842 antiPLUR - One Life Left to Live.osz", + "965730 The Flashbulb - Lawn Wake IV (Black).osz", + "966240 Creo - Challenger.osz", + "968232 Rin - Lunatic set 15 ~ The Moon as Seen from the Shrine.osz", + "972302 VINXIS - A Centralized View.osz", + "972887 HyuN - Illusion of Inflict.osz", + "1008600 LukHash - WHEN AN ANGEL DIES.osz", + "1032103 LukHash - H8 U.osz", + "943516 antiPLUR - Clockwork Spooks.osz", + "946394 VINXIS - Three Times The Original Charm.osz", + "966408 antiPLUR - One Life Left to Live.osz", + "971561 antiPLUR - Runengon.osz", + "983864 James Landino - Shiba Island.osz", + "989512 BilliumMoto - 1xMISS.osz", + "994104 James Landino - Reaction feat. Slyleaf.osz", + "1003217 nekodex - circles!.osz", + "1009907 James Landino & Kabuki - Birdsong.osz", + "1015169 Thaehan - Insert Coin.osz", + }; + } +} From b042f1cad55680e61d4656522b4b4cb500a9e05a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 18:57:20 +0900 Subject: [PATCH 39/63] Add screen to perform bundled beatmap downloads --- .../FirstRunSetup/ScreenBundledBeatmaps.cs | 47 +++++++++++++++++++ osu.Game/Overlays/FirstRunSetupOverlay.cs | 1 + 2 files changed, 48 insertions(+) create mode 100644 osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs new file mode 100644 index 0000000000..fae852860b --- /dev/null +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.ComponentModel; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.FirstRunSetup +{ + [Description("Bundled Beatmaps")] + public class ScreenBundledBeatmaps : FirstRunSetupScreen + { + private TriangleButton downloadButton; + + [BackgroundDependencyLoader] + private void load() + { + Content.Children = new Drawable[] + { + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + { + Text = "osu! doesn't come with any beatmaps pre-loaded. To get started, we have some recommended beatmaps.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + downloadButton = new TriangleButton + { + Width = 300, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Download beatmap selection", + Action = download + } + }; + } + + private void download() + { + AddInternal(new BundledBeatmapDownloader()); + downloadButton.Enabled.Value = false; + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 82158793a2..b43f14522e 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -60,6 +60,7 @@ namespace osu.Game.Overlays private readonly Type[] steps = { typeof(ScreenWelcome), + typeof(ScreenBundledBeatmaps), typeof(ScreenUIScale) }; From 973dd4bfa9eab3d7d7ee934ed4b02df0a6c940ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 19:41:13 +0900 Subject: [PATCH 40/63] Add initial flow for download button / progress display --- .../Drawables/BundledBeatmapDownloader.cs | 51 ++++++++++++++++--- .../FirstRunSetup/ScreenBundledBeatmaps.cs | 29 ++++++++++- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 6b14ed760d..5937186705 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -1,37 +1,55 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; +using osu.Game.Database; +using osu.Game.Online; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; namespace osu.Game.Beatmaps.Drawables { public class BundledBeatmapDownloader : CompositeDrawable { - [Resolved] - private BeatmapModelDownloader beatmapDownloader { get; set; } + public IEnumerable DownloadTrackers => downloadTrackers; + + private readonly List downloadTrackers = new List(); [BackgroundDependencyLoader] - private void load() + private void load(BeatmapManager beatmapManager, IAPIProvider api, INotificationOverlay notifications) { + var beatmapDownloader = new BundledBeatmapModelDownloader(beatmapManager, api) + { + PostNotification = notifications.Post + }; + foreach (string filename in bundled_beatmap_filenames.OrderBy(_ => RNG.NextSingle()).Take(10)) { var match = Regex.Match(filename, @"([0-9]*) (.*) - (.*)\.osz"); - beatmapDownloader.Download(new APIBeatmapSet + var beatmapSet = new APIBeatmapSet { OnlineID = int.Parse(match.Groups[1].Value), Artist = match.Groups[2].Value, Title = match.Groups[3].Value, - }); + }; + + var beatmapDownloadTracker = new BeatmapDownloadTracker(beatmapSet); + downloadTrackers.Add(beatmapDownloadTracker); + AddInternal(beatmapDownloadTracker); + + beatmapDownloader.Download(beatmapSet); } } - private static readonly string[] bundled_beatmap_filenames = new[] + private static readonly string[] bundled_beatmap_filenames = { "682286 Yuyoyuppe - Emerald Galaxy.osz", "682287 baker - For a Dead Girl+.osz", @@ -223,5 +241,26 @@ namespace osu.Game.Beatmaps.Drawables "1009907 James Landino & Kabuki - Birdsong.osz", "1015169 Thaehan - Insert Coin.osz", }; + + private class BundledBeatmapModelDownloader : BeatmapModelDownloader + { + public BundledBeatmapModelDownloader(IModelImporter beatmapImporter, IAPIProvider api) + : base(beatmapImporter, api) + { + } + + protected override ArchiveDownloadRequest CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize) + => new BundledBeatmapDownloadRequest(set, minimiseDownloadSize); + + public class BundledBeatmapDownloadRequest : DownloadBeatmapSetRequest + { + protected override string Uri => $"https://assets.ppy.sh/client-resources/bundled/{Model.OnlineID}.osz"; + + public BundledBeatmapDownloadRequest(IBeatmapSetInfo beatmapSetInfo, bool minimiseDownloadSize) + : base(beatmapSetInfo, minimiseDownloadSize) + { + } + } + } } } diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs index fae852860b..97f429bcf2 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs @@ -2,12 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using System.ComponentModel; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osuTK.Graphics; namespace osu.Game.Overlays.FirstRunSetup { @@ -16,6 +19,9 @@ namespace osu.Game.Overlays.FirstRunSetup { private TriangleButton downloadButton; + private ProgressBar progressBar; + private BundledBeatmapDownloader downloader; + [BackgroundDependencyLoader] private void load() { @@ -34,14 +40,33 @@ namespace osu.Game.Overlays.FirstRunSetup Origin = Anchor.TopCentre, Text = "Download beatmap selection", Action = download - } + }, }; + + downloadButton.Add(progressBar = new ProgressBar(false) + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + FillColour = Color4.Aqua, + Alpha = 0.5f, + Depth = float.MinValue + }); } private void download() { - AddInternal(new BundledBeatmapDownloader()); + AddInternal(downloader = new BundledBeatmapDownloader()); downloadButton.Enabled.Value = false; + + foreach (var tracker in downloader.DownloadTrackers) + tracker.State.BindValueChanged(_ => updateProgress()); + } + + private void updateProgress() + { + double progress = (double)downloader.DownloadTrackers.Count(t => t.State.Value == DownloadState.LocallyAvailable) / downloader.DownloadTrackers.Count(); + + this.TransformBindableTo(progressBar.Current, progress, 1000, Easing.OutQuint); } } } From b424d20f267b26104eca245cf1f3de926f29849b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 20:18:46 +0900 Subject: [PATCH 41/63] Fix rounded buttons not allowing custom colour specifications --- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index 23ebc6e98d..f535a32b39 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; +using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -28,7 +29,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 [BackgroundDependencyLoader(true)] private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) { - BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; + if (BackgroundColour == Color4.White) + BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; } protected override void LoadComplete() From 58399a5113e9bc5906e529672fc917b19a33a187 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Apr 2022 20:19:11 +0900 Subject: [PATCH 42/63] Add tutorial download support and improve the visuals "slightly" --- .../TestSceneBundledBeatmapDownloader.cs | 2 +- .../Drawables/BundledBeatmapDownloader.cs | 37 +++++-- .../FirstRunSetup/ScreenBundledBeatmaps.cs | 98 +++++++++++++++---- 3 files changed, 108 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs b/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs index 895524e79a..2af1c9a0f0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBundledBeatmapDownloader.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Create downloader", () => { downloader?.Expire(); - Add(downloader = new BundledBeatmapDownloader()); + Add(downloader = new BundledBeatmapDownloader(false)); }); } } diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 5937186705..0bdaaaca22 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -22,15 +22,34 @@ namespace osu.Game.Beatmaps.Drawables private readonly List downloadTrackers = new List(); - [BackgroundDependencyLoader] - private void load(BeatmapManager beatmapManager, IAPIProvider api, INotificationOverlay notifications) - { - var beatmapDownloader = new BundledBeatmapModelDownloader(beatmapManager, api) - { - PostNotification = notifications.Post - }; + private readonly List downloadableFilenames = new List(); - foreach (string filename in bundled_beatmap_filenames.OrderBy(_ => RNG.NextSingle()).Take(10)) + private BundledBeatmapModelDownloader beatmapDownloader; + + public BundledBeatmapDownloader(bool onlyTutorial) + { + if (onlyTutorial) + downloadableFilenames.Add(tutorial_filename); + else + downloadableFilenames.AddRange(bundled_beatmap_filenames); + } + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var localDependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + localDependencies.CacheAs(beatmapDownloader = new BundledBeatmapModelDownloader(parent.Get(), parent.Get()) + { + PostNotification = parent.Get().Post + }); + + return localDependencies; + } + + [BackgroundDependencyLoader] + private void load() + { + foreach (string filename in downloadableFilenames.OrderBy(_ => RNG.NextSingle()).Take(10)) { var match = Regex.Match(filename, @"([0-9]*) (.*) - (.*)\.osz"); @@ -49,6 +68,8 @@ namespace osu.Game.Beatmaps.Drawables } } + private const string tutorial_filename = "1011011 nekodex - new beginnings.osz"; + private static readonly string[] bundled_beatmap_filenames = { "682286 Yuyoyuppe - Emerald Galaxy.osz", diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs index 97f429bcf2..518903e74b 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs @@ -9,64 +9,122 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Online; -using osuTK.Graphics; +using osuTK; namespace osu.Game.Overlays.FirstRunSetup { [Description("Bundled Beatmaps")] public class ScreenBundledBeatmaps : FirstRunSetupScreen { - private TriangleButton downloadButton; + private RoundedButton downloadBundledButton; - private ProgressBar progressBar; - private BundledBeatmapDownloader downloader; + private ProgressBar progressBarBundled; + + private RoundedButton downloadTutorialButton; + private ProgressBar progressBarTutorial; + + private BundledBeatmapDownloader tutorialDownloader; + private BundledBeatmapDownloader bundledDownloader; [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { + Vector2 buttonSize = new Vector2(500, 80); + Content.Children = new Drawable[] { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Text = "osu! doesn't come with any beatmaps pre-loaded. To get started, we have some recommended beatmaps.", + Text = + "osu! doesn't come with any beatmaps pre-loaded. To get started, we have some recommended beatmaps. You can obtain more beatmaps from the main menu \"browse\" button at any time.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, - downloadButton = new TriangleButton + downloadTutorialButton = new RoundedButton { - Width = 300, + Size = buttonSize, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = "Download beatmap selection", - Action = download + BackgroundColour = colours.Pink3, + Text = "Download tutorial", + Action = downloadTutorial }, + downloadBundledButton = new RoundedButton + { + Size = buttonSize, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + BackgroundColour = colours.Blue3, + Text = "Download beatmap selection", + Action = downloadBundled + }, + // TODO: add stable import button if a stable install is detected. }; - downloadButton.Add(progressBar = new ProgressBar(false) + downloadTutorialButton.Add(progressBarTutorial = new ProgressBar(false) { RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, - FillColour = Color4.Aqua, + FillColour = downloadTutorialButton.BackgroundColour, + Alpha = 0.5f, + Depth = float.MinValue + }); + + downloadBundledButton.Add(progressBarBundled = new ProgressBar(false) + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + FillColour = downloadBundledButton.BackgroundColour, Alpha = 0.5f, Depth = float.MinValue }); } - private void download() + private void downloadTutorial() { - AddInternal(downloader = new BundledBeatmapDownloader()); - downloadButton.Enabled.Value = false; + if (tutorialDownloader != null) + return; - foreach (var tracker in downloader.DownloadTrackers) - tracker.State.BindValueChanged(_ => updateProgress()); + tutorialDownloader = new BundledBeatmapDownloader(true); + + AddInternal(tutorialDownloader); + + var downloadTracker = tutorialDownloader.DownloadTrackers.First(); + + downloadTracker.Progress.BindValueChanged(progress => + { + progressBarTutorial.Current.Value = progress.NewValue; + + if (progress.NewValue == 1) + downloadTutorialButton.Enabled.Value = false; + }, true); } - private void updateProgress() + private void downloadBundled() { - double progress = (double)downloader.DownloadTrackers.Count(t => t.State.Value == DownloadState.LocallyAvailable) / downloader.DownloadTrackers.Count(); + if (bundledDownloader != null) + return; - this.TransformBindableTo(progressBar.Current, progress, 1000, Easing.OutQuint); + // downloadBundledButton.Enabled.Value = false; + + bundledDownloader = new BundledBeatmapDownloader(false); + + AddInternal(bundledDownloader); + + foreach (var tracker in bundledDownloader.DownloadTrackers) + tracker.State.BindValueChanged(_ => updateProgress(), true); + + void updateProgress() + { + double progress = (double)bundledDownloader.DownloadTrackers.Count(t => t.State.Value == DownloadState.LocallyAvailable) / bundledDownloader.DownloadTrackers.Count(); + + this.TransformBindableTo(progressBarBundled.Current, progress, 1000, Easing.OutQuint); + + if (progress == 1) + downloadBundledButton.Enabled.Value = false; + } } } } From d0564657423632e9ae41978b085f85682054ba6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 16:08:32 +0900 Subject: [PATCH 43/63] Remove explicit dependency on `INotificationOverlay` --- osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 0bdaaaca22..029bb37c18 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -38,10 +38,10 @@ namespace osu.Game.Beatmaps.Drawables { var localDependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - localDependencies.CacheAs(beatmapDownloader = new BundledBeatmapModelDownloader(parent.Get(), parent.Get()) - { - PostNotification = parent.Get().Post - }); + localDependencies.CacheAs(beatmapDownloader = new BundledBeatmapModelDownloader(parent.Get(), parent.Get())); + + if (parent.Get() is INotificationOverlay notifications) + beatmapDownloader.PostNotification = notifications.Post; return localDependencies; } From 3c0bdcaf3804717eb827bdbbcf579c6e8345143a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 16:09:00 +0900 Subject: [PATCH 44/63] Rename screen, add tests and add stable import step --- .../TestSceneFirstRunScreenBundledBeatmaps.cs | 24 ++++++ ...enBundledBeatmaps.cs => ScreenBeatmaps.cs} | 81 ++++++++++++++----- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 3 files changed, 84 insertions(+), 23 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBundledBeatmaps.cs rename osu.Game/Overlays/FirstRunSetup/{ScreenBundledBeatmaps.cs => ScreenBeatmaps.cs} (52%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBundledBeatmaps.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBundledBeatmaps.cs new file mode 100644 index 0000000000..51065939f0 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunScreenBundledBeatmaps.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Screens; +using osu.Game.Overlays; +using osu.Game.Overlays.FirstRunSetup; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneFirstRunScreenBundledBeatmaps : OsuManualInputManagerTestScene + { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + public TestSceneFirstRunScreenBundledBeatmaps() + { + AddStep("load screen", () => + { + Child = new ScreenStack(new ScreenBeatmaps()); + }); + } + } +} diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs similarity index 52% rename from osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs rename to osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 518903e74b..aff084be73 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBundledBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -1,3 +1,4 @@ +#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. @@ -6,39 +7,51 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps.Drawables; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Localisation; using osu.Game.Online; using osuTK; namespace osu.Game.Overlays.FirstRunSetup { - [Description("Bundled Beatmaps")] - public class ScreenBundledBeatmaps : FirstRunSetupScreen + [Description("Obtaining Beatmaps")] + public class ScreenBeatmaps : FirstRunSetupScreen { - private RoundedButton downloadBundledButton; + private RoundedButton downloadBundledButton = null!; + private RoundedButton importBeatmapsButton = null!; - private ProgressBar progressBarBundled; + private ProgressBar progressBarBundled = null!; - private RoundedButton downloadTutorialButton; - private ProgressBar progressBarTutorial; + private RoundedButton downloadTutorialButton = null!; + private ProgressBar progressBarTutorial = null!; - private BundledBeatmapDownloader tutorialDownloader; - private BundledBeatmapDownloader bundledDownloader; + private BundledBeatmapDownloader tutorialDownloader = null!; + private BundledBeatmapDownloader bundledDownloader = null!; - [BackgroundDependencyLoader] - private void load(OsuColour colours) + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuColour colours, OverlayColourProvider overlayColourProvider, LegacyImportManager? legacyImportManager) { - Vector2 buttonSize = new Vector2(500, 80); + Vector2 buttonSize = new Vector2(500, 60); Content.Children = new Drawable[] { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { + Colour = overlayColourProvider.Content1, Text = - "osu! doesn't come with any beatmaps pre-loaded. To get started, we have some recommended beatmaps. You can obtain more beatmaps from the main menu \"browse\" button at any time.", + "\"Beatmaps\" are what we call playable levels in osu!.\n\nosu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + { + Colour = overlayColourProvider.Content1, + Text = + "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -51,6 +64,13 @@ namespace osu.Game.Overlays.FirstRunSetup Text = "Download tutorial", Action = downloadTutorial }, + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + { + Colour = overlayColourProvider.Content1, + Text = "To get you started, we have some recommended beatmaps.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, downloadBundledButton = new RoundedButton { Size = buttonSize, @@ -60,9 +80,34 @@ namespace osu.Game.Overlays.FirstRunSetup Text = "Download beatmap selection", Action = downloadBundled }, - // TODO: add stable import button if a stable install is detected. + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + { + Colour = overlayColourProvider.Content1, + Text = "If you have an existing osu! install, you can also choose to import your existing beatmap collection.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, + importBeatmapsButton = new RoundedButton + { + Size = buttonSize, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + BackgroundColour = colours.Blue3, + Text = MaintenanceSettingsStrings.ImportBeatmapsFromStable, + Action = () => + { + importBeatmapsButton.Enabled.Value = false; + legacyImportManager?.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => importBeatmapsButton.Enabled.Value = true)); + } + }, + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + { + Colour = overlayColourProvider.Content1, + Text = "You can also obtain more beatmaps from the main menu \"browse\" button at any time.", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, }; - downloadTutorialButton.Add(progressBarTutorial = new ProgressBar(false) { RelativeSizeAxes = Axes.Both, @@ -84,9 +129,6 @@ namespace osu.Game.Overlays.FirstRunSetup private void downloadTutorial() { - if (tutorialDownloader != null) - return; - tutorialDownloader = new BundledBeatmapDownloader(true); AddInternal(tutorialDownloader); @@ -104,11 +146,6 @@ namespace osu.Game.Overlays.FirstRunSetup private void downloadBundled() { - if (bundledDownloader != null) - return; - - // downloadBundledButton.Enabled.Value = false; - bundledDownloader = new BundledBeatmapDownloader(false); AddInternal(bundledDownloader); diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index b43f14522e..c151554081 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays private readonly Type[] steps = { typeof(ScreenWelcome), - typeof(ScreenBundledBeatmaps), + typeof(ScreenBeatmaps), typeof(ScreenUIScale) }; From 081275e0b996b5198c116ebb2903d0ef9898e0e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 16:36:25 +0900 Subject: [PATCH 45/63] Adjust text and layout --- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 6 +++--- osu.Game/Overlays/FirstRunSetupOverlay.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index aff084be73..b1e2394f89 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.FirstRunSetup { Colour = overlayColourProvider.Content1, Text = - "\"Beatmaps\" are what we call playable levels in osu!.\n\nosu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.", + "\"Beatmaps\" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.FirstRunSetup Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, BackgroundColour = colours.Pink3, - Text = "Download tutorial", + Text = "Get the osu! tutorial", Action = downloadTutorial }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.FirstRunSetup Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, BackgroundColour = colours.Blue3, - Text = "Download beatmap selection", + Text = "Get recommended beatmaps", Action = downloadBundled }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index c151554081..13fb73bd27 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 50 }, + Padding = new MarginPadding { Horizontal = 70 * 2 }, Child = new InputBlockingContainer { Masking = true, @@ -104,7 +104,7 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Vertical = 20, - Horizontal = 20, + Horizontal = 70, }, } }, From c860eb46b0274e28a32aff95c3935c37290fec8f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 17:11:29 +0900 Subject: [PATCH 46/63] Split out button implementation into its own class --- .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 97 ++++++++++++------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index b1e2394f89..893c940a1d 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; @@ -21,19 +22,18 @@ namespace osu.Game.Overlays.FirstRunSetup [Description("Obtaining Beatmaps")] public class ScreenBeatmaps : FirstRunSetupScreen { - private RoundedButton downloadBundledButton = null!; - private RoundedButton importBeatmapsButton = null!; + private ProgressRoundedButton downloadBundledButton = null!; + private ProgressRoundedButton importBeatmapsButton = null!; + private ProgressRoundedButton downloadTutorialButton = null!; - private ProgressBar progressBarBundled = null!; + private BundledBeatmapDownloader? tutorialDownloader; + private BundledBeatmapDownloader? bundledDownloader; - private RoundedButton downloadTutorialButton = null!; - private ProgressBar progressBarTutorial = null!; - - private BundledBeatmapDownloader tutorialDownloader = null!; - private BundledBeatmapDownloader bundledDownloader = null!; + [Resolved] + private OsuColour colours { get; set; } = null!; [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, OverlayColourProvider overlayColourProvider, LegacyImportManager? legacyImportManager) + private void load(OverlayColourProvider overlayColourProvider, LegacyImportManager? legacyImportManager) { Vector2 buttonSize = new Vector2(500, 60); @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, - downloadTutorialButton = new RoundedButton + downloadTutorialButton = new ProgressRoundedButton { Size = buttonSize, Anchor = Anchor.TopCentre, @@ -71,7 +71,7 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, - downloadBundledButton = new RoundedButton + downloadBundledButton = new ProgressRoundedButton { Size = buttonSize, Anchor = Anchor.TopCentre, @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.FirstRunSetup RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, - importBeatmapsButton = new RoundedButton + importBeatmapsButton = new ProgressRoundedButton { Size = buttonSize, Anchor = Anchor.TopCentre, @@ -97,7 +97,13 @@ namespace osu.Game.Overlays.FirstRunSetup Action = () => { importBeatmapsButton.Enabled.Value = false; - legacyImportManager?.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => importBeatmapsButton.Enabled.Value = true)); + legacyImportManager?.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => + { + // TODO: can we know if the import was successful? + // if so we should turn the button green and disable it in that case alone. + // importBeatmapsButton.Enabled.Value = true; + importBeatmapsButton.Complete(); + })); } }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) @@ -108,27 +114,13 @@ namespace osu.Game.Overlays.FirstRunSetup AutoSizeAxes = Axes.Y }, }; - downloadTutorialButton.Add(progressBarTutorial = new ProgressBar(false) - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, - FillColour = downloadTutorialButton.BackgroundColour, - Alpha = 0.5f, - Depth = float.MinValue - }); - - downloadBundledButton.Add(progressBarBundled = new ProgressBar(false) - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, - FillColour = downloadBundledButton.BackgroundColour, - Alpha = 0.5f, - Depth = float.MinValue - }); } private void downloadTutorial() { + if (tutorialDownloader != null) + return; + tutorialDownloader = new BundledBeatmapDownloader(true); AddInternal(tutorialDownloader); @@ -137,15 +129,18 @@ namespace osu.Game.Overlays.FirstRunSetup downloadTracker.Progress.BindValueChanged(progress => { - progressBarTutorial.Current.Value = progress.NewValue; + downloadTutorialButton.Current.Value = progress.NewValue; if (progress.NewValue == 1) - downloadTutorialButton.Enabled.Value = false; + downloadTutorialButton.Complete(); }, true); } private void downloadBundled() { + if (bundledDownloader != null) + return; + bundledDownloader = new BundledBeatmapDownloader(false); AddInternal(bundledDownloader); @@ -157,10 +152,44 @@ namespace osu.Game.Overlays.FirstRunSetup { double progress = (double)bundledDownloader.DownloadTrackers.Count(t => t.State.Value == DownloadState.LocallyAvailable) / bundledDownloader.DownloadTrackers.Count(); - this.TransformBindableTo(progressBarBundled.Current, progress, 1000, Easing.OutQuint); + this.TransformBindableTo(downloadBundledButton.Current, progress, 2000, Easing.OutQuint); if (progress == 1) - downloadBundledButton.Enabled.Value = false; + downloadBundledButton.Complete(); + } + } + + private class ProgressRoundedButton : RoundedButton + { + [Resolved] + private OsuColour colours { get; set; } = null!; + + private ProgressBar progressBar = null!; + + public Bindable Current => progressBar.Current; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(progressBar = new ProgressBar(false) + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + FillColour = BackgroundColour, + Alpha = 0.5f, + Depth = float.MinValue + }); + } + + public void Complete() + { + Enabled.Value = false; + + Background.FadeColour(colours.Green, 500, Easing.OutQuint); + progressBar.FillColour = colours.Green; + + this.TransformBindableTo(Current, 1, 500, Easing.OutQuint); } } } From 18a38f7e23786e2e83de7a9c6478e440b64fe8aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 17:46:00 +0900 Subject: [PATCH 47/63] Move `FastRandom` to `LegacyRandom` in `osu.Game` project --- .../Beatmaps/CatchBeatmapProcessor.cs | 8 +- .../Beatmaps/ManiaBeatmapConverter.cs | 8 +- .../Legacy/DistanceObjectPatternGenerator.cs | 4 +- .../Legacy/EndTimeObjectPatternGenerator.cs | 4 +- .../Legacy/HitObjectPatternGenerator.cs | 4 +- .../Patterns/Legacy/PatternGenerator.cs | 6 +- .../MathUtils/FastRandom.cs | 95 ------------------- .../Utils/LegacyRandom.cs | 37 +++++--- 8 files changed, 40 insertions(+), 126 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs rename osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs => osu.Game/Utils/LegacyRandom.cs (79%) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 346a09cac8..ab61b14ac4 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.MathUtils; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Utils; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps public void ApplyPositionOffsets(IBeatmap beatmap) { - var rng = new FastRandom(RNG_SEED); + var rng = new LegacyRandom(RNG_SEED); float? lastPosition = null; double lastStartTime = 0; @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps initialiseHyperDash(beatmap); } - private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) + private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, LegacyRandom rng) { float offsetPosition = hitObject.OriginalX; double startTime = hitObject.StartTime; @@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps /// The position which the offset should be applied to. /// The maximum offset, cannot exceed 20px. /// The random number generator. - private static void applyRandomOffset(ref float position, double maxOffset, FastRandom rng) + private static void applyRandomOffset(ref float position, double maxOffset, LegacyRandom rng) { bool right = rng.NextBool(); float rand = Math.Min(20, (float)rng.Next(0, Math.Max(0, maxOffset))); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 47e0e6d7b1..207c6907c8 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -11,8 +11,8 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Beatmaps.Patterns; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; +using osu.Game.Utils; using osuTK; namespace osu.Game.Rulesets.Mania.Beatmaps @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private readonly int originalTargetColumns; // Internal for testing purposes - internal FastRandom Random { get; private set; } + internal LegacyRandom Random { get; private set; } private Pattern lastPattern = new Pattern(); @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps IBeatmapDifficultyInfo difficulty = original.Difficulty; int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate); - Random = new FastRandom(seed); + Random = new LegacyRandom(seed); return base.ConvertBeatmap(original, cancellationToken); } @@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator { - public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public SpecificBeatmapPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 5f8b58d94d..dafe65f415 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -8,12 +8,12 @@ using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Game.Audio; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private PatternType convertType; - public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public DistanceObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { convertType = PatternType.None; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index f816a70ab3..2265d3d347 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -2,13 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using System.Linq; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly int endTime; private readonly PatternType convertType; - public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public EndTimeObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { endTime = (int)((HitObject as IHasDuration)?.EndTime ?? 0); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 53b059b4e2..41d4c9322b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -9,11 +9,11 @@ using osuTK; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly PatternType convertType; - public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, + public HitObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index eaf0ea0f2b..d5689c047a 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -5,8 +5,8 @@ using System; using System.Linq; using JetBrains.Annotations; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { @@ -23,14 +23,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// The random number generator to use. /// - protected readonly FastRandom Random; + protected readonly LegacyRandom Random; /// /// The beatmap which is being converted from. /// protected readonly IBeatmap OriginalBeatmap; - protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + protected PatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(hitObject, beatmap, previousPattern) { if (random == null) throw new ArgumentNullException(nameof(random)); diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs deleted file mode 100644 index a9cd7f2476..0000000000 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; - -namespace osu.Game.Rulesets.Mania.MathUtils -{ - /// - /// A PRNG specified in http://heliosphan.org/fastrandom.html. - /// - internal class FastRandom - { - private const double int_to_real = 1.0 / (int.MaxValue + 1.0); - private const uint int_mask = 0x7FFFFFFF; - private const uint y = 842502087; - private const uint z = 3579807591; - private const uint w = 273326509; - - internal uint X { get; private set; } - internal uint Y { get; private set; } = y; - internal uint Z { get; private set; } = z; - internal uint W { get; private set; } = w; - - public FastRandom(int seed) - { - X = (uint)seed; - } - - public FastRandom() - : this(Environment.TickCount) - { - } - - /// - /// Generates a random unsigned integer within the range [, ). - /// - /// The random value. - public uint NextUInt() - { - uint t = X ^ (X << 11); - X = Y; - Y = Z; - Z = W; - return W = W ^ (W >> 19) ^ t ^ (t >> 8); - } - - /// - /// Generates a random integer value within the range [0, ). - /// - /// The random value. - public int Next() => (int)(int_mask & NextUInt()); - - /// - /// Generates a random integer value within the range [0, ). - /// - /// The upper bound. - /// The random value. - public int Next(int upperBound) => (int)(NextDouble() * upperBound); - - /// - /// Generates a random integer value within the range [, ). - /// - /// The lower bound of the range. - /// The upper bound of the range. - /// The random value. - public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); - - /// - /// Generates a random double value within the range [0, 1). - /// - /// The random value. - public double NextDouble() => int_to_real * Next(); - - private uint bitBuffer; - private int bitIndex = 32; - - /// - /// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls. - /// - /// The random value. - public bool NextBool() - { - if (bitIndex == 32) - { - bitBuffer = NextUInt(); - bitIndex = 1; - - return (bitBuffer & 1) == 1; - } - - bitIndex++; - return ((bitBuffer >>= 1) & 1) == 1; - } - } -} diff --git a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs b/osu.Game/Utils/LegacyRandom.cs similarity index 79% rename from osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs rename to osu.Game/Utils/LegacyRandom.cs index 46e427e1b7..cf731aa91f 100644 --- a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs +++ b/osu.Game/Utils/LegacyRandom.cs @@ -2,27 +2,36 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Utils; -namespace osu.Game.Rulesets.Catch.MathUtils +namespace osu.Game.Utils { /// /// A PRNG specified in http://heliosphan.org/fastrandom.html. + /// Should only be used to match legacy behaviour. See for a newer alternative. /// - public class FastRandom + /// + /// Known in osu-stable code as `FastRandom`. + /// + public class LegacyRandom { private const double int_to_real = 1.0 / (int.MaxValue + 1.0); private const uint int_mask = 0x7FFFFFFF; - private const uint y_initial = 842502087; - private const uint z_initial = 3579807591; - private const uint w_initial = 273326509; - private uint x, y = y_initial, z = z_initial, w = w_initial; + private const uint y = 842502087; + private const uint z = 3579807591; + private const uint w = 273326509; - public FastRandom(int seed) + public uint X { get; private set; } + public uint Y { get; private set; } = y; + public uint Z { get; private set; } = z; + public uint W { get; private set; } = w; + + public LegacyRandom(int seed) { - x = (uint)seed; + X = (uint)seed; } - public FastRandom() + public LegacyRandom() : this(Environment.TickCount) { } @@ -33,11 +42,11 @@ namespace osu.Game.Rulesets.Catch.MathUtils /// The random value. public uint NextUInt() { - uint t = x ^ (x << 11); - x = y; - y = z; - z = w; - return w = w ^ (w >> 19) ^ t ^ (t >> 8); + uint t = X ^ (X << 11); + X = Y; + Y = Z; + Z = W; + return W = W ^ (W >> 19) ^ t ^ (t >> 8); } /// From 3c9e52018834ab2409758e57fae5d0fa3a71f548 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 17:24:47 +0900 Subject: [PATCH 48/63] Update bundle logic to match stable --- .../Drawables/BundledBeatmapDownloader.cs | 84 +++++++++++++------ 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 029bb37c18..5b81022cbf 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -1,18 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; -using osu.Framework.Utils; using osu.Game.Database; using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; +using osu.Game.Utils; namespace osu.Game.Beatmaps.Drawables { @@ -29,9 +30,16 @@ namespace osu.Game.Beatmaps.Drawables public BundledBeatmapDownloader(bool onlyTutorial) { if (onlyTutorial) - downloadableFilenames.Add(tutorial_filename); + { + queueDownloads(new[] { tutorial_filename }); + } else - downloadableFilenames.AddRange(bundled_beatmap_filenames); + { + queueDownloads(bundled_osu, 8); + queueDownloads(bundled_taiko, 3); + queueDownloads(bundled_catch, 3); + queueDownloads(bundled_mania, 3); + } } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -49,7 +57,7 @@ namespace osu.Game.Beatmaps.Drawables [BackgroundDependencyLoader] private void load() { - foreach (string filename in downloadableFilenames.OrderBy(_ => RNG.NextSingle()).Take(10)) + foreach (string filename in downloadableFilenames) { var match = Regex.Match(filename, @"([0-9]*) (.*) - (.*)\.osz"); @@ -68,9 +76,42 @@ namespace osu.Game.Beatmaps.Drawables } } + private void queueDownloads(string[] sourceFilenames, int? limit = null) + { + try + { + // Matches osu-stable, in order to provide new users with roughly the same randomised selection of bundled beatmaps. + var random = new LegacyRandom(DateTime.UtcNow.Year * 1000 + (DateTime.UtcNow.DayOfYear / 7)); + + downloadableFilenames.AddRange(sourceFilenames.OrderBy(x => random.NextDouble()).Take(limit ?? int.MaxValue)); + } + catch { } + } + + private class BundledBeatmapModelDownloader : BeatmapModelDownloader + { + public BundledBeatmapModelDownloader(IModelImporter beatmapImporter, IAPIProvider api) + : base(beatmapImporter, api) + { + } + + protected override ArchiveDownloadRequest CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize) + => new BundledBeatmapDownloadRequest(set, minimiseDownloadSize); + + public class BundledBeatmapDownloadRequest : DownloadBeatmapSetRequest + { + protected override string Uri => $"https://assets.ppy.sh/client-resources/bundled/{Model.OnlineID}.osz"; + + public BundledBeatmapDownloadRequest(IBeatmapSetInfo beatmapSetInfo, bool minimiseDownloadSize) + : base(beatmapSetInfo, minimiseDownloadSize) + { + } + } + } + private const string tutorial_filename = "1011011 nekodex - new beginnings.osz"; - private static readonly string[] bundled_beatmap_filenames = + private static readonly string[] bundled_osu = { "682286 Yuyoyuppe - Emerald Galaxy.osz", "682287 baker - For a Dead Girl+.osz", @@ -180,6 +221,10 @@ namespace osu.Game.Beatmaps.Drawables "1019827 UNDEAD CORPORATION - Sad Dream.osz", "1020213 Creo - Idolize.osz", "1021450 Thaehan - Chiptune & Baroque.osz", + }; + + private static readonly string[] bundled_taiko = + { "707824 Fractal Dreamers - Fortuna Redux.osz", "789553 Cranky - Ran.osz", "827822 Function Phantom - Neuronecia.osz", @@ -227,6 +272,10 @@ namespace osu.Game.Beatmaps.Drawables "1023681 Inferi - The Ruin of Mankind.osz", "1034358 ALEPH - The Evil Spirit.osz", "1037567 ALEPH - Scintillations.osz", + }; + + private static readonly string[] bundled_catch = + { "554256 Helblinde - When Time Sleeps.osz", "693123 yuki. - Nadeshiko Sensation.osz", "767009 OISHII - PIZZA PLAZA.osz", @@ -251,6 +300,10 @@ namespace osu.Game.Beatmaps.Drawables "972887 HyuN - Illusion of Inflict.osz", "1008600 LukHash - WHEN AN ANGEL DIES.osz", "1032103 LukHash - H8 U.osz", + }; + + private static readonly string[] bundled_mania = + { "943516 antiPLUR - Clockwork Spooks.osz", "946394 VINXIS - Three Times The Original Charm.osz", "966408 antiPLUR - One Life Left to Live.osz", @@ -262,26 +315,5 @@ namespace osu.Game.Beatmaps.Drawables "1009907 James Landino & Kabuki - Birdsong.osz", "1015169 Thaehan - Insert Coin.osz", }; - - private class BundledBeatmapModelDownloader : BeatmapModelDownloader - { - public BundledBeatmapModelDownloader(IModelImporter beatmapImporter, IAPIProvider api) - : base(beatmapImporter, api) - { - } - - protected override ArchiveDownloadRequest CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize) - => new BundledBeatmapDownloadRequest(set, minimiseDownloadSize); - - public class BundledBeatmapDownloadRequest : DownloadBeatmapSetRequest - { - protected override string Uri => $"https://assets.ppy.sh/client-resources/bundled/{Model.OnlineID}.osz"; - - public BundledBeatmapDownloadRequest(IBeatmapSetInfo beatmapSetInfo, bool minimiseDownloadSize) - : base(beatmapSetInfo, minimiseDownloadSize) - { - } - } - } } } From 9f6597695ce4555cbaa12df3d4705534d05f928e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 18:17:29 +0900 Subject: [PATCH 49/63] Only mark stable import complete if it actually completes --- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 893c940a1d..abbef52f8f 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -99,10 +99,10 @@ namespace osu.Game.Overlays.FirstRunSetup importBeatmapsButton.Enabled.Value = false; legacyImportManager?.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => { - // TODO: can we know if the import was successful? - // if so we should turn the button green and disable it in that case alone. - // importBeatmapsButton.Enabled.Value = true; - importBeatmapsButton.Complete(); + if (t.IsCompletedSuccessfully) + importBeatmapsButton.Complete(); + else + importBeatmapsButton.Enabled.Value = true; })); } }, From 4e0d70cbf49322e74362c88bba9f4bd5a0eb6e06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 18:26:29 +0900 Subject: [PATCH 50/63] Internalise bindable and progress handling to ensure transforms don't go backwards --- .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index abbef52f8f..e02ba4d3ed 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -5,7 +5,6 @@ using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; @@ -129,7 +128,7 @@ namespace osu.Game.Overlays.FirstRunSetup downloadTracker.Progress.BindValueChanged(progress => { - downloadTutorialButton.Current.Value = progress.NewValue; + downloadTutorialButton.SetProgress(progress.NewValue, false); if (progress.NewValue == 1) downloadTutorialButton.Complete(); @@ -152,10 +151,10 @@ namespace osu.Game.Overlays.FirstRunSetup { double progress = (double)bundledDownloader.DownloadTrackers.Count(t => t.State.Value == DownloadState.LocallyAvailable) / bundledDownloader.DownloadTrackers.Count(); - this.TransformBindableTo(downloadBundledButton.Current, progress, 2000, Easing.OutQuint); - if (progress == 1) downloadBundledButton.Complete(); + else + downloadBundledButton.SetProgress(progress, true); } } @@ -166,8 +165,6 @@ namespace osu.Game.Overlays.FirstRunSetup private ProgressBar progressBar = null!; - public Bindable Current => progressBar.Current; - protected override void LoadComplete() { base.LoadComplete(); @@ -189,7 +186,15 @@ namespace osu.Game.Overlays.FirstRunSetup Background.FadeColour(colours.Green, 500, Easing.OutQuint); progressBar.FillColour = colours.Green; - this.TransformBindableTo(Current, 1, 500, Easing.OutQuint); + this.TransformBindableTo(progressBar.Current, 1, 500, Easing.OutQuint); + } + + public void SetProgress(double progress, bool animated) + { + if (!Enabled.Value) + return; + + this.TransformBindableTo(progressBar.Current, progress, animated ? 500 : 0, Easing.OutQuint); } } } From 88bfd5dece938e0e886dce8f433191cd7fe4b9ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 18:57:32 +0900 Subject: [PATCH 51/63] Show current beatmap count on beatmap screen --- .../FirstRunSetup/FirstRunSetupScreen.cs | 7 ++- .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 63 +++++++++++++++++-- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index 282ba52ddc..d4032306ad 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -19,8 +19,11 @@ namespace osu.Game.Overlays.FirstRunSetup protected FillFlowContainer Content { get; private set; } + [Resolved] + protected OverlayColourProvider OverlayColourProvider { get; private set; } + [BackgroundDependencyLoader] - private void load(OverlayColourProvider overlayColourProvider) + private void load() { const float header_size = 40; const float spacing = 20; @@ -36,7 +39,7 @@ namespace osu.Game.Overlays.FirstRunSetup { Text = this.GetLocalisableDescription(), Font = OsuFont.Default.With(size: header_size), - Colour = overlayColourProvider.Light1, + Colour = OverlayColourProvider.Light1, }, Content = new FillFlowContainer { diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index e02ba4d3ed..fda9fde8b7 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -2,10 +2,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; using osu.Game.Graphics; @@ -15,6 +17,8 @@ using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; using osu.Game.Online; using osuTK; +using Realms; +using Container = osu.Framework.Graphics.Containers.Container; namespace osu.Game.Overlays.FirstRunSetup { @@ -25,14 +29,21 @@ namespace osu.Game.Overlays.FirstRunSetup private ProgressRoundedButton importBeatmapsButton = null!; private ProgressRoundedButton downloadTutorialButton = null!; + private OsuTextFlowContainer currentlyLoadedBeatmaps = null!; + private BundledBeatmapDownloader? tutorialDownloader; private BundledBeatmapDownloader? bundledDownloader; [Resolved] private OsuColour colours { get; set; } = null!; + [Resolved] + private RealmAccess realmAccess { get; set; } = null!; + + private IDisposable? beatmapSubscription; + [BackgroundDependencyLoader(permitNulls: true)] - private void load(OverlayColourProvider overlayColourProvider, LegacyImportManager? legacyImportManager) + private void load(LegacyImportManager? legacyImportManager) { Vector2 buttonSize = new Vector2(500, 60); @@ -40,15 +51,31 @@ namespace osu.Game.Overlays.FirstRunSetup { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Colour = overlayColourProvider.Content1, + Colour = OverlayColourProvider.Content1, Text = "\"Beatmaps\" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, + new Container + { + RelativeSizeAxes = Axes.X, + Height = 30, + Children = new Drawable[] + { + currentlyLoadedBeatmaps = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24, weight: FontWeight.SemiBold)) + { + Colour = OverlayColourProvider.Content2, + TextAnchor = Anchor.Centre, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + }, + } + }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Colour = overlayColourProvider.Content1, + Colour = OverlayColourProvider.Content1, Text = "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay.", RelativeSizeAxes = Axes.X, @@ -65,7 +92,7 @@ namespace osu.Game.Overlays.FirstRunSetup }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Colour = overlayColourProvider.Content1, + Colour = OverlayColourProvider.Content1, Text = "To get you started, we have some recommended beatmaps.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y @@ -81,7 +108,7 @@ namespace osu.Game.Overlays.FirstRunSetup }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Colour = overlayColourProvider.Content1, + Colour = OverlayColourProvider.Content1, Text = "If you have an existing osu! install, you can also choose to import your existing beatmap collection.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y @@ -107,7 +134,7 @@ namespace osu.Game.Overlays.FirstRunSetup }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { - Colour = overlayColourProvider.Content1, + Colour = OverlayColourProvider.Content1, Text = "You can also obtain more beatmaps from the main menu \"browse\" button at any time.", RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y @@ -115,6 +142,30 @@ namespace osu.Game.Overlays.FirstRunSetup }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + beatmapSubscription = realmAccess.RegisterForNotifications(r => r.All().Where(s => !s.DeletePending && !s.Protected), beatmapsChanged); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + beatmapSubscription?.Dispose(); + } + + private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes, Exception error) + { + currentlyLoadedBeatmaps.Text = $"You currently have {sender.Count} beatmap(s) loaded!"; + + if (changes != null && (changes.DeletedIndices.Any() || changes.InsertedIndices.Any())) + { + currentlyLoadedBeatmaps.FadeColour(colours.YellowLight).FadeColour(OverlayColourProvider.Content2); + currentlyLoadedBeatmaps.ScaleTo(1.1f).ScaleTo(1, 1000, Easing.OutQuint); + } + } + private void downloadTutorial() { if (tutorialDownloader != null) From 804848c9fb546aea2634da3e9b45c9eee2d6ad10 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 18:57:41 +0900 Subject: [PATCH 52/63] Allow bundled downloader to not post notifications --- .../Beatmaps/Drawables/BundledBeatmapDownloader.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 5b81022cbf..bc99aa4a5a 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -19,6 +19,8 @@ namespace osu.Game.Beatmaps.Drawables { public class BundledBeatmapDownloader : CompositeDrawable { + private readonly bool shouldPostNotifications; + public IEnumerable DownloadTrackers => downloadTrackers; private readonly List downloadTrackers = new List(); @@ -27,8 +29,15 @@ namespace osu.Game.Beatmaps.Drawables private BundledBeatmapModelDownloader beatmapDownloader; - public BundledBeatmapDownloader(bool onlyTutorial) + /// + /// Construct a new beatmap downloader. + /// + /// Whether only the tutorial should be downloaded, instead of bundled beatmaps. + /// Whether downloads should create tracking notifications. + public BundledBeatmapDownloader(bool onlyTutorial, bool shouldPostNotifications = false) { + this.shouldPostNotifications = shouldPostNotifications; + if (onlyTutorial) { queueDownloads(new[] { tutorial_filename }); @@ -48,7 +57,7 @@ namespace osu.Game.Beatmaps.Drawables localDependencies.CacheAs(beatmapDownloader = new BundledBeatmapModelDownloader(parent.Get(), parent.Get())); - if (parent.Get() is INotificationOverlay notifications) + if (shouldPostNotifications && parent.Get() is INotificationOverlay notifications) beatmapDownloader.PostNotification = notifications.Post; return localDependencies; From b6896376e7db72813bbd8c42a6d9ce9bd26c3a45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 19:08:16 +0900 Subject: [PATCH 53/63] Better signal when no beatmaps are loaded --- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index fda9fde8b7..645713c52a 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -159,10 +159,17 @@ namespace osu.Game.Overlays.FirstRunSetup { currentlyLoadedBeatmaps.Text = $"You currently have {sender.Count} beatmap(s) loaded!"; - if (changes != null && (changes.DeletedIndices.Any() || changes.InsertedIndices.Any())) + if (sender.Count == 0) { - currentlyLoadedBeatmaps.FadeColour(colours.YellowLight).FadeColour(OverlayColourProvider.Content2); - currentlyLoadedBeatmaps.ScaleTo(1.1f).ScaleTo(1, 1000, Easing.OutQuint); + currentlyLoadedBeatmaps.FadeColour(colours.Red1, 500, Easing.OutQuint); + } + else if (changes != null && (changes.DeletedIndices.Any() || changes.InsertedIndices.Any())) + { + currentlyLoadedBeatmaps.FadeColour(colours.Yellow) + .FadeColour(OverlayColourProvider.Content2, 1500, Easing.OutQuint); + + currentlyLoadedBeatmaps.ScaleTo(1.1f) + .ScaleTo(1, 1500, Easing.OutQuint); } } From 14316855f9a478734cc4627463e70332c3b8fdbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 19:26:28 +0900 Subject: [PATCH 54/63] Add localisation length hinting --- .editorconfig | 2 ++ osu.Game/.editorconfig | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index be5652954b..49e83c9103 100644 --- a/.editorconfig +++ b/.editorconfig @@ -175,6 +175,8 @@ csharp_style_prefer_switch_expression = false:none #Supressing roslyn built-in analyzers # Suppress: EC112 +dotnet_diagnostic.OLOC001.words_in_name = 5 + #Private method is unused dotnet_diagnostic.IDE0051.severity = silent #Private member is unused diff --git a/osu.Game/.editorconfig b/osu.Game/.editorconfig index 4107d1bb35..539cd56dab 100644 --- a/osu.Game/.editorconfig +++ b/osu.Game/.editorconfig @@ -1,3 +1,4 @@ [*.cs] +dotnet_diagnostic.OLOC001.words_in_name = 5 dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation -dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. \ No newline at end of file +dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. From 694f33b827d327b2b8fb8afd7e39761c8a12a25b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 19:33:16 +0900 Subject: [PATCH 55/63] Add localisation of beatmaps screen --- .../FirstRunSetupBeatmapScreenStrings.cs | 54 +++++++++++++++++++ .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 22 ++++---- 2 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs diff --git a/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs b/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs new file mode 100644 index 0000000000..3a7fe4bb12 --- /dev/null +++ b/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs @@ -0,0 +1,54 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class FirstRunSetupBeatmapScreenStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.FirstRunSetupBeatmapScreen"; + + /// + /// "Obtaining Beatmaps" + /// + public static LocalisableString Header => new TranslatableString(getKey(@"header"), @"Obtaining Beatmaps"); + + /// + /// ""Beatmaps" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection." + /// + public static LocalisableString Description => new TranslatableString(getKey(@"description"), @"""Beatmaps"" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection."); + + /// + /// "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay." + /// + public static LocalisableString TutorialDescription => new TranslatableString(getKey(@"tutorial_description"), @"If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay."); + + /// + /// "Get the osu! tutorial" + /// + public static LocalisableString TutorialButton => new TranslatableString(getKey(@"tutorial_button"), @"Get the osu! tutorial"); + + /// + /// "To get you started, we have some recommended beatmaps." + /// + public static LocalisableString BundledDescription => new TranslatableString(getKey(@"bundled_description"), @"To get you started, we have some recommended beatmaps."); + + /// + /// "Get recommended beatmaps" + /// + public static LocalisableString BundledButton => new TranslatableString(getKey(@"bundled_button"), @"Get recommended beatmaps"); + + /// + /// "You can also obtain more beatmaps from the main menu "browse" button at any time." + /// + public static LocalisableString ObtainMoreBeatmaps => new TranslatableString(getKey(@"obtain_more_beatmaps"), @"You can also obtain more beatmaps from the main menu ""browse"" button at any time."); + + /// + /// "You currently have {0} beatmap(s) loaded!" + /// + public static LocalisableString CurrentlyLoadedBeatmaps(int beatmaps) => new TranslatableString(getKey(@"currently_loaded_beatmaps"), @"You currently have {0} beatmap(s) loaded!", beatmaps); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 645713c52a..892856d280 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -3,10 +3,11 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; @@ -18,11 +19,10 @@ using osu.Game.Localisation; using osu.Game.Online; using osuTK; using Realms; -using Container = osu.Framework.Graphics.Containers.Container; namespace osu.Game.Overlays.FirstRunSetup { - [Description("Obtaining Beatmaps")] + [LocalisableDescription(typeof(FirstRunSetupBeatmapScreenStrings), nameof(FirstRunSetupBeatmapScreenStrings.Header))] public class ScreenBeatmaps : FirstRunSetupScreen { private ProgressRoundedButton downloadBundledButton = null!; @@ -52,8 +52,7 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { Colour = OverlayColourProvider.Content1, - Text = - "\"Beatmaps\" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.", + Text = FirstRunSetupBeatmapScreenStrings.Description, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -76,8 +75,7 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { Colour = OverlayColourProvider.Content1, - Text = - "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay.", + Text = FirstRunSetupBeatmapScreenStrings.TutorialDescription, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -87,13 +85,13 @@ namespace osu.Game.Overlays.FirstRunSetup Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, BackgroundColour = colours.Pink3, - Text = "Get the osu! tutorial", + Text = FirstRunSetupBeatmapScreenStrings.TutorialButton, Action = downloadTutorial }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { Colour = OverlayColourProvider.Content1, - Text = "To get you started, we have some recommended beatmaps.", + Text = FirstRunSetupBeatmapScreenStrings.BundledDescription, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -103,7 +101,7 @@ namespace osu.Game.Overlays.FirstRunSetup Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, BackgroundColour = colours.Blue3, - Text = "Get recommended beatmaps", + Text = FirstRunSetupBeatmapScreenStrings.BundledButton, Action = downloadBundled }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) @@ -135,7 +133,7 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) { Colour = OverlayColourProvider.Content1, - Text = "You can also obtain more beatmaps from the main menu \"browse\" button at any time.", + Text = FirstRunSetupBeatmapScreenStrings.ObtainMoreBeatmaps, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y }, @@ -157,7 +155,7 @@ namespace osu.Game.Overlays.FirstRunSetup private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes, Exception error) { - currentlyLoadedBeatmaps.Text = $"You currently have {sender.Count} beatmap(s) loaded!"; + currentlyLoadedBeatmaps.Text = FirstRunSetupBeatmapScreenStrings.CurrentlyLoadedBeatmaps(sender.Count); if (sender.Count == 0) { From f300b62877cad3c2c25b52fae29c7a446a887dda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 20:03:54 +0900 Subject: [PATCH 56/63] Add `ScrollIntoView` method which accepts an offset to allow usage in mod select --- .../Graphics/Containers/OsuScrollContainer.cs | 21 +++++++++++++++++++ osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index 017ea6ec32..817b8409e6 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -3,6 +3,7 @@ #nullable enable +using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -57,6 +58,26 @@ namespace osu.Game.Graphics.Containers { } + /// + /// Scrolls a into view. + /// + /// The to scroll into view. + /// Whether to animate the movement. + /// An added amount to scroll beyond the requirement to bring the target into view. + public void ScrollIntoView(Drawable d, bool animated = true, float extraScroll = 0) + { + float childPos0 = GetChildPosInContent(d); + float childPos1 = GetChildPosInContent(d, d.DrawSize); + + float minPos = Math.Min(childPos0, childPos1); + float maxPos = Math.Max(childPos0, childPos1); + + if (minPos < Current || (minPos > Current && d.DrawSize[ScrollDim] > DisplayableContent)) + ScrollTo(minPos - extraScroll, animated); + else if (maxPos > Current + DisplayableContent) + ScrollTo(maxPos - DisplayableContent + extraScroll, animated); + } + protected override bool OnMouseDown(MouseDownEvent e) { if (shouldPerformRightMouseScroll(e)) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 03a66575c4..ffd6e9a52c 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -161,7 +161,7 @@ namespace osu.Game.Overlays.Mods { AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, - RequestScroll = column => columnScroll.ScrollTo(Math.Clamp(column.DrawPosition.X - 70, 0, columnScroll.ScrollableExtent)) + RequestScroll = column => columnScroll.ScrollIntoView(column, extraScroll: 140) }; protected override void LoadComplete() From a50dd2ae93becc4c980c88e8b881a1a914e8b914 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 20:15:40 +0900 Subject: [PATCH 57/63] Fix rider failures in nullable --- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 892856d280..190a0badab 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -1,7 +1,8 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Linq; using osu.Framework.Allocation; From b902e69634ea7f56e4af3ceb29037186695b39b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 20:16:32 +0900 Subject: [PATCH 58/63] Update test expectations based on new screen order --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 31c4d66784..836cf6caad 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("step to next", () => overlay.NextButton.TriggerClick()); - AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenUIScale); + AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenBeatmaps); AddStep("hide", () => overlay.Hide()); AddAssert("overlay hidden", () => overlay.State.Value == Visibility.Hidden); @@ -188,7 +188,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("run notification action", () => lastNotification.Activated()); AddAssert("overlay shown", () => overlay.State.Value == Visibility.Visible); - AddAssert("is resumed", () => overlay.CurrentScreen is ScreenUIScale); + AddAssert("is resumed", () => overlay.CurrentScreen is ScreenBeatmaps); } } } From 32ad2166698528574947aca2c17fe28eb35d2757 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Apr 2022 22:58:32 +0900 Subject: [PATCH 59/63] Allow dragging the osu! logo Logic borrowed from `NowPlayingOverlay`. --- osu.Game/Screens/Menu/OsuLogo.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 1d3aef0653..1217a28fe7 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -109,7 +109,7 @@ namespace osu.Game.Screens.Menu AutoSizeAxes = Axes.Both, Children = new Drawable[] { - logoBounceContainer = new Container + logoBounceContainer = new DragContainer { AutoSizeAxes = Axes.Both, Children = new Drawable[] @@ -402,5 +402,26 @@ namespace osu.Game.Screens.Menu impactContainer.ScaleTo(0.96f); impactContainer.ScaleTo(1.12f, 250); } + + private class DragContainer : Container + { + protected override bool OnDragStart(DragStartEvent e) => true; + + protected override void OnDrag(DragEvent e) + { + Vector2 change = e.MousePosition - e.MouseDownPosition; + + // Diminish the drag distance as we go further to simulate "rubber band" feeling. + change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.6f) / change.Length; + + this.MoveTo(change); + } + + protected override void OnDragEnd(DragEndEvent e) + { + this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); + base.OnDragEnd(e); + } + } } } From 845e7dba95d28d2d37bf8502db04ee7910cb0e63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Apr 2022 11:27:14 +0900 Subject: [PATCH 60/63] Reduce padding slightly to restore usability of UI scale screen --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index c3dcdef318..4277f0f2ba 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -84,7 +84,7 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 70 * 2 }, + Padding = new MarginPadding { Horizontal = 70 * 1.2f }, Child = new InputBlockingContainer { Masking = true, From 92d4463e55acf51e8707d3eddbba642ec6a22541 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Apr 2022 11:45:18 +0900 Subject: [PATCH 61/63] Add "always bundled" beatmaps --- .../Beatmaps/Drawables/BundledBeatmapDownloader.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index bc99aa4a5a..df0a69cb25 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -44,6 +44,8 @@ namespace osu.Game.Beatmaps.Drawables } else { + queueDownloads(always_bundled_beatmaps); + queueDownloads(bundled_osu, 8); queueDownloads(bundled_taiko, 3); queueDownloads(bundled_catch, 3); @@ -120,6 +122,15 @@ namespace osu.Game.Beatmaps.Drawables private const string tutorial_filename = "1011011 nekodex - new beginnings.osz"; + /// + /// Contest winners or other special cases. + /// + private static readonly string[] always_bundled_beatmaps = + { + // This thing is 40mb, I'm not sure we want it here... + @"1388906 Raphlesia & BilliumMoto - My Love.osz" + }; + private static readonly string[] bundled_osu = { "682286 Yuyoyuppe - Emerald Galaxy.osz", From 22e9620211fd32352494ef48ba336dad178471b3 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 29 Apr 2022 12:09:11 +0900 Subject: [PATCH 62/63] Don't block clicks when dragging osu! logo --- osu.Game/Screens/Menu/OsuLogo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 1217a28fe7..f5743c7d5a 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -405,6 +405,8 @@ namespace osu.Game.Screens.Menu private class DragContainer : Container { + public override bool DragBlocksClick => false; + protected override bool OnDragStart(DragStartEvent e) => true; protected override void OnDrag(DragEvent e) From d7441fbcd443464ca8e2e108ac677e68b42f1f51 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 29 Apr 2022 12:16:37 +0900 Subject: [PATCH 63/63] Move diagnostic down --- .editorconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 49e83c9103..840fa98334 100644 --- a/.editorconfig +++ b/.editorconfig @@ -175,8 +175,6 @@ csharp_style_prefer_switch_expression = false:none #Supressing roslyn built-in analyzers # Suppress: EC112 -dotnet_diagnostic.OLOC001.words_in_name = 5 - #Private method is unused dotnet_diagnostic.IDE0051.severity = silent #Private member is unused @@ -193,4 +191,5 @@ dotnet_diagnostic.CA2225.severity = none # Banned APIs dotnet_diagnostic.RS0030.severity = error +dotnet_diagnostic.OLOC001.words_in_name = 5 dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text.