Skip to content

Conversation

@frenzibyte
Copy link
Member

The final piece, containing migration of all settings to use the new form controls, as well as minor changes within the settings overlay to complete the migration.

To avoid touching any irrelevant file, I've chosen to make and use separate constants and classes for new settings, as shown in 8956bbd. Once this PR is merged, a follow-up PR will migrate the remaining non-settings usages of SettingsItem to the V2 counterpart, remove SettingsItem completely, and drop the V2 suffix from all existing components.

Side design changes worthy of note:

Before After
CleanShot 2025-12-31 at 16 02 58 CleanShot 2025-12-31 at 16 01 38
CleanShot 2025-12-31 at 16 06 46 CleanShot 2025-12-31 at 16 01 11
CleanShot 2025-12-31 at 16 05 53 CleanShot 2025-12-31 at 16 03 24
CleanShot 2025-12-31 at 16 05 47 CleanShot 2025-12-31 at 16 03 58

@frenzibyte frenzibyte added type/cosmetic Only affects the game visually. Doesn't affect things working or not working. area:settings labels Dec 31, 2025
@frenzibyte frenzibyte requested a review from a team December 31, 2025 23:13
@peppy peppy self-requested a review January 1, 2026 07:56
@peppy
Copy link
Member

peppy commented Jan 1, 2026

I dunno about the (disabled) text on collapsed items, seems not necessary, and actually confusing to me. It feels like I shouldn't be able to toggle it because it says this.

Also, I'd expect to be able to click anywhere on the text in such header items to perform the toggle.

@peppy
Copy link
Member

peppy commented Jan 1, 2026

2026-01-01.17.19.00.mp4

This is weird. Might be best to fix separately but definitely needs to be changed.

@peppy
Copy link
Member

peppy commented Jan 1, 2026

2026-01-01 17 21 26@2x

This is a bit too loud (visually). Should probably not be warning level.

@cihe13375
Copy link

Maybe too late to say now, but I feel that the on vs. off contrast of switches is too subtle.

This is particularly confusing for vertical switches. It is not clear if "up" is on or "down" is on.

