From c6a8d9a15074ec129041237eb817b05c7c0ed7e4 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 19 Dec 2025 01:27:01 -0500 Subject: [PATCH 01/14] Add extra form controls in test scene --- .../UserInterface/TestSceneFormControls.cs | 273 ++++++++++++------ 1 file changed, 183 insertions(+), 90 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFormControls.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFormControls.cs index 2003f5de83..a1872b30de 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFormControls.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFormControls.cs @@ -1,15 +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.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; +using osu.Game.Overlays; using osu.Game.Screens.Edit.Setup; using osuTK; @@ -25,109 +28,199 @@ namespace osu.Game.Tests.Visual.UserInterface protected override Drawable CreateContent() => new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, - Child = new PopoverContainer + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Child = new OsuScrollContainer + new BackgroundBox { RelativeSizeAxes = Axes.Both, - Child = new FillFlowContainer + }, + new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Child = new OsuScrollContainer { - AutoSizeAxes = Axes.Y, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 400, - Direction = FillDirection.Vertical, - Spacing = new Vector2(5), - Padding = new MarginPadding(10), - Children = new Drawable[] + RelativeSizeAxes = Axes.Both, + Child = new FillFlowContainer { - new FormTextBox + AutoSizeAxes = Axes.Y, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 400, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5), + Padding = new MarginPadding(10), + Children = new Drawable[] { - Caption = "Artist", - HintText = "Poot artist here!", - PlaceholderText = "Here is an artist", - TabbableContentContainer = this, - }, - new FormTextBox - { - Caption = "Artist", - HintText = "Poot artist here!", - PlaceholderText = "Here is an artist", - Current = { Disabled = true }, - TabbableContentContainer = this, - }, - new FormNumberBox(allowDecimals: true) - { - Caption = "Number", - HintText = "Insert your favourite number", - PlaceholderText = "Mine is 42!", - TabbableContentContainer = this, - }, - new FormCheckBox - { - Caption = EditorSetupStrings.LetterboxDuringBreaks, - HintText = EditorSetupStrings.LetterboxDuringBreaksDescription, - }, - new FormCheckBox - { - Caption = EditorSetupStrings.LetterboxDuringBreaks, - HintText = EditorSetupStrings.LetterboxDuringBreaksDescription, - Current = { Disabled = true }, - }, - new FormSliderBar - { - Caption = "Slider", - Current = new BindableFloat + new FormTextBox { - MinValue = 0, - MaxValue = 10, - Value = 5, - Precision = 0.1f, + Caption = "Artist", + HintText = "Poot artist here!", + PlaceholderText = "Here is an artist", + TabbableContentContainer = this, }, - TabbableContentContainer = this, - }, - new FormEnumDropdown - { - Caption = EditorSetupStrings.EnableCountdown, - HintText = EditorSetupStrings.CountdownDescription, - }, - new FormFileSelector - { - Caption = "File selector", - PlaceholderText = "Select a file", - }, - new FormBeatmapFileSelector(true) - { - Caption = "File selector with intermediate choice dialog", - PlaceholderText = "Select a file", - }, - new FormColourPalette - { - Caption = "Combo colours", - Colours = + new FormTextBox { - Colour4.Red, - Colour4.Green, - Colour4.Blue, - Colour4.Yellow, - } - }, - new FormButton - { - Caption = "No text in button", - Action = () => { }, - }, - new FormButton - { - Caption = "Text in button which is pretty long and is very likely to wrap", - ButtonText = "Foo the bar", - Action = () => { }, + Caption = "Artist", + HintText = "Poot artist here!", + PlaceholderText = "Here is an artist", + Current = { Disabled = true }, + TabbableContentContainer = this, + }, + new FormNumberBox(allowDecimals: true) + { + Caption = "Number", + HintText = "Insert your favourite number", + PlaceholderText = "Mine is 42!", + TabbableContentContainer = this, + }, + new FormCheckBox + { + Caption = EditorSetupStrings.LetterboxDuringBreaks, + HintText = EditorSetupStrings.LetterboxDuringBreaksDescription, + }, + new FormCheckBox + { + Caption = EditorSetupStrings.LetterboxDuringBreaks, + HintText = EditorSetupStrings.LetterboxDuringBreaksDescription, + Current = { Disabled = true }, + }, + new FormCheckBox + { + Caption = EditorSetupStrings.LetterboxDuringBreaks, + HintText = EditorSetupStrings.LetterboxDuringBreaksDescription, + Current = { Value = true, Disabled = true }, + }, + new FormSliderBar + { + Caption = "Slider", + HintText = "Slider hint", + Current = new BindableFloat + { + MinValue = 0, + MaxValue = 10, + Value = 5, + Precision = 0.1f, + }, + TabbableContentContainer = this, + }, + new FormSliderBar + { + Caption = "Slider", + HintText = "Slider hint", + Current = new BindableFloat + { + MinValue = 0, + MaxValue = 10, + Value = 5, + Precision = 0.1f, + Disabled = true, + }, + TransferValueOnCommit = true, + TabbableContentContainer = this, + }, + new FormSliderBar + { + Caption = "Slider (percentage)", + HintText = "Percentage slider hint", + Current = new BindableFloat + { + MinValue = 0, + MaxValue = 1, + Value = 0.2f, + Precision = 0.0001f, + }, + DisplayAsPercentage = true, + TabbableContentContainer = this, + }, + new FormSliderBar + { + Caption = "Slider (custom)", + HintText = "Custom slider hint", + Current = new BindableFloat + { + MinValue = 0, + MaxValue = 1, + Value = 0.2f, + Precision = 0.0001f, + }, + LabelFormat = v => $"{v * 100:0.00} funometer", + TooltipFormat = v => $"This setting has the value set to {v * 100:0.00} funometer.", + TabbableContentContainer = this, + }, + new FormSliderBar + { + Caption = "Slider (custom)", + HintText = "Custom slider hint", + Current = new BindableFloat + { + MinValue = 0, + MaxValue = 1, + Value = 0.2f, + Precision = 0.0001f, + Disabled = true, + }, + TransferValueOnCommit = true, + LabelFormat = v => $"{v * 100:0.00} funometer", + TooltipFormat = v => $"This setting has the value set to {v * 100:0.00} funometer.", + TabbableContentContainer = this, + }, + new FormEnumDropdown + { + Caption = EditorSetupStrings.EnableCountdown, + HintText = EditorSetupStrings.CountdownDescription, + }, + new FormEnumDropdown + { + Caption = EditorSetupStrings.EnableCountdown, + HintText = EditorSetupStrings.CountdownDescription, + Current = { Disabled = true }, + }, + new FormFileSelector + { + Caption = "File selector", + PlaceholderText = "Select a file", + }, + new FormBeatmapFileSelector(true) + { + Caption = "File selector with intermediate choice dialog", + PlaceholderText = "Select a file", + }, + new FormColourPalette + { + Caption = "Combo colours", + Colours = + { + Colour4.Red, + Colour4.Green, + Colour4.Blue, + Colour4.Yellow, + } + }, + new FormButton + { + Caption = "No text in button", + Action = () => { }, + }, + new FormButton + { + Caption = "Text in button which is pretty long and is very likely to wrap", + ButtonText = "Foo the bar", + Action = () => { }, + }, }, }, }, - }, + } } }; + + private partial class BackgroundBox : Box + { + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Colour = colourProvider.Background4; + } + } } } From a2872ee87071624911fc7540e279552179dbc272 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 19 Dec 2025 01:21:39 -0500 Subject: [PATCH 02/14] Update "disabled" visual feedback of form controls --- .../Graphics/UserInterfaceV2/FormCheckBox.cs | 18 +++++------- .../Graphics/UserInterfaceV2/FormDropdown.cs | 29 +++++++++---------- .../Graphics/UserInterfaceV2/FormSliderBar.cs | 11 ++++--- .../Graphics/UserInterfaceV2/FormTextBox.cs | 26 +++++++---------- 4 files changed, 39 insertions(+), 45 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs b/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs index 586f6546ab..df5b3c433b 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs @@ -147,20 +147,18 @@ namespace osu.Game.Graphics.UserInterfaceV2 private void updateState() { - background.Colour = Current.Disabled ? colourProvider.Background4 : colourProvider.Background5; - caption.Colour = Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content2; - checkbox.Colour = Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content1; - text.Colour = Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content1; + caption.Colour = Current.Disabled ? colourProvider.Background1 : colourProvider.Content2; + text.Colour = Current.Disabled ? colourProvider.Background1 : colourProvider.Content1; text.Text = Current.Value ? CommonStrings.Enabled : CommonStrings.Disabled; - if (!Current.Disabled) - { - BorderThickness = IsHovered ? 2 : 0; + // use FadeColour to override any existing colour transform (i.e. FlashColour on click). + background.FadeColour(IsHovered + ? ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4) + : colourProvider.Background5); - if (IsHovered) - BorderColour = colourProvider.Light4; - } + BorderThickness = IsHovered ? 2 : 0; + BorderColour = Current.Disabled ? colourProvider.Dark1 : colourProvider.Light4; } public IEnumerable FilterTerms => Caption.Yield(); diff --git a/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs b/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs index a12d166f56..4b9c94d101 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs @@ -210,29 +210,26 @@ namespace osu.Game.Graphics.UserInterfaceV2 { label.Alpha = string.IsNullOrEmpty(SearchBar.SearchTerm.Value) ? 1 : 0; - caption.Colour = Dropdown.Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content2; - label.Colour = Dropdown.Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content1; - chevron.Colour = Dropdown.Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content1; + caption.Colour = Dropdown.Current.Disabled ? colourProvider.Background1 : colourProvider.Content2; + label.Colour = Dropdown.Current.Disabled ? colourProvider.Background1 : colourProvider.Content1; + chevron.Colour = Dropdown.Current.Disabled ? colourProvider.Background1 : colourProvider.Content1; DisabledColour = Colour4.White; bool dropdownOpen = Dropdown.Menu.State == MenuState.Open; - if (!Dropdown.Current.Disabled) - { - BorderThickness = IsHovered || dropdownOpen ? 2 : 0; + BorderThickness = IsHovered || dropdownOpen ? 2 : 0; + + if (Dropdown.Current.Disabled) + BorderColour = colourProvider.Dark1; + else BorderColour = dropdownOpen ? colourProvider.Highlight1 : colourProvider.Light4; - if (dropdownOpen) - Background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3); - else if (IsHovered) - Background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4); - else - Background.Colour = colourProvider.Background5; - } + if (dropdownOpen) + Background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3); + else if (IsHovered) + Background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4); else - { - Background.Colour = colourProvider.Background4; - } + Background.Colour = colourProvider.Background5; } private void updateChevron() diff --git a/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs b/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs index 06f9db7846..dfed778bbf 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs @@ -302,12 +302,15 @@ namespace osu.Game.Graphics.UserInterfaceV2 textBox.Alpha = 1; textBox.ReadOnly = Current.Disabled; - background.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background4 : colourProvider.Background5; - captionText.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Foreground1 : colourProvider.Content2; - textBox.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Foreground1 : colourProvider.Content1; + captionText.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background1 : colourProvider.Content2; + textBox.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background1 : colourProvider.Content1; BorderThickness = childHasFocus || IsHovered || slider.IsDragging.Value ? 2 : 0; - BorderColour = childHasFocus ? colourProvider.Highlight1 : colourProvider.Light4; + + if (Current.Disabled) + BorderColour = colourProvider.Dark1; + else + BorderColour = childHasFocus ? colourProvider.Highlight1 : colourProvider.Light4; if (childHasFocus) background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3); diff --git a/osu.Game/Graphics/UserInterfaceV2/FormTextBox.cs b/osu.Game/Graphics/UserInterfaceV2/FormTextBox.cs index 4981557e37..6429eb6e96 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormTextBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormTextBox.cs @@ -189,26 +189,22 @@ namespace osu.Game.Graphics.UserInterfaceV2 textBox.ReadOnly = disabled; textBox.Alpha = 1; - caption.Colour = disabled ? colourProvider.Foreground1 : colourProvider.Content2; + caption.Colour = disabled ? colourProvider.Background1 : colourProvider.Content2; textBox.Colour = disabled ? colourProvider.Foreground1 : colourProvider.Content1; - if (!disabled) - { - BorderThickness = IsHovered || textBox.Focused.Value ? 2 : 0; + BorderThickness = IsHovered || textBox.Focused.Value ? 2 : 0; + + if (disabled) + BorderColour = colourProvider.Dark1; + else BorderColour = textBox.Focused.Value ? colourProvider.Highlight1 : colourProvider.Light4; - if (textBox.Focused.Value) - background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3); - else if (IsHovered) - background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4); - else - background.Colour = colourProvider.Background5; - } + if (textBox.Focused.Value) + background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3); + else if (IsHovered) + background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4); else - { - BorderThickness = 0; - background.Colour = colourProvider.Background4; - } + background.Colour = colourProvider.Background5; } internal partial class InnerTextBox : OsuTextBox From 339aad42a7a261faf6be7e53375dcdee7baecb6d Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 19 Dec 2025 01:28:29 -0500 Subject: [PATCH 03/14] Use switch button for form checkbox controls --- .../UserInterface/TestSceneSwitchButton.cs | 11 ++++ .../Graphics/UserInterfaceV2/FormCheckBox.cs | 9 ++- .../Graphics/UserInterfaceV2/SwitchButton.cs | 66 +++++++++++-------- 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs index f3ab5dbff8..93b96dd5ec 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterfaceV2; using osuTK.Input; @@ -42,5 +43,15 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("toggle bindable", () => bindable.Toggle()); AddStep("toggle bindable", () => bindable.Toggle()); } + + [Test] + public void TestDisabledState() + { + AddToggleStep("toggle disabled", v => + { + if (switchButton.IsNotNull()) + switchButton.Current.Disabled = v; + }); + } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs b/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs index df5b3c433b..01a197c99c 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs @@ -16,7 +16,6 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Overlays; @@ -45,7 +44,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 private Box background = null!; private FormFieldCaption caption = null!; private OsuSpriteText text = null!; - private Nub checkbox = null!; private Sample? sampleChecked; private Sample? sampleUnchecked; @@ -89,7 +87,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, }, - checkbox = new Nub + new FormSwitchButton { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, @@ -170,5 +168,10 @@ namespace osu.Game.Graphics.UserInterfaceV2 public void SetDefault() => Current.SetDefault(); public bool IsDisabled => Current.Disabled; + + private partial class FormSwitchButton : SwitchButton + { + public override bool PropagatePositionalInputSubTree => false; + } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs index cf569a73ca..fe72775bb5 100644 --- a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs @@ -4,7 +4,6 @@ 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; using osu.Framework.Graphics.Colour; @@ -26,10 +25,10 @@ namespace osu.Game.Graphics.UserInterfaceV2 private readonly Box fill; private readonly Container switchContainer; private readonly Drawable switchCircle; - private readonly CircularBorderContainer circularContainer; + private readonly CircularContainer circularContainer; - private Color4 enabledColour; - private Color4 disabledColour; + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; private Sample? sampleChecked; private Sample? sampleUnchecked; @@ -38,7 +37,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { Size = new Vector2(45, 20); - InternalChild = circularContainer = new CircularBorderContainer + InternalChild = circularContainer = new CircularContainer { RelativeSizeAxes = Axes.Both, BorderColour = Color4.White, @@ -73,14 +72,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 } [BackgroundDependencyLoader(true)] - private void load(OverlayColourProvider? colourProvider, OsuColour colours, AudioManager audio) + private void load(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"); } @@ -89,27 +82,29 @@ namespace osu.Game.Graphics.UserInterfaceV2 { base.LoadComplete(); - Current.BindValueChanged(updateState, true); + Current.BindDisabledChanged(_ => updateColours()); + Current.BindValueChanged(_ => updateState(), true); + FinishTransforms(true); } - private void updateState(ValueChangedEvent state) + private void updateState() { - switchCircle.MoveToX(state.NewValue ? switchContainer.DrawWidth - switchCircle.DrawWidth : 0, 200, Easing.OutQuint); - fill.FadeTo(state.NewValue ? 1 : 0, 250, Easing.OutQuint); + switchCircle.MoveToX(Current.Value ? switchContainer.DrawWidth - switchCircle.DrawWidth : 0, 200, Easing.OutQuint); + fill.FadeTo(Current.Value ? 1 : 0, 250, Easing.OutQuint); - updateBorder(); + updateColours(); } protected override bool OnHover(HoverEvent e) { - updateBorder(); + updateColours(); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - updateBorder(); + updateColours(); base.OnHoverLost(e); } @@ -123,15 +118,34 @@ namespace osu.Game.Graphics.UserInterfaceV2 sampleUnchecked?.Play(); } - private void updateBorder() + private void updateColours() { - circularContainer.TransformBorderTo((Current.Value ? enabledColour : disabledColour).Lighten(IsHovered ? 0.3f : 0)); - } + ColourInfo targetSwitchColour; + ColourInfo targetBorderColour; - private partial class CircularBorderContainer : CircularContainer - { - public void TransformBorderTo(ColourInfo colour) - => this.TransformTo(nameof(BorderColour), colour, 250, Easing.OutQuint); + if (Current.Disabled) + { + if (Current.Value) + targetBorderColour = colourProvider.Dark1.Opacity(0.5f); + else + targetBorderColour = colourProvider.Background2.Opacity(0.5f); + + targetSwitchColour = colourProvider.Dark1.Opacity(0.5f); + fill.Colour = colourProvider.Background5; + } + else + { + if (Current.Value) + targetBorderColour = IsHovered ? colourProvider.Highlight1.Lighten(0.3f) : colourProvider.Highlight1; + else + targetBorderColour = IsHovered ? colourProvider.Background1 : colourProvider.Background2; + + targetSwitchColour = colourProvider.Highlight1; + fill.Colour = colourProvider.Background4; + } + + switchContainer.FadeColour(targetSwitchColour, 250, Easing.OutQuint); + circularContainer.TransformTo(nameof(BorderColour), targetBorderColour, 250, Easing.OutQuint); } } } From c2c1fa4d4c10e662a310f502245b66556369a47f Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 19 Dec 2025 01:28:43 -0500 Subject: [PATCH 04/14] Add right margin to form dropdown chevron --- osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs b/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs index 4b9c94d101..1f6928b22d 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs @@ -171,6 +171,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Size = new Vector2(16), + Margin = new MarginPadding { Right = 5 }, }, }; From 4ee7e6f787435188a7a1d58832d5fb6e1a6ebf74 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 19 Dec 2025 01:42:32 -0500 Subject: [PATCH 05/14] Add label and tooltip formatting to form sliders --- .../Graphics/UserInterface/OsuSliderBar.cs | 6 +- .../Graphics/UserInterfaceV2/FormSliderBar.cs | 62 ++++++++++++++++++- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index ca95d45042..3aeb95ac01 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -46,7 +46,7 @@ namespace osu.Game.Graphics.UserInterface protected override void LoadComplete() { base.LoadComplete(); - CurrentNumber.BindValueChanged(current => TooltipText = GetDisplayableValue(current.NewValue), true); + CurrentNumber.BindValueChanged(current => TooltipText = GetTooltipText(current.NewValue), true); } protected override void OnUserChange(T value) @@ -55,7 +55,7 @@ namespace osu.Game.Graphics.UserInterface playSample(value); - TooltipText = GetDisplayableValue(value); + TooltipText = GetTooltipText(value); } private void playSample(T value) @@ -83,6 +83,6 @@ namespace osu.Game.Graphics.UserInterface channel.Play(); } - public LocalisableString GetDisplayableValue(T value) => value.ToStandardFormattedString(max_decimal_digits, DisplayAsPercentage); + protected virtual LocalisableString GetTooltipText(T value) => value.ToStandardFormattedString(max_decimal_digits, DisplayAsPercentage); } } diff --git a/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs b/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs index dfed778bbf..6e3c059470 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs @@ -17,6 +17,8 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Game.Extensions; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Overlays; @@ -97,9 +99,31 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } + /// + /// Whether to format the tooltip as a percentage or the actual value. + /// + public bool DisplayAsPercentage { get; init; } + + /// + /// Whether sound effects should play when adjusting this slider. + /// + public bool PlaySamplesOnAdjust { get; init; } + + /// + /// The string formatting function to use for the value label. + /// + public Func LabelFormat { get; init; } + + /// + /// The string formatting function to use for the slider's tooltip text. + /// If not provided, is used. + /// + public Func TooltipFormat { get; init; } + private Box background = null!; private Box flashLayer = null!; private FormTextBox.InnerTextBox textBox = null!; + private OsuSpriteText valueLabel = null!; private InnerSlider slider = null!; private FormFieldCaption captionText = null!; private IFocusManager focusManager = null!; @@ -109,6 +133,12 @@ namespace osu.Game.Graphics.UserInterfaceV2 private readonly Bindable currentLanguage = new Bindable(); + public FormSliderBar() + { + LabelFormat ??= defaultLabelFormat; + TooltipFormat ??= v => LabelFormat(v); + } + [BackgroundDependencyLoader] private void load(OsuColour colours, OsuGame? game) { @@ -153,6 +183,10 @@ namespace osu.Game.Graphics.UserInterfaceV2 Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, Width = 0.5f, + // the textbox is hidden when the control is unfocused, + // but clicking on the label should reach the textbox, + // therefore make it always present. + AlwaysPresent = true, CommitOnFocusLost = true, SelectAllOnFocus = true, OnInputError = () => @@ -162,6 +196,14 @@ namespace osu.Game.Graphics.UserInterfaceV2 }, TabbableContentContainer = tabbableContentContainer, }, + valueLabel = new TruncatingSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Width = 0.5f, + Padding = new MarginPadding { Right = 5 }, + }, slider = new InnerSlider { Anchor = Anchor.CentreRight, @@ -170,6 +212,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 Width = 0.5f, Current = currentNumberInstantaneous, OnCommit = () => current.Value = currentNumberInstantaneous.Value, + TooltipFormat = TooltipFormat, + DisplayAsPercentage = DisplayAsPercentage, + PlaySamplesOnAdjust = PlaySamplesOnAdjust, } }, }, @@ -218,6 +263,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 current.CopyTo(currentNumberInstantaneous); currentLanguage.BindValueChanged(_ => Schedule(updateValueDisplay)); + currentNumberInstantaneous.BindDisabledChanged(_ => updateState()); currentNumberInstantaneous.BindValueChanged(e => { if (!TransferValueOnCommit) @@ -299,11 +345,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 { bool childHasFocus = slider.Focused.Value || textBox.Focused.Value; - textBox.Alpha = 1; - textBox.ReadOnly = Current.Disabled; + textBox.ReadOnly = currentNumberInstantaneous.Disabled; + textBox.Alpha = textBox.Focused.Value ? 1 : 0; + valueLabel.Alpha = textBox.Focused.Value ? 0 : 1; captionText.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background1 : colourProvider.Content2; textBox.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background1 : colourProvider.Content1; + valueLabel.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background1 : colourProvider.Content1; BorderThickness = childHasFocus || IsHovered || slider.IsDragging.Value ? 2 : 0; @@ -324,16 +372,22 @@ namespace osu.Game.Graphics.UserInterfaceV2 { if (updatingFromTextBox) return; - textBox.Text = slider.GetDisplayableValue(currentNumberInstantaneous.Value).ToString(); + textBox.Text = currentNumberInstantaneous.Value.ToStandardFormattedString(5); + valueLabel.Text = LabelFormat(currentNumberInstantaneous.Value); } + private LocalisableString defaultLabelFormat(T value) => currentNumberInstantaneous.Value.ToStandardFormattedString(5, DisplayAsPercentage); + private partial class InnerSlider : OsuSliderBar { public BindableBool Focused { get; } = new BindableBool(); public BindableBool IsDragging { get; set; } = new BindableBool(); + public Action? OnCommit { get; set; } + public required Func TooltipFormat { get; init; } + private Box leftBox = null!; private Box rightBox = null!; private InnerSliderNub nub = null!; @@ -473,6 +527,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 return result; } + + protected override LocalisableString GetTooltipText(T value) => TooltipFormat(value); } private partial class InnerSliderNub : Circle From cb32d2da3e9fc780138d7d604a1ff8c62be8d8a0 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Mon, 22 Dec 2025 20:19:36 -0500 Subject: [PATCH 06/14] Make sound playback consistent across all disabled controls --- osu.Game/Graphics/UserInterface/OsuSliderBar.cs | 4 +++- osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs | 5 +++++ osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs | 5 ++++- osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs | 4 +++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 3aeb95ac01..f584e1e5d9 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -33,6 +33,7 @@ namespace osu.Game.Graphics.UserInterface private const int max_decimal_digits = 5; private Sample sample = null!; + private Sample sampleDisabled = null!; private double lastSampleTime; private T lastSampleValue; @@ -41,6 +42,7 @@ namespace osu.Game.Graphics.UserInterface private void load(AudioManager audio) { sample = audio.Samples.Get(@"UI/notch-tick"); + sampleDisabled = audio.Samples.Get(@"UI/default-select-disabled"); } protected override void LoadComplete() @@ -72,7 +74,7 @@ namespace osu.Game.Graphics.UserInterface lastSampleValue = value; lastSampleTime = Clock.CurrentTime; - var channel = sample.GetChannel(); + var channel = Current.Disabled ? sampleDisabled.GetChannel() : sample.GetChannel(); channel.Frequency.Value = 0.99f + RNG.NextDouble(0.02f) + NormalizedValue * 0.2f; diff --git a/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs b/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs index 01a197c99c..43e42faa24 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs @@ -47,6 +47,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 private Sample? sampleChecked; private Sample? sampleUnchecked; + private Sample? sampleDisabled; [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -99,6 +100,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 sampleChecked = audio.Samples.Get(@"UI/check-on"); sampleUnchecked = audio.Samples.Get(@"UI/check-off"); + sampleDisabled = audio.Samples.Get(@"UI/default-select-disabled"); } protected override void LoadComplete() @@ -140,6 +142,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 { if (!Current.Disabled) Current.Value = !Current.Value; + else + sampleDisabled?.Play(); + return true; } diff --git a/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs b/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs index 1f6928b22d..1c595e1386 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs @@ -175,7 +175,10 @@ namespace osu.Game.Graphics.UserInterfaceV2 }, }; - AddInternal(new HoverClickSounds()); + AddInternal(new HoverClickSounds + { + Enabled = { BindTarget = Enabled }, + }); } protected override void LoadComplete() diff --git a/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs b/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs index 6e3c059470..884ae91aa8 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs @@ -391,6 +391,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 private Box leftBox = null!; private Box rightBox = null!; private InnerSliderNub nub = null!; + private HoverClickSounds sounds = null!; public const float NUB_WIDTH = 10; [Resolved] @@ -439,7 +440,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } }, - new HoverClickSounds() + sounds = new HoverClickSounds() }; } @@ -499,6 +500,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 private void updateState() { + sounds.Enabled.Value = !Current.Disabled; rightBox.Colour = colourProvider.Background6; if (Current.Disabled) From a3a5b2ca92a9335a7233b89abc2801576bf7f069 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Tue, 23 Dec 2025 05:58:57 -0500 Subject: [PATCH 07/14] Fix tests not having colour providers --- .../Visual/UserInterface/TestSceneLabelledSwitchButton.cs | 5 +++++ osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs index bec517af2c..24ef51806a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledSwitchButton.cs @@ -2,14 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; namespace osu.Game.Tests.Visual.UserInterface { public partial class TestSceneLabelledSwitchButton : OsuTestScene { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + [TestCase(false)] [TestCase(true)] public void TestSwitchButton(bool hasDescription) => createSwitchButton(hasDescription); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs index 93b96dd5ec..9b2a7ae2ef 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSwitchButton.cs @@ -4,16 +4,21 @@ #nullable disable using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { public partial class TestSceneSwitchButton : OsuManualInputManagerTestScene { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + private SwitchButton switchButton; [SetUp] From a1d4d4415130b6fc54647f8943b3ce28e73a69fa Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Tue, 23 Dec 2025 05:59:51 -0500 Subject: [PATCH 08/14] Revert unintended change --- osu.Game/Graphics/UserInterface/OsuSliderBar.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index f584e1e5d9..3aeb95ac01 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -33,7 +33,6 @@ namespace osu.Game.Graphics.UserInterface private const int max_decimal_digits = 5; private Sample sample = null!; - private Sample sampleDisabled = null!; private double lastSampleTime; private T lastSampleValue; @@ -42,7 +41,6 @@ namespace osu.Game.Graphics.UserInterface private void load(AudioManager audio) { sample = audio.Samples.Get(@"UI/notch-tick"); - sampleDisabled = audio.Samples.Get(@"UI/default-select-disabled"); } protected override void LoadComplete() @@ -74,7 +72,7 @@ namespace osu.Game.Graphics.UserInterface lastSampleValue = value; lastSampleTime = Clock.CurrentTime; - var channel = Current.Disabled ? sampleDisabled.GetChannel() : sample.GetChannel(); + var channel = sample.GetChannel(); channel.Frequency.Value = 0.99f + RNG.NextDouble(0.02f) + NormalizedValue * 0.2f; From 520baf6c5080562ea745bcdfeec7686331397c0a Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Tue, 23 Dec 2025 06:03:04 -0500 Subject: [PATCH 09/14] Add explanatory note --- osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs b/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs index 43e42faa24..78feee871d 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs @@ -176,6 +176,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 private partial class FormSwitchButton : SwitchButton { + // it doesn't make sense for this to show hover state or respond to input. + // FormCheckBox already handles all of that. public override bool PropagatePositionalInputSubTree => false; } } From e4d7bc3896f0200f466c3e59d64b9956629eaeb7 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Tue, 23 Dec 2025 06:10:42 -0500 Subject: [PATCH 10/14] Seal tooltip text overrides --- osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs b/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs index 884ae91aa8..e8af73ed8b 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs @@ -386,6 +386,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 public Action? OnCommit { get; set; } + public sealed override LocalisableString TooltipText => base.TooltipText; + public required Func TooltipFormat { get; init; } private Box leftBox = null!; @@ -530,7 +532,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 return result; } - protected override LocalisableString GetTooltipText(T value) => TooltipFormat(value); + protected sealed override LocalisableString GetTooltipText(T value) => TooltipFormat(value); } private partial class InnerSliderNub : Circle From fa32be37d69f080a9cfa36a40936fa10cee4d72e Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Tue, 23 Dec 2025 06:14:48 -0500 Subject: [PATCH 11/14] Expose constant for max decimal digits display --- osu.Game/Graphics/UserInterface/OsuSliderBar.cs | 4 ++-- osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 3aeb95ac01..f9a7f475d6 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -30,7 +30,7 @@ namespace osu.Game.Graphics.UserInterface /// /// Maximum number of decimal digits to be displayed in the tooltip. /// - private const int max_decimal_digits = 5; + public const int MAX_DECIMAL_DIGITS = 5; private Sample sample = null!; @@ -83,6 +83,6 @@ namespace osu.Game.Graphics.UserInterface channel.Play(); } - protected virtual LocalisableString GetTooltipText(T value) => value.ToStandardFormattedString(max_decimal_digits, DisplayAsPercentage); + protected virtual LocalisableString GetTooltipText(T value) => value.ToStandardFormattedString(MAX_DECIMAL_DIGITS, DisplayAsPercentage); } } diff --git a/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs b/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs index e8af73ed8b..310be77f31 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormSliderBar.cs @@ -372,11 +372,11 @@ namespace osu.Game.Graphics.UserInterfaceV2 { if (updatingFromTextBox) return; - textBox.Text = currentNumberInstantaneous.Value.ToStandardFormattedString(5); + textBox.Text = currentNumberInstantaneous.Value.ToStandardFormattedString(OsuSliderBar.MAX_DECIMAL_DIGITS); valueLabel.Text = LabelFormat(currentNumberInstantaneous.Value); } - private LocalisableString defaultLabelFormat(T value) => currentNumberInstantaneous.Value.ToStandardFormattedString(5, DisplayAsPercentage); + private LocalisableString defaultLabelFormat(T value) => currentNumberInstantaneous.Value.ToStandardFormattedString(OsuSliderBar.MAX_DECIMAL_DIGITS, DisplayAsPercentage); private partial class InnerSlider : OsuSliderBar { From 95cf05029884bb1ccff19171f005eeef774bdf1e Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Tue, 23 Dec 2025 06:31:26 -0500 Subject: [PATCH 12/14] Make setter protected to fix build error --- osu.Game/Graphics/UserInterface/OsuSliderBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index f9a7f475d6..72e3b19a6a 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -25,7 +25,7 @@ namespace osu.Game.Graphics.UserInterface /// public bool DisplayAsPercentage { get; set; } - public virtual LocalisableString TooltipText { get; private set; } + public virtual LocalisableString TooltipText { get; protected set; } /// /// Maximum number of decimal digits to be displayed in the tooltip. From 033a73e969c12058349c06fd3f6ab37b2eba8b55 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Tue, 23 Dec 2025 20:18:59 -0500 Subject: [PATCH 13/14] Update switch button visuals --- .../Graphics/UserInterfaceV2/FormCheckBox.cs | 9 +--- .../Graphics/UserInterfaceV2/SwitchButton.cs | 50 +++++++++---------- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs b/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs index 78feee871d..f54cc7cc7c 100644 --- a/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/FormCheckBox.cs @@ -88,7 +88,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, }, - new FormSwitchButton + new SwitchButton { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, @@ -173,12 +173,5 @@ namespace osu.Game.Graphics.UserInterfaceV2 public void SetDefault() => Current.SetDefault(); public bool IsDisabled => Current.Disabled; - - private partial class FormSwitchButton : SwitchButton - { - // it doesn't make sense for this to show hover state or respond to input. - // FormCheckBox already handles all of that. - public override bool PropagatePositionalInputSubTree => false; - } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs index fe72775bb5..1c1dae0f31 100644 --- a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs @@ -23,9 +23,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 private const float padding = 1.25f; private readonly Box fill; - private readonly Container switchContainer; - private readonly Drawable switchCircle; - private readonly CircularContainer circularContainer; + private readonly Container nubContainer; + private readonly Drawable nub; + private readonly CircularContainer content; [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -37,7 +37,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { Size = new Vector2(45, 20); - InternalChild = circularContainer = new CircularContainer + InternalChild = content = new CircularContainer { RelativeSizeAxes = Axes.Both, BorderColour = Color4.White, @@ -55,15 +55,14 @@ namespace osu.Game.Graphics.UserInterfaceV2 { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(border_thickness + padding), - Child = switchContainer = new Container + Child = nubContainer = new Container { RelativeSizeAxes = Axes.Both, - Child = switchCircle = new CircularContainer + Child = nub = new Circle { RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, Masking = true, - Child = new Box { RelativeSizeAxes = Axes.Both } } } } @@ -90,7 +89,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 private void updateState() { - switchCircle.MoveToX(Current.Value ? switchContainer.DrawWidth - switchCircle.DrawWidth : 0, 200, Easing.OutQuint); + nub.MoveToX(Current.Value ? nubContainer.DrawWidth - nub.DrawWidth : 0, 200, Easing.OutQuint); fill.FadeTo(Current.Value ? 1 : 0, 250, Easing.OutQuint); updateColours(); @@ -120,32 +119,33 @@ namespace osu.Game.Graphics.UserInterfaceV2 private void updateColours() { - ColourInfo targetSwitchColour; - ColourInfo targetBorderColour; + ColourInfo borderColour; + ColourInfo switchColour; if (Current.Disabled) { - if (Current.Value) - targetBorderColour = colourProvider.Dark1.Opacity(0.5f); - else - targetBorderColour = colourProvider.Background2.Opacity(0.5f); - - targetSwitchColour = colourProvider.Dark1.Opacity(0.5f); - fill.Colour = colourProvider.Background5; + borderColour = colourProvider.Dark2; + switchColour = colourProvider.Dark1; + fill.Colour = colourProvider.Dark5; } else { - if (Current.Value) - targetBorderColour = IsHovered ? colourProvider.Highlight1.Lighten(0.3f) : colourProvider.Highlight1; - else - targetBorderColour = IsHovered ? colourProvider.Background1 : colourProvider.Background2; + bool hover = IsHovered && !Current.Disabled; - targetSwitchColour = colourProvider.Highlight1; - fill.Colour = colourProvider.Background4; + borderColour = hover ? colourProvider.Highlight1.Opacity(0.5f) : colourProvider.Highlight1.Opacity(0.3f); + switchColour = hover ? colourProvider.Highlight1 : colourProvider.Light4; + + if (!Current.Value) + { + borderColour = borderColour.MultiplyAlpha(0.8f); + switchColour = switchColour.MultiplyAlpha(0.8f); + } + + fill.Colour = colourProvider.Background6; } - switchContainer.FadeColour(targetSwitchColour, 250, Easing.OutQuint); - circularContainer.TransformTo(nameof(BorderColour), targetBorderColour, 250, Easing.OutQuint); + nubContainer.FadeColour(switchColour, 250, Easing.OutQuint); + content.TransformTo(nameof(BorderColour), borderColour, 250, Easing.OutQuint); } } } From 2d85fe8133802e97d4353aed90bec6b5e177847d Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Wed, 24 Dec 2025 02:22:51 -0500 Subject: [PATCH 14/14] Fix tournament screens not having colour provider cached --- osu.Game.Tournament/Screens/Setup/SetupScreen.cs | 3 +-- osu.Game.Tournament/Screens/TournamentScreen.cs | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Setup/SetupScreen.cs b/osu.Game.Tournament/Screens/Setup/SetupScreen.cs index bd368d88a0..6bed3c87df 100644 --- a/osu.Game.Tournament/Screens/Setup/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/Setup/SetupScreen.cs @@ -8,7 +8,6 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Online.API; @@ -56,7 +55,7 @@ namespace osu.Game.Tournament.Screens.Setup new Box { RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f), + Colour = ColourProvider.Background5, }, new OsuScrollContainer { diff --git a/osu.Game.Tournament/Screens/TournamentScreen.cs b/osu.Game.Tournament/Screens/TournamentScreen.cs index 1e119e0336..137153b91d 100644 --- a/osu.Game.Tournament/Screens/TournamentScreen.cs +++ b/osu.Game.Tournament/Screens/TournamentScreen.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Overlays; using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Screens @@ -15,6 +16,9 @@ namespace osu.Game.Tournament.Screens [Resolved] protected LadderInfo LadderInfo { get; private set; } = null!; + [Cached] + protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + protected TournamentScreen() { RelativeSizeAxes = Axes.Both;