From 34146b8bcbaa2bf3adb77390e67c303c81e82e83 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Nov 2025 16:23:53 +0900 Subject: [PATCH 01/12] Update rounded button to be less rounded Intended to match the rest of the UI which is less rounded these days. See inline comment for reason for not matching `FormControl` corner radius just yet. --- .../Graphics/UserInterfaceV2/RoundedButton.cs | 18 +++--------------- osu.Game/Overlays/Settings/SettingsButton.cs | 7 ++++++- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index bf92f20526..01c495ae30 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -26,18 +26,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 private Color4? triangleGradientSecondColour; - public override float Height - { - get => base.Height; - set - { - base.Height = value; - - if (IsLoaded) - updateCornerRadius(); - } - } - public override Color4 BackgroundColour { get => base.BackgroundColour; @@ -61,7 +49,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 { base.LoadComplete(); - updateCornerRadius(); + // This doesn't match the latest design spec (should be 5) but is an in-between that feels right to the eye + // until we move everything over to Form controls. + Content.CornerRadius = 10; Add(Triangles = new TrianglesV2 { @@ -98,8 +88,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 base.OnHoverLost(e); } - private void updateCornerRadius() => Content.CornerRadius = DrawHeight / 2; - public virtual IEnumerable FilterTerms => new[] { Text }; public bool MatchingFilter diff --git a/osu.Game/Overlays/Settings/SettingsButton.cs b/osu.Game/Overlays/Settings/SettingsButton.cs index 196ddca953..0033543fdb 100644 --- a/osu.Game/Overlays/Settings/SettingsButton.cs +++ b/osu.Game/Overlays/Settings/SettingsButton.cs @@ -16,7 +16,12 @@ namespace osu.Game.Overlays.Settings public SettingsButton() { RelativeSizeAxes = Axes.X; - Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS }; + Margin = new MarginPadding { Vertical = -5 }; + Padding = new MarginPadding + { + Left = SettingsPanel.CONTENT_MARGINS, + Right = SettingsPanel.CONTENT_MARGINS, + }; } public IEnumerable Keywords { get; set; } = Array.Empty(); From df79269e6fa409cf7340de5553fb332f3ef57f9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Nov 2025 16:24:10 +0900 Subject: [PATCH 02/12] Adjust tablet settings layout to feel a touch nicer --- .../Overlays/Settings/Sections/Input/TabletAreaSelection.cs | 2 +- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 33f4f49173..b3fdd53466 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -137,7 +137,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input rotation.BindValueChanged(val => { usableAreaContainer.RotateTo(val.NewValue, 100, Easing.OutQuint); - tabletContainer.RotateTo(-val.NewValue, 800, Easing.OutQuint); + tabletContainer.RotateTo(-val.NewValue, 400, Easing.OutQuint); checkBounds(); }, true); diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 6aebec88a9..86b52328b2 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Alpha = 0, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0, 8), + Spacing = new Vector2(0, SettingsSection.ITEM_SPACING), Direction = FillDirection.Vertical, Children = new Drawable[] { From 908a950cd2431ed2dbed1a763101a709859dbf4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Nov 2025 16:25:04 +0900 Subject: [PATCH 03/12] Move quick action settings into own subsection --- .../Localisation/GeneralSettingsStrings.cs | 5 ++ .../Sections/General/QuickActionSettings.cs | 52 +++++++++++++++++++ .../Settings/Sections/GeneralSection.cs | 31 +---------- 3 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 osu.Game/Overlays/Settings/Sections/General/QuickActionSettings.cs diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs index 7e4ee94286..d1e22c197e 100644 --- a/osu.Game/Localisation/GeneralSettingsStrings.cs +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -29,6 +29,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Prefer24HourTimeDisplay => new TranslatableString(getKey(@"prefer_24_hour_time_display"), @"Prefer 24-hour time display"); + /// + /// "Quick Actions" + /// + public static LocalisableString QuickActionsHeader => new TranslatableString(getKey(@"quick_actions_header"), @"Quick Actions"); + /// /// "Updates" /// diff --git a/osu.Game/Overlays/Settings/Sections/General/QuickActionSettings.cs b/osu.Game/Overlays/Settings/Sections/General/QuickActionSettings.cs new file mode 100644 index 0000000000..94f0e7a7d1 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/General/QuickActionSettings.cs @@ -0,0 +1,52 @@ +// 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.Localisation; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Settings.Sections.General +{ + public partial class QuickActionSettings : SettingsSubsection + { + [Resolved(CanBeNull = true)] + private FirstRunSetupOverlay? firstRunSetupOverlay { get; set; } + + [Resolved(CanBeNull = true)] + private OsuGame? game { get; set; } + + protected override LocalisableString Header => GeneralSettingsStrings.QuickActionsHeader; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AddRange(new Drawable[] + { + new SettingsButton + { + Text = GeneralSettingsStrings.RunSetupWizard, + Keywords = new[] { @"first run", @"initial", @"getting started", @"import", @"tutorial", @"recommended beatmaps" }, + TooltipText = FirstRunSetupOverlayStrings.FirstRunSetupDescription, + Action = () => firstRunSetupOverlay?.Show(), + }, + new SettingsButton + { + Text = GeneralSettingsStrings.LearnMoreAboutLazer, + TooltipText = GeneralSettingsStrings.LearnMoreAboutLazerTooltip, + BackgroundColour = colours.YellowDark, + Action = () => game?.ShowWiki(@"Help_centre/Upgrading_to_lazer") + }, + new SettingsButton + { + Text = GeneralSettingsStrings.ReportIssue, + TooltipText = GeneralSettingsStrings.ReportIssueTooltip, + BackgroundColour = colours.YellowDarker, + Action = () => game?.OpenUrlExternally(@"https://osu.ppy.sh/community/forums/topics/create?forum_id=5", LinkWarnMode.NeverWarn) + }, + }); + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index b3ef1b9242..a8acbb6bbb 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -7,19 +7,12 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Localisation; -using osu.Game.Online.Chat; using osu.Game.Overlays.Settings.Sections.General; namespace osu.Game.Overlays.Settings.Sections { public partial class GeneralSection : SettingsSection { - [Resolved(CanBeNull = true)] - private FirstRunSetupOverlay? firstRunSetupOverlay { get; set; } - - [Resolved(CanBeNull = true)] - private OsuGame? game { get; set; } - public override LocalisableString Header => CommonStrings.General; public override Drawable CreateIcon() => new SpriteIcon @@ -28,33 +21,13 @@ namespace osu.Game.Overlays.Settings.Sections }; [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { Children = new Drawable[] { - new SettingsButton - { - Text = GeneralSettingsStrings.RunSetupWizard, - Keywords = new[] { @"first run", @"initial", @"getting started", @"import", @"tutorial", @"recommended beatmaps" }, - TooltipText = FirstRunSetupOverlayStrings.FirstRunSetupDescription, - Action = () => firstRunSetupOverlay?.Show(), - }, - new SettingsButton - { - Text = GeneralSettingsStrings.LearnMoreAboutLazer, - TooltipText = GeneralSettingsStrings.LearnMoreAboutLazerTooltip, - BackgroundColour = colours.YellowDark, - Action = () => game?.ShowWiki(@"Help_centre/Upgrading_to_lazer") - }, - new SettingsButton - { - Text = GeneralSettingsStrings.ReportIssue, - TooltipText = GeneralSettingsStrings.ReportIssueTooltip, - BackgroundColour = colours.DarkOrange2, - Action = () => game?.OpenUrlExternally(@"https://osu.ppy.sh/community/forums/topics/create?forum_id=5", LinkWarnMode.NeverWarn) - }, new LanguageSettings(), new UpdateSettings(), + new QuickActionSettings(), }; } } From a8594f1c08dae7d72c91e25c32737132744f0e20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Nov 2025 16:25:13 +0900 Subject: [PATCH 04/12] Move installation settings into own subsection --- .../Localisation/GeneralSettingsStrings.cs | 5 + .../Sections/General/InstallationSettings.cs | 114 ++++++++++++++++++ .../Sections/General/UpdateSettings.cs | 88 +------------- .../Settings/Sections/GeneralSection.cs | 1 + 4 files changed, 121 insertions(+), 87 deletions(-) create mode 100644 osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs index d1e22c197e..9b6276781a 100644 --- a/osu.Game/Localisation/GeneralSettingsStrings.cs +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -29,6 +29,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Prefer24HourTimeDisplay => new TranslatableString(getKey(@"prefer_24_hour_time_display"), @"Prefer 24-hour time display"); + /// + /// "Installation" + /// + public static LocalisableString InstallationHeader => new TranslatableString(getKey(@"installation_header"), @"Installation"); + /// /// "Quick Actions" /// diff --git a/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs b/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs new file mode 100644 index 0000000000..92f6c5e3b0 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs @@ -0,0 +1,114 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; +using osu.Framework; +using osu.Framework.Allocation; +using osu.Framework.Localisation; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Framework.Screens; +using osu.Framework.Statistics; +using osu.Game.Graphics; +using osu.Game.IO; +using osu.Game.Localisation; +using osu.Game.Overlays.Notifications; +using osu.Game.Overlays.Settings.Sections.Maintenance; +using osu.Game.Utils; +using SharpCompress.Archives.Zip; + +namespace osu.Game.Overlays.Settings.Sections.General +{ + public partial class InstallationSettings : SettingsSubsection + { + protected override LocalisableString Header => GeneralSettingsStrings.InstallationHeader; + + [Resolved] + private INotificationOverlay? notifications { get; set; } + + [Resolved] + private OsuGame? game { get; set; } + + private Storage exportStorage = null!; + + [BackgroundDependencyLoader] + private void load(Storage storage, OsuColour colours) + { + bool isDesktop = RuntimeInfo.IsDesktop; + bool supportsExport = RuntimeInfo.OS != RuntimeInfo.Platform.Android; + + // Loosely update-related maintenance buttons. + if (isDesktop) + { + Add(new SettingsButton + { + Text = GeneralSettingsStrings.OpenOsuFolder, + Keywords = new[] { @"logs", @"files", @"access", "directory" }, + Action = () => storage.PresentExternally(), + }); + + Add(new DangerousSettingsButton + { + Text = GeneralSettingsStrings.ChangeFolderLocation, + Action = () => game?.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) + }); + } + + if (supportsExport) + { + Add(new SettingsButton + { + Text = GeneralSettingsStrings.ExportLogs, + BackgroundColour = colours.YellowDarker, + Keywords = new[] { @"bug", "report", "logs", "files" }, + Action = () => Task.Run(exportLogs), + }); + + exportStorage = (storage as OsuStorage)?.GetExportStorage() ?? storage.GetStorageForDirectory(@"exports"); + } + } + + private void exportLogs() + { + ProgressNotification notification = new ProgressNotification + { + State = ProgressNotificationState.Active, + Text = "Exporting logs...", + }; + + notifications?.Post(notification); + + const string archive_filename = "compressed-logs.zip"; + + try + { + GlobalStatistics.OutputToLog(); + Logger.Flush(); + + var logStorage = Logger.Storage; + + using (var outStream = exportStorage.CreateFileSafely(archive_filename)) + using (var zip = ZipArchive.Create()) + { + foreach (string? f in logStorage.GetFiles(string.Empty, "*.log")) + FileUtils.AttemptOperation(z => z.AddEntry(f, logStorage.GetStream(f), true), zip); + + zip.SaveTo(outStream); + } + } + catch + { + notification.State = ProgressNotificationState.Cancelled; + + // cleanup if export is failed or canceled. + exportStorage.Delete(archive_filename); + throw; + } + + notification.CompletionText = "Exported logs! Click to view."; + notification.CompletionClickAction = () => exportStorage.PresentFileExternally(archive_filename); + + notification.State = ProgressNotificationState.Completed; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 596e4b2589..04dec05399 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -7,20 +7,12 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Framework.Logging; -using osu.Framework.Platform; -using osu.Framework.Screens; -using osu.Framework.Statistics; using osu.Game.Configuration; -using osu.Game.IO; using osu.Game.Localisation; using osu.Game.Online.Multiplayer; using osu.Game.Overlays.Dialog; using osu.Game.Overlays.Notifications; -using osu.Game.Overlays.Settings.Sections.Maintenance; using osu.Game.Updater; -using osu.Game.Utils; -using SharpCompress.Archives.Zip; namespace osu.Game.Overlays.Settings.Sections.General { @@ -45,15 +37,12 @@ namespace osu.Game.Overlays.Settings.Sections.General [Resolved] private IDialogOverlay? dialogOverlay { get; set; } - private Storage exportStorage = null!; - [BackgroundDependencyLoader] - private void load(OsuConfigManager config, Storage storage) + private void load(OsuConfigManager config) { config.BindWith(OsuSetting.ReleaseStream, configReleaseStream); bool isDesktop = RuntimeInfo.IsDesktop; - bool supportsExport = RuntimeInfo.OS != RuntimeInfo.Platform.Android; bool canCheckUpdates = updateManager?.CanCheckForUpdate == true; if (canCheckUpdates) @@ -86,38 +75,6 @@ namespace osu.Game.Overlays.Settings.Sections.General Action = () => checkForUpdates().FireAndForget() }); } - - // Loosely update-related maintenance buttons. - if (isDesktop) - { - Add(new SettingsButton - { - Text = GeneralSettingsStrings.OpenOsuFolder, - Keywords = new[] { @"logs", @"files", @"access", "directory" }, - Action = () => storage.PresentExternally(), - }); - } - - if (supportsExport) - { - Add(new SettingsButton - { - Text = GeneralSettingsStrings.ExportLogs, - Keywords = new[] { @"bug", "report", "logs", "files" }, - Action = () => Task.Run(exportLogs), - }); - } - - if (isDesktop) - { - Add(new SettingsButton - { - Text = GeneralSettingsStrings.ChangeFolderLocation, - Action = () => game?.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) - }); - } - - exportStorage = (storage as OsuStorage)?.GetExportStorage() ?? storage.GetStorageForDirectory(@"exports"); } private void releaseStreamChanged(ValueChangedEvent stream) @@ -176,48 +133,5 @@ namespace osu.Game.Overlays.Settings.Sections.General checkForUpdatesButton.Enabled.Value = true; } } - - private void exportLogs() - { - ProgressNotification notification = new ProgressNotification - { - State = ProgressNotificationState.Active, - Text = "Exporting logs...", - }; - - notifications?.Post(notification); - - const string archive_filename = "compressed-logs.zip"; - - try - { - GlobalStatistics.OutputToLog(); - Logger.Flush(); - - var logStorage = Logger.Storage; - - using (var outStream = exportStorage.CreateFileSafely(archive_filename)) - using (var zip = ZipArchive.Create()) - { - foreach (string? f in logStorage.GetFiles(string.Empty, "*.log")) - FileUtils.AttemptOperation(z => z.AddEntry(f, logStorage.GetStream(f), true), zip); - - zip.SaveTo(outStream); - } - } - catch - { - notification.State = ProgressNotificationState.Cancelled; - - // cleanup if export is failed or canceled. - exportStorage.Delete(archive_filename); - throw; - } - - notification.CompletionText = "Exported logs! Click to view."; - notification.CompletionClickAction = () => exportStorage.PresentFileExternally(archive_filename); - - notification.State = ProgressNotificationState.Completed; - } } } diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index a8acbb6bbb..7124d9a37d 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -27,6 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections { new LanguageSettings(), new UpdateSettings(), + new InstallationSettings(), new QuickActionSettings(), }; } From a6a98fc078049e42680eb2d463c6a3a6f8d46d4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Nov 2025 16:30:24 +0900 Subject: [PATCH 05/12] Only show update settings if the game can be updated --- .../Sections/General/UpdateSettings.cs | 44 +++++++++---------- .../Settings/Sections/GeneralSection.cs | 15 +++---- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 04dec05399..87b1acc23a 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -43,38 +43,34 @@ namespace osu.Game.Overlays.Settings.Sections.General config.BindWith(OsuSetting.ReleaseStream, configReleaseStream); bool isDesktop = RuntimeInfo.IsDesktop; - bool canCheckUpdates = updateManager?.CanCheckForUpdate == true; - if (canCheckUpdates) + // For simplicity, hide the concept of release streams from mobile users. + if (isDesktop) { - // For simplicity, hide the concept of release streams from mobile users. - if (isDesktop) + Add(releaseStreamDropdown = new SettingsEnumDropdown { - Add(releaseStreamDropdown = new SettingsEnumDropdown - { - LabelText = GeneralSettingsStrings.ReleaseStream, - Current = { Value = configReleaseStream.Value }, - Keywords = new[] { @"version" }, - }); + LabelText = GeneralSettingsStrings.ReleaseStream, + Current = { Value = configReleaseStream.Value }, + Keywords = new[] { @"version" }, + }); - if (updateManager!.FixedReleaseStream != null) - { - configReleaseStream.Value = updateManager.FixedReleaseStream.Value; + if (updateManager!.FixedReleaseStream != null) + { + configReleaseStream.Value = updateManager.FixedReleaseStream.Value; - releaseStreamDropdown.ShowsDefaultIndicator = false; - releaseStreamDropdown.Items = [updateManager.FixedReleaseStream.Value]; - releaseStreamDropdown.SetNoticeText(GeneralSettingsStrings.ChangeReleaseStreamPackageManagerWarning); - } - - releaseStreamDropdown.Current.BindValueChanged(releaseStreamChanged); + releaseStreamDropdown.ShowsDefaultIndicator = false; + releaseStreamDropdown.Items = [updateManager.FixedReleaseStream.Value]; + releaseStreamDropdown.SetNoticeText(GeneralSettingsStrings.ChangeReleaseStreamPackageManagerWarning); } - Add(checkForUpdatesButton = new SettingsButton - { - Text = GeneralSettingsStrings.CheckUpdate, - Action = () => checkForUpdates().FireAndForget() - }); + releaseStreamDropdown.Current.BindValueChanged(releaseStreamChanged); } + + Add(checkForUpdatesButton = new SettingsButton + { + Text = GeneralSettingsStrings.CheckUpdate, + Action = () => checkForUpdates().FireAndForget() + }); } private void releaseStreamChanged(ValueChangedEvent stream) diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index 7124d9a37d..1243887386 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -8,6 +8,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.General; +using osu.Game.Updater; namespace osu.Game.Overlays.Settings.Sections { @@ -21,15 +22,13 @@ namespace osu.Game.Overlays.Settings.Sections }; [BackgroundDependencyLoader] - private void load() + private void load(UpdateManager? updateManager) { - Children = new Drawable[] - { - new LanguageSettings(), - new UpdateSettings(), - new InstallationSettings(), - new QuickActionSettings(), - }; + Add(new LanguageSettings()); + if (updateManager?.CanCheckForUpdate == true) + Add(new UpdateSettings()); + Add(new InstallationSettings()); + Add(new QuickActionSettings()); } } } From 73349ab1825e449a8833f46ad19b3abf9dd5294c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Nov 2025 16:34:53 +0900 Subject: [PATCH 06/12] Move quick actions to top --- osu.Game/Overlays/Settings/Sections/GeneralSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index 1243887386..18e650d70d 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -24,11 +24,11 @@ namespace osu.Game.Overlays.Settings.Sections [BackgroundDependencyLoader] private void load(UpdateManager? updateManager) { + Add(new QuickActionSettings()); Add(new LanguageSettings()); if (updateManager?.CanCheckForUpdate == true) Add(new UpdateSettings()); Add(new InstallationSettings()); - Add(new QuickActionSettings()); } } } From 56ce955e0c3d16c26fabc4cc75e371b801dd98f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Nov 2025 16:40:38 +0900 Subject: [PATCH 07/12] Move export logs to quick actions (to sit with report issue button) --- .../Sections/General/InstallationSettings.cs | 72 +----------------- .../Sections/General/QuickActionSettings.cs | 75 ++++++++++++++++++- 2 files changed, 75 insertions(+), 72 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs b/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs index 92f6c5e3b0..3aaeadd158 100644 --- a/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs @@ -1,21 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Threading.Tasks; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Localisation; -using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Screens; -using osu.Framework.Statistics; -using osu.Game.Graphics; -using osu.Game.IO; using osu.Game.Localisation; -using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Settings.Sections.Maintenance; -using osu.Game.Utils; -using SharpCompress.Archives.Zip; namespace osu.Game.Overlays.Settings.Sections.General { @@ -23,19 +15,13 @@ namespace osu.Game.Overlays.Settings.Sections.General { protected override LocalisableString Header => GeneralSettingsStrings.InstallationHeader; - [Resolved] - private INotificationOverlay? notifications { get; set; } - [Resolved] private OsuGame? game { get; set; } - private Storage exportStorage = null!; - [BackgroundDependencyLoader] - private void load(Storage storage, OsuColour colours) + private void load(Storage storage) { bool isDesktop = RuntimeInfo.IsDesktop; - bool supportsExport = RuntimeInfo.OS != RuntimeInfo.Platform.Android; // Loosely update-related maintenance buttons. if (isDesktop) @@ -53,62 +39,6 @@ namespace osu.Game.Overlays.Settings.Sections.General Action = () => game?.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) }); } - - if (supportsExport) - { - Add(new SettingsButton - { - Text = GeneralSettingsStrings.ExportLogs, - BackgroundColour = colours.YellowDarker, - Keywords = new[] { @"bug", "report", "logs", "files" }, - Action = () => Task.Run(exportLogs), - }); - - exportStorage = (storage as OsuStorage)?.GetExportStorage() ?? storage.GetStorageForDirectory(@"exports"); - } - } - - private void exportLogs() - { - ProgressNotification notification = new ProgressNotification - { - State = ProgressNotificationState.Active, - Text = "Exporting logs...", - }; - - notifications?.Post(notification); - - const string archive_filename = "compressed-logs.zip"; - - try - { - GlobalStatistics.OutputToLog(); - Logger.Flush(); - - var logStorage = Logger.Storage; - - using (var outStream = exportStorage.CreateFileSafely(archive_filename)) - using (var zip = ZipArchive.Create()) - { - foreach (string? f in logStorage.GetFiles(string.Empty, "*.log")) - FileUtils.AttemptOperation(z => z.AddEntry(f, logStorage.GetStream(f), true), zip); - - zip.SaveTo(outStream); - } - } - catch - { - notification.State = ProgressNotificationState.Cancelled; - - // cleanup if export is failed or canceled. - exportStorage.Delete(archive_filename); - throw; - } - - notification.CompletionText = "Exported logs! Click to view."; - notification.CompletionClickAction = () => exportStorage.PresentFileExternally(archive_filename); - - notification.State = ProgressNotificationState.Completed; } } } diff --git a/osu.Game/Overlays/Settings/Sections/General/QuickActionSettings.cs b/osu.Game/Overlays/Settings/Sections/General/QuickActionSettings.cs index 94f0e7a7d1..b6b78a6d00 100644 --- a/osu.Game/Overlays/Settings/Sections/General/QuickActionSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/QuickActionSettings.cs @@ -1,12 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Threading.Tasks; +using osu.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Framework.Statistics; using osu.Game.Graphics; +using osu.Game.IO; using osu.Game.Localisation; using osu.Game.Online.Chat; +using osu.Game.Overlays.Notifications; +using osu.Game.Utils; +using SharpCompress.Archives.Zip; namespace osu.Game.Overlays.Settings.Sections.General { @@ -21,7 +31,7 @@ namespace osu.Game.Overlays.Settings.Sections.General protected override LocalisableString Header => GeneralSettingsStrings.QuickActionsHeader; [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, Storage storage) { AddRange(new Drawable[] { @@ -47,6 +57,69 @@ namespace osu.Game.Overlays.Settings.Sections.General Action = () => game?.OpenUrlExternally(@"https://osu.ppy.sh/community/forums/topics/create?forum_id=5", LinkWarnMode.NeverWarn) }, }); + + bool supportsExport = RuntimeInfo.OS != RuntimeInfo.Platform.Android; + + if (supportsExport) + { + Add(new SettingsButton + { + Text = GeneralSettingsStrings.ExportLogs, + BackgroundColour = colours.YellowDarker.Darken(0.5f), + Keywords = new[] { @"bug", "report", "logs", "files" }, + Action = () => Task.Run(exportLogs), + }); + + exportStorage = (storage as OsuStorage)?.GetExportStorage() ?? storage.GetStorageForDirectory(@"exports"); + } + } + + [Resolved] + private INotificationOverlay? notifications { get; set; } + + private Storage exportStorage = null!; + + private void exportLogs() + { + ProgressNotification notification = new ProgressNotification + { + State = ProgressNotificationState.Active, + Text = "Exporting logs...", + }; + + notifications?.Post(notification); + + const string archive_filename = "compressed-logs.zip"; + + try + { + GlobalStatistics.OutputToLog(); + Logger.Flush(); + + var logStorage = Logger.Storage; + + using (var outStream = exportStorage.CreateFileSafely(archive_filename)) + using (var zip = ZipArchive.Create()) + { + foreach (string? f in logStorage.GetFiles(string.Empty, "*.log")) + FileUtils.AttemptOperation(z => z.AddEntry(f, logStorage.GetStream(f), true), zip); + + zip.SaveTo(outStream); + } + } + catch + { + notification.State = ProgressNotificationState.Cancelled; + + // cleanup if export is failed or canceled. + exportStorage.Delete(archive_filename); + throw; + } + + notification.CompletionText = "Exported logs! Click to view."; + notification.CompletionClickAction = () => exportStorage.PresentFileExternally(archive_filename); + + notification.State = ProgressNotificationState.Completed; } } } From d59e9572d2f075c5c4e063ff0dc7aa5bd04d4130 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Nov 2025 00:55:22 +0900 Subject: [PATCH 08/12] Add missing padding around countdown settings button --- .../Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index a91b844900..f73983217f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -50,11 +50,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match ColumnDimensions = new[] { new Dimension(), + new Dimension(GridSizeMode.Absolute, 5), new Dimension(GridSizeMode.AutoSize) }, Content = new[] { - new Drawable[] + new Drawable?[] { readyButton = new MultiplayerReadyButton { @@ -62,6 +63,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Size = Vector2.One, Action = onReadyButtonClick, }, + null, countdownButton = new MultiplayerCountdownButton { RelativeSizeAxes = Axes.Y, From b0762fc8ec2f3bf4c3fd983d7964aecb34b48e9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Nov 2025 00:55:42 +0900 Subject: [PATCH 09/12] Reduce abstractions of rounded button --- .../UserInterface/DangerousRoundedButton.cs | 2 +- .../Settings/Sections/Input/KeyBindingRow.cs | 32 +++++++------------ osu.Game/Rulesets/Edit/ExpandableButton.cs | 2 +- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DangerousRoundedButton.cs b/osu.Game/Graphics/UserInterface/DangerousRoundedButton.cs index 39ef7924b9..cb9250c15c 100644 --- a/osu.Game/Graphics/UserInterface/DangerousRoundedButton.cs +++ b/osu.Game/Graphics/UserInterface/DangerousRoundedButton.cs @@ -6,7 +6,7 @@ using osu.Game.Graphics.UserInterfaceV2; namespace osu.Game.Graphics.UserInterface { - public partial class DangerousRoundedButton : RoundedButton + public sealed partial class DangerousRoundedButton : RoundedButton { [BackgroundDependencyLoader] private void load(OsuColour colours) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 083c678176..c9ef6ef891 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -191,8 +191,18 @@ namespace osu.Game.Overlays.Settings.Sections.Input Spacing = new Vector2(5), Children = new Drawable[] { - new CancelButton { Action = () => finalise(false) }, - new ClearButton { Action = clear }, + new RoundedButton + { + Text = CommonStrings.ButtonsCancel, + Size = new Vector2(80, 20), + Action = () => finalise(false) + }, + new DangerousRoundedButton + { + Text = CommonStrings.ButtonsClear, + Size = new Vector2(80, 20), + Action = clear + }, }, }, new HoverClickSounds() @@ -538,23 +548,5 @@ namespace osu.Game.Overlays.Settings.Sections.Input { isDefault.Value = KeyBindings.Select(b => b.KeyCombination).SequenceEqual(Defaults); } - - private partial class CancelButton : RoundedButton - { - public CancelButton() - { - Text = CommonStrings.ButtonsCancel; - Size = new Vector2(80, 20); - } - } - - public partial class ClearButton : DangerousRoundedButton - { - public ClearButton() - { - Text = CommonStrings.ButtonsClear; - Size = new Vector2(80, 20); - } - } } } diff --git a/osu.Game/Rulesets/Edit/ExpandableButton.cs b/osu.Game/Rulesets/Edit/ExpandableButton.cs index 9139802d68..d1f855a8ad 100644 --- a/osu.Game/Rulesets/Edit/ExpandableButton.cs +++ b/osu.Game/Rulesets/Edit/ExpandableButton.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics.UserInterfaceV2; namespace osu.Game.Rulesets.Edit { - public partial class ExpandableButton : RoundedButton, IExpandable + public sealed partial class ExpandableButton : RoundedButton, IExpandable { private float actualHeight; From 64668eafb96c08dcfad1f02e50053b688613eb6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Nov 2025 00:55:53 +0900 Subject: [PATCH 10/12] Adjust some more visual metrics to feel better --- osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs | 3 ++- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 3 ++- osu.Game/Graphics/UserInterfaceV2/FormColourPalette.cs | 3 ++- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs index 48d225de41..67a4cf6890 100644 --- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs @@ -56,7 +56,8 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.Centre, Anchor = Anchor.Centre, RelativeSizeAxes = Axes.Both, - CornerRadius = 5, + CornerRadius = 10, + CornerExponent = 2.5f, Masking = true, EdgeEffect = new EdgeEffectParameters { diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index e0179f8bc4..ae5501a3dd 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -357,7 +357,8 @@ namespace osu.Game.Graphics.UserInterface Icon = FontAwesome.Solid.ChevronDown, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Size = new Vector2(16), + Size = new Vector2(10), + Margin = new MarginPadding { Right = 2 }, }, } } diff --git a/osu.Game/Graphics/UserInterfaceV2/FormColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/FormColourPalette.cs index a0348fa27a..90e12b128c 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormColourPalette.cs @@ -178,7 +178,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 Size = new Vector2(70); Masking = true; - CornerRadius = 35; + CornerRadius = 10; + CornerExponent = 2.5f; Action = this.ShowPopover; Children = new Drawable[] diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index 01c495ae30..faabb80299 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -52,6 +52,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 // This doesn't match the latest design spec (should be 5) but is an in-between that feels right to the eye // until we move everything over to Form controls. Content.CornerRadius = 10; + Content.CornerExponent = 2.5f; Add(Triangles = new TrianglesV2 { From 52af905237562a3ea9d88017cba0a90d3289d46a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Nov 2025 01:06:29 +0900 Subject: [PATCH 11/12] Hide full installation section on non-desktop platforms --- .../Sections/General/InstallationSettings.cs | 27 +++++++------------ .../Settings/Sections/GeneralSection.cs | 4 ++- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs b/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs index 3aaeadd158..68f3ba9b17 100644 --- a/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/InstallationSettings.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Localisation; using osu.Framework.Platform; @@ -21,24 +20,18 @@ namespace osu.Game.Overlays.Settings.Sections.General [BackgroundDependencyLoader] private void load(Storage storage) { - bool isDesktop = RuntimeInfo.IsDesktop; - - // Loosely update-related maintenance buttons. - if (isDesktop) + Add(new SettingsButton { - Add(new SettingsButton - { - Text = GeneralSettingsStrings.OpenOsuFolder, - Keywords = new[] { @"logs", @"files", @"access", "directory" }, - Action = () => storage.PresentExternally(), - }); + Text = GeneralSettingsStrings.OpenOsuFolder, + Keywords = new[] { @"logs", @"files", @"access", "directory" }, + Action = () => storage.PresentExternally(), + }); - Add(new DangerousSettingsButton - { - Text = GeneralSettingsStrings.ChangeFolderLocation, - Action = () => game?.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) - }); - } + Add(new DangerousSettingsButton + { + Text = GeneralSettingsStrings.ChangeFolderLocation, + Action = () => game?.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) + }); } } } diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index 18e650d70d..7136de1327 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.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 osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; @@ -28,7 +29,8 @@ namespace osu.Game.Overlays.Settings.Sections Add(new LanguageSettings()); if (updateManager?.CanCheckForUpdate == true) Add(new UpdateSettings()); - Add(new InstallationSettings()); + if (RuntimeInfo.IsDesktop) + Add(new InstallationSettings()); } } } From 45567f19b72cc12a0461f6c379730be4fc44d60e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 25 Nov 2025 07:46:29 +0100 Subject: [PATCH 12/12] Fix test not compiling --- .../Visual/Settings/TestSceneKeyBindingPanel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 4cad283833..8e671f331d 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("schedule button clicks", () => { - var clearButton = firstRow.ChildrenOfType().Single(); + var clearButton = firstRow.ChildrenOfType().Single(); InputManager.MoveMouseTo(clearButton); @@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual.Settings { AddStep("click clear button", () => { - var clearButton = multiBindingRow.ChildrenOfType().Single(); + var clearButton = multiBindingRow.ChildrenOfType().Single(); InputManager.MoveMouseTo(clearButton); InputManager.Click(MouseButton.Left); @@ -386,7 +386,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("clear binding", () => { var row = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text.ToString() == "Left (centre)")); - row.ChildrenOfType().Single().TriggerClick(); + row.ChildrenOfType().Single().TriggerClick(); }); scrollToAndStartBinding("Left (rim)"); AddStep("bind M1", () => InputManager.Click(MouseButton.Left)); @@ -462,7 +462,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("clear binding", () => { var row = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text.ToString() == "Left (centre)")); - row.ChildrenOfType().Single().TriggerClick(); + row.ChildrenOfType().Single().TriggerClick(); }); }