public SettingsButtonV2()
{
RelativeSizeAxes = Axes.X;
Margin = new MarginPadding { Vertical = -1.5f };
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike SettingsButton with -5, the vertical margin here is set to -1.5 instead. This is because the overall settings spacing specified in flow containers has been reduced from 14 to 7, and to preserve the button spacing with that change, -5 + 7 / 2 = -1.5.

Anchor = Anchor.TopRight,
Origin = Anchor.TopRight
Origin = Anchor.TopRight,
Spacing = new Vector2(-6, 0),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the settings margin changes, the select button row got overflowed with the key buttons:

Image

The spacing adjustment above alleviates the issue for now:

Image

Comment on lines -486 to +498
content.AutoSizeDuration = 500;
content.AutoSizeDuration = 250;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duration set here felt too slow for a snappy animation.

Before:

CleanShot.2026-01-01.at.06.45.52.mp4

After:

CleanShot.2026-01-01.at.06.45.32.mp4

Comment on lines +58 to +61
bool negativeBottomMargin = !handlerEnabled.Value || FlowContent.Count == 0;
HeaderContainer.TransformTo(nameof(Margin), new MarginPadding { Bottom = negativeBottomMargin ? -15 : 0 }, 300, Easing.OutQuint);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is added to not have too much vertical gap between disabled input subsections.

Before:

Image

After:

Image

Comment on lines -342 to +378
windowModeDropdown.SetNoticeText(LayoutSettingsStrings.FullscreenMacOSNote, true);
windowModeDropdownNote.Value = new SettingsNote.Data(LayoutSettingsStrings.FullscreenMacOSNote, SettingsNote.Type.Critical);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've increased the level of this note from warning to critical as system-wide freezes may occur in this mode.

Comment on lines -360 to +396
windowModeDropdown.SetNoticeText(LayoutSettingsStrings.CheckingForFullscreenCapabilities, true);
windowModeDropdownNote.Value = new SettingsNote.Data(LayoutSettingsStrings.CheckingForFullscreenCapabilities, SettingsNote.Type.Informational);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've dropped down the level of this note as it's not really a "warning" yet. Informational makes most sense.

@frenzibyte
Copy link
Member Author

Also, I'd expect to be able to click anywhere on the text in such header items to perform the toggle.

I've added this but I'm not 100% sure about the hover design and feedback there:

CleanShot.2026-01-01.at.09.20.52.mp4

@Joppe27
Copy link
Contributor

Joppe27 commented Jan 1, 2026

This is particularly confusing for vertical switches. It is not clear if "up" is on or "down" is on.

Sorry to +1 but I very much agree with this. I almost never see vertical switches in software, and think it's a suboptimal design because there is no established on or off position like you said. I personally expected "up" to be the on position because that's how most real-life switches work in my head. Wouldn't a horizontal switch look just as good here?

@tadatomix
Copy link
Contributor

I'm sorry, I understand that localization was never a highest priority, but in some of those locales text is being overflown in places where it wasn't before. The reason why I want to mention it, because neither OP, nor somebody else has mentioned this issue, while I think it's quite serious since in some cases entire words can be cut

Since my native language is Russian, I'll show examples with that language, however it does seem to also affect Belarusian, Finnish, Bulgarian, Catalan, Danish, German, Greek, Spanish, French, Croatian, Hungarian, Indonesian, Italian, Japanese, Lithuanian, Latvian, Malay, Dutch, Norwegian, Polish, Portuguese, Brazilian, Romanian, Slovene, Serbian, Swedish, Turkish and Ukrainian in different (and sometimes more minor) cases.

I don't list cases with these buttons, since they happen in release builds as well and weren't changed in this PR
изображение

Here's the list of overflowing cases I found in Russian:

This PR 2026.102.1-lazer Was one line sentence in release build
изображение изображение
изображение изображение
изображение изображение
изображение изображение
изображение изображение
изображение изображение
изображение изображение
изображение изображение
изображение изображение
изображение изображение
изображение изображение
изображение изображение

List of raw keys where I saw this issue, since otherwise it this comparison would be hard to see the difference: [[TabletSettings:tip_pressure_for_click]], [[MouseSettings:disable_mouse_wheel_volume_adjust]], [[UserInterface:hold_to_confirm_activation_time]], [[GraphicsSettings:combo_colour_normalisation]], [[GameplaySettings:fade_playfield_when_health_low]], [[GameplaySettings:show_health_display_when_cant_fail]], [[SkinSettings:auto_cursor_size]], [[GameplaySettings:increase_first_object_visibility]], [[AudioSettings:adjust_beatmap_offset_automatically]], [[GraphicsSettings:minimise_on_focus_loss]], [[OnlineSettings:show_explicit_content]].

Some of them can be shortened with synonyms, I agree. But what to do with those that are couldn't be shortened? What to do with different languages? I understand that this layout was perfectly aligned only for English, but in places where text could move onto another line, now it's just being overflown, I can't suggest you what to do with this, but I just want you to know that this issue does exist and hope there will be something that could fix it

@peppy
Copy link
Member

peppy commented Jan 5, 2026

Definitely need some word wrap for those, well spotted.

@frenzibyte
Copy link
Member Author

frenzibyte commented Jan 8, 2026

The button text overlaps shown above will not be addressed in this PR / for this PR to proceed. It is too involved to be included here.

@Theighlin
Copy link

-skin dropdown: doesn't show "type to search" anymore when active
-all dropdowns: typed search shows on top of dropdown label

Registrazione.dello.schermo.2026-01-09.105028.mp4

when closing a dropdown, the easing comes to a stop and then there's a final stutter that gets below elements into the correct place.

Registrazione.dello.schermo.2026-01-09.105424.mp4

peppy pushed a commit that referenced this pull request Jan 13, 2026
Addresses concerns in
#36193 (comment) (excluding
the last part, that is too involved and I can't imagine any workaround
for it due to how strict the `Dropdown` structure is). Also adds
truncation/padding to header label and search bar.

Preview:


https://github.com/user-attachments/assets/8885cb90-44dc-42ee-af21-cb33f7723e63

scalingSettings.AutoSizeAxes = scalingMode.Value != ScalingMode.Off ? Axes.Y : Axes.None;
scalingSettings.ForEach(s =>
scalingSettings.ForEach(item =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this to just store an array of the stuff we want to change (during construction) and address directly, rather than ForEach with comparisons and casts and stuff.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following diff attempts to achieve the suggestion, but the end result doesn't quite feel like worth it just to not have Control exposed:

diff
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
index 81a3029c2d..a04e103093 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
@@ -31,6 +31,8 @@ public partial class LayoutSettings : SettingsSubsection
 
         private FillFlowContainer<SettingsItemV2> scalingSettings = null!;
 
+        private readonly List<FormSliderBar<float>> scalingSettingsControls = new List<FormSliderBar<float>>();
+
         private readonly Bindable<Display> currentDisplay = new Bindable<Display>();
 
         private Bindable<ScalingMode> scalingMode = null!;
@@ -60,7 +62,8 @@ public partial class LayoutSettings : SettingsSubsection
         private FormDropdown<Display> displayDropdown = null!;
         private FormDropdown<WindowMode> windowModeDropdown = null!;
 
-        private FormSliderBar<float> dimSlider = null!;
+        private SettingsItemV2 dimSliderSetting = null!;
+        private FormSliderBar<float> dimSliderSettingControl = null!;
 
         private readonly Bindable<SettingsNote.Data?> windowModeDropdownNote = new Bindable<SettingsNote.Data?>();
 
@@ -194,7 +197,7 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, Gam
                             Current = scalingPositionX,
                             KeyboardStep = 0.01f,
                             DisplayAsPercentage = true,
-                        }.With(bindPreviewEvent))
+                        }.With(setupScalingControl))
                         {
                             Keywords = new[] { "screen", "scaling" },
                         },
@@ -204,7 +207,7 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, Gam
                             Current = scalingPositionY,
                             KeyboardStep = 0.01f,
                             DisplayAsPercentage = true,
-                        }.With(bindPreviewEvent))
+                        }.With(setupScalingControl))
                         {
                             Keywords = new[] { "screen", "scaling" },
                         },
@@ -214,7 +217,7 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, Gam
                             Current = scalingSizeX,
                             KeyboardStep = 0.01f,
                             DisplayAsPercentage = true,
-                        }.With(bindPreviewEvent))
+                        }.With(setupScalingControl))
                         {
                             Keywords = new[] { "screen", "scaling" },
                         },
@@ -224,17 +227,17 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, Gam
                             Current = scalingSizeY,
                             KeyboardStep = 0.01f,
                             DisplayAsPercentage = true,
-                        }.With(bindPreviewEvent))
+                        }.With(setupScalingControl))
                         {
                             Keywords = new[] { "screen", "scaling" },
                         },
-                        new SettingsItemV2(dimSlider = new FormSliderBar<float>
+                        dimSliderSetting = new SettingsItemV2(dimSliderSettingControl = new FormSliderBar<float>
                         {
                             Caption = GameplaySettingsStrings.BackgroundDim,
                             Current = scalingBackgroundDim,
                             KeyboardStep = 0.01f,
                             DisplayAsPercentage = true,
-                        }.With(bindPreviewEvent)),
+                        }.With(setupScalingControl)),
                     }
                 },
             };
@@ -242,6 +245,21 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, Gam
             fullscreenCapability.BindValueChanged(_ => Schedule(updateScreenModeWarning), true);
         }
 
+        private void setupScalingControl(FormSliderBar<float> slider)
+        {
+            scalingSettingsControls.Add(slider);
+
+            slider.Current.ValueChanged += _ =>
+            {
+                switch (scalingMode.Value)
+                {
+                    case ScalingMode.Gameplay:
+                        showPreview();
+                        break;
+                }
+            };
+        }
+
         protected override void LoadComplete()
         {
             base.LoadComplete();
@@ -336,15 +354,16 @@ void updateScalingModeVisibility()
                 scalingSettings.AutoSizeAxes = scalingMode.Value != ScalingMode.Off ? Axes.Y : Axes.None;
                 scalingSettings.ForEach(item =>
                 {
-                    FormSliderBar<float> slider = (FormSliderBar<float>)item.Control;
-
-                    if (slider == dimSlider)
+                    if (item == dimSliderSetting)
                         item.CanBeShown.Value = scalingMode.Value == ScalingMode.Everything || scalingMode.Value == ScalingMode.ExcludeOverlays;
                     else
-                    {
-                        slider.TransferValueOnCommit = scalingMode.Value == ScalingMode.Everything;
                         item.CanBeShown.Value = scalingMode.Value != ScalingMode.Off;
-                    }
+                });
+
+                scalingSettingsControls.ForEach(slider =>
+                {
+                    if (slider != dimSliderSettingControl)
+                        slider.TransferValueOnCommit = scalingMode.Value == ScalingMode.Everything;
                 });
             }
         }
@@ -412,19 +431,6 @@ private void updateScreenModeWarning()
             }
         }
 
-        private void bindPreviewEvent(FormSliderBar<float> slider)
-        {
-            slider.Current.ValueChanged += _ =>
-            {
-                switch (scalingMode.Value)
-                {
-                    case ScalingMode.Gameplay:
-                        showPreview();
-                        break;
-                }
-            };
-        }
-
         private Drawable? preview;
 
         private void showPreview()

Particularly, it seems the suggestion's goal is to replace the highlighted ForEach loop with a simpler one that doesn't require pattern matching, but in reality the surrounding code changed from one loop into two loops instead (because there's a flag in SettingsItemV2 that's updated in this code path):

image

For now, I've at least omitted the first contextless commit and split out the LayoutSettings change into its own commit with the required structural change: 874e3ad

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just going to go with what you already have at this point. I won't comment on my thoughts on the structure of things for now. Hopefully this is a one-off.

@frenzibyte frenzibyte force-pushed the new-settings/overlay branch from 3186c8e to ed091dc Compare January 14, 2026 07:17
@peppy peppy self-requested a review January 14, 2026 10:19
Copy link
Collaborator

@bdach bdach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

structural review only, scope of review limited to the high level components, I'm not going through the 60 other files to check if there's anything structural in there

I guess you could merge this as is if you really want to, nothing here is blocking per se

[BackgroundDependencyLoader]
private void load()
{
HeaderContainer.Child = new ToggleableHeader(Header, IsToggleable)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is dodgy as hell, the preferred solution would be to either have a CreateHeader() method in the base, or just remove the header from the base and have inheritors insert it themselves

public sealed partial class SettingsItemV2 : CompositeDrawable, ISettingsItem, IConditionalFilterable
{
private readonly IFormControl control;
public readonly IFormControl Control;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure how much of a stink to kick up about this one because it appears to be done for the sake of a single usage in LayoutSettings and could be obviated by trivial if a little annoying local workaround or even .ChildrenOfType<T>(). I guess I can let it slide but will watch closely nothing stupid happens further with this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take a quick review of:

#36193 (comment)
#36193 (comment)
https://discord.com/channels/90072389919997952/1458132886946451497/1460540323011887238

and see if you agree with me turning a blind eye here or not.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I've seen those and as I mentioned above the possible workarounds here are wonky so I'm not going to insist on changes. can stay as is for now.

@Joehuu
Copy link
Member

Joehuu commented Jan 15, 2026

Oops didn't mean to push that commit here, will remove.

peppy
peppy previously approved these changes Jan 22, 2026
@peppy peppy enabled auto-merge January 22, 2026 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:settings size/XXL type/cosmetic Only affects the game visually. Doesn't affect things working or not working.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Update settings to use new "form" style controls

8 participants