1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-23 09:40:16 +08:00

Rewrite input settings and use new form controls

This commit is contained in:
Salman Alshamrani
2025-12-31 10:45:11 -05:00
Unverified
parent 874e3adcb7
commit 5d9ea9d699
11 changed files with 355 additions and 195 deletions
@@ -8,6 +8,7 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input.Handlers;
using osu.Framework.Input.Handlers.Tablet;
using osu.Framework.Testing;
using osu.Framework.Utils;
@@ -69,7 +70,7 @@ namespace osu.Game.Tests.Visual.Settings
{
AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Vector2(160, 100)));
AddStep("Reset to full area", () => settings.ChildrenOfType<DangerousSettingsButton>().First().TriggerClick());
AddStep("Reset to full area", () => settings.ChildrenOfType<DangerousSettingsButtonV2>().First().TriggerClick());
ensureValid();
AddStep("rotate 10", () => tabletHandler.Rotation.Value = 10);
@@ -129,7 +130,7 @@ namespace osu.Game.Tests.Visual.Settings
private void ensureInvalid() => AddAssert("area invalid", () => !settings.AreaSelection.IsWithinBounds);
public class TestTabletHandler : ITabletHandler
public class TestTabletHandler : InputHandler, ITabletHandler
{
public Bindable<Vector2> AreaOffset { get; } = new Bindable<Vector2>();
public Bindable<Vector2> AreaSize { get; } = new Bindable<Vector2>();
@@ -149,7 +150,7 @@ namespace osu.Game.Tests.Visual.Settings
private readonly Bindable<TabletInfo> tablet = new Bindable<TabletInfo>();
public BindableBool Enabled { get; } = new BindableBool(true);
public override bool IsActive => true;
public void SetTabletSize(Vector2 size)
{
@@ -112,7 +112,11 @@ namespace osu.Game.Graphics.UserInterfaceV2
protected override void OnUserChange(bool value)
{
base.OnUserChange(value);
PlaySample(value);
}
public void PlaySample(bool value)
{
if (value)
sampleChecked?.Play();
else
+1 -1
View File
@@ -625,7 +625,7 @@ namespace osu.Game
return new TouchSettings(th);
case MidiHandler:
return new InputSection.HandlerSection(handler);
return new InputSubsection(handler);
// return null for handlers that shouldn't have settings.
default:
@@ -8,25 +8,23 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input.Handlers.Joystick;
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Localisation;
namespace osu.Game.Overlays.Settings.Sections.Input
{
public partial class JoystickSettings : SettingsSubsection
public partial class JoystickSettings : InputSubsection
{
protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad;
private readonly JoystickHandler joystickHandler;
private readonly Bindable<bool> enabled = new BindableBool(true);
private SettingsSlider<float> deadzoneSlider;
private Bindable<float> handlerDeadzone;
private Bindable<float> localDeadzone;
public JoystickSettings(JoystickHandler joystickHandler)
: base(joystickHandler)
{
this.joystickHandler = joystickHandler;
}
@@ -38,30 +36,22 @@ namespace osu.Game.Overlays.Settings.Sections.Input
handlerDeadzone = joystickHandler.DeadzoneThreshold.GetBoundCopy();
localDeadzone = handlerDeadzone.GetUnboundCopy();
Children = new Drawable[]
AddRange(new Drawable[]
{
new SettingsCheckbox
new SettingsItemV2(new FormSliderBar<float>
{
LabelText = CommonStrings.Enabled,
Current = enabled
},
deadzoneSlider = new SettingsSlider<float>
{
LabelText = JoystickSettingsStrings.DeadzoneThreshold,
Caption = JoystickSettingsStrings.DeadzoneThreshold,
KeyboardStep = 0.01f,
DisplayAsPercentage = true,
Current = localDeadzone,
},
};
})
});
}
protected override void LoadComplete()
{
base.LoadComplete();
enabled.BindTo(joystickHandler.Enabled);
enabled.BindValueChanged(e => deadzoneSlider.Current.Disabled = !e.NewValue, true);
handlerDeadzone.BindValueChanged(val =>
{
bool disabled = localDeadzone.Disabled;
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -11,30 +9,33 @@ using osu.Framework.Graphics;
using osu.Framework.Input.Handlers.Mouse;
using osu.Framework.Localisation;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Input;
using osu.Game.Localisation;
namespace osu.Game.Overlays.Settings.Sections.Input
{
public partial class MouseSettings : SettingsSubsection
public partial class MouseSettings : InputSubsection
{
private readonly MouseHandler mouseHandler;
protected override LocalisableString Header => MouseSettingsStrings.Mouse;
private Bindable<double> handlerSensitivity;
private Bindable<double> handlerSensitivity = null!;
private Bindable<double> localSensitivity = null!;
private Bindable<WindowMode> windowMode = null!;
private Bindable<bool> minimiseOnFocusLoss = null!;
private FormEnumDropdown<OsuConfineMouseMode> confineMouseModeSetting = null!;
private Bindable<bool> relativeMode = null!;
private Bindable<double> localSensitivity;
private FormCheckBox highPrecisionMouse = null!;
private Bindable<WindowMode> windowMode;
private Bindable<bool> minimiseOnFocusLoss;
private SettingsEnumDropdown<OsuConfineMouseMode> confineMouseModeSetting;
private Bindable<bool> relativeMode;
private readonly Bindable<SettingsNote.Data?> highPrecisionMouseNote = new Bindable<SettingsNote.Data?>();
private SettingsCheckbox highPrecisionMouse;
protected override bool IsToggleable => false;
public MouseSettings(MouseHandler mouseHandler)
: base(mouseHandler)
{
this.mouseHandler = mouseHandler;
}
@@ -50,38 +51,47 @@ namespace osu.Game.Overlays.Settings.Sections.Input
windowMode = config.GetBindable<WindowMode>(FrameworkSetting.WindowMode);
minimiseOnFocusLoss = config.GetBindable<bool>(FrameworkSetting.MinimiseOnFocusLossInFullscreen);
Children = new Drawable[]
AddRange(new Drawable[]
{
highPrecisionMouse = new SettingsCheckbox
new SettingsItemV2(highPrecisionMouse = new FormCheckBox
{
LabelText = MouseSettingsStrings.HighPrecisionMouse,
TooltipText = MouseSettingsStrings.HighPrecisionMouseTooltip,
Caption = MouseSettingsStrings.HighPrecisionMouse,
HintText = MouseSettingsStrings.HighPrecisionMouseTooltip,
Current = relativeMode,
})
{
Keywords = new[] { @"raw", @"input", @"relative", @"cursor", "sensitivity", "speed", "velocity" },
Note = { BindTarget = highPrecisionMouseNote },
},
new SensitivitySetting
new SettingsItemV2(new FormSliderBar<double>
{
Caption = MouseSettingsStrings.CursorSensitivity,
Current = localSensitivity,
KeyboardStep = 0.01f,
TransferValueOnCommit = true,
LabelFormat = v => $@"{v:0.##}x",
TooltipFormat = v => localSensitivity.Disabled ? MouseSettingsStrings.EnableHighPrecisionForSensitivityAdjust : $@"{v:0.##}x",
})
{
Keywords = new[] { "speed", "velocity" },
LabelText = MouseSettingsStrings.CursorSensitivity,
Current = localSensitivity
},
confineMouseModeSetting = new SettingsEnumDropdown<OsuConfineMouseMode>
new SettingsItemV2(confineMouseModeSetting = new FormEnumDropdown<OsuConfineMouseMode>
{
LabelText = MouseSettingsStrings.ConfineMouseMode,
Caption = MouseSettingsStrings.ConfineMouseMode,
Current = osuConfig.GetBindable<OsuConfineMouseMode>(OsuSetting.ConfineMouseMode)
},
new SettingsCheckbox
}),
new SettingsItemV2(new FormCheckBox
{
LabelText = MouseSettingsStrings.DisableMouseWheelVolumeAdjust,
TooltipText = MouseSettingsStrings.DisableMouseWheelVolumeAdjustTooltip,
Caption = MouseSettingsStrings.DisableMouseWheelVolumeAdjust,
HintText = MouseSettingsStrings.DisableMouseWheelVolumeAdjustTooltip,
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableWheel)
},
new SettingsCheckbox
}),
new SettingsItemV2(new FormCheckBox
{
LabelText = MouseSettingsStrings.DisableClicksDuringGameplay,
Caption = MouseSettingsStrings.DisableClicksDuringGameplay,
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons)
},
};
}),
});
}
protected override void LoadComplete()
@@ -112,9 +122,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input
case RuntimeInfo.Platform.macOS:
case RuntimeInfo.Platform.iOS:
if (highPrecision.NewValue)
highPrecisionMouse.SetNoticeText(MouseSettingsStrings.HighPrecisionPlatformWarning, true);
highPrecisionMouseNote.Value = new SettingsNote.Data(MouseSettingsStrings.HighPrecisionPlatformWarning, SettingsNote.Type.Warning);
else
highPrecisionMouse.ClearNoticeText();
highPrecisionMouseNote.Value = null;
break;
}
@@ -131,27 +141,13 @@ namespace osu.Game.Overlays.Settings.Sections.Input
if (confineModeOverriden)
{
confineMouseModeSetting.Current.Disabled = true;
confineMouseModeSetting.TooltipText = MouseSettingsStrings.NotApplicableFullscreen;
confineMouseModeSetting.HintText = MouseSettingsStrings.NotApplicableFullscreen;
}
else
{
confineMouseModeSetting.Current.Disabled = false;
confineMouseModeSetting.TooltipText = string.Empty;
confineMouseModeSetting.HintText = default;
}
}
public partial class SensitivitySetting : SettingsSlider<double, SensitivitySlider>
{
public SensitivitySetting()
{
KeyboardStep = 0.01f;
TransferValueOnCommit = true;
}
}
public partial class SensitivitySlider : RoundedSliderBar<double>
{
public override LocalisableString TooltipText => Current.Disabled ? MouseSettingsStrings.EnableHighPrecisionForSensitivityAdjust : $"{base.TooltipText}x";
}
}
}
@@ -45,7 +45,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
this.handler = handler;
Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS };
Padding = SettingsPanel.ContentPaddingV2;
}
[BackgroundDependencyLoader]
@@ -9,6 +9,8 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Handlers;
using osu.Framework.Input.Handlers.Tablet;
using osu.Framework.Localisation;
using osu.Framework.Platform;
@@ -17,13 +19,14 @@ using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterfaceV2;
using osuTK;
using osu.Game.Localisation;
using osu.Game.Online.Chat;
namespace osu.Game.Overlays.Settings.Sections.Input
{
public partial class TabletSettings : SettingsSubsection
public partial class TabletSettings : InputSubsection
{
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "area" });
@@ -74,70 +77,92 @@ namespace osu.Game.Overlays.Settings.Sections.Input
private FillFlowContainer mainSettings;
private FillFlowContainer noTabletMessage;
private Container noTabletMessage;
protected override LocalisableString Header => TabletSettingsStrings.Tablet;
public TabletSettings(ITabletHandler tabletHandler)
: base((InputHandler)tabletHandler)
{
this.tabletHandler = tabletHandler;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationManager localisation, OsuConfigManager osuConfig)
private void load(OsuColour colours, LocalisationManager localisation, OsuConfigManager osuConfig, OverlayColourProvider colourProvider)
{
scalingMode = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling);
scalingSizeX = osuConfig.GetBindable<float>(OsuSetting.ScalingSizeX);
scalingSizeY = osuConfig.GetBindable<float>(OsuSetting.ScalingSizeY);
Children = new Drawable[]
AddRange(new Drawable[]
{
new SettingsCheckbox
{
LabelText = CommonStrings.Enabled,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Current = enabled,
},
noTabletMessage = new FillFlowContainer
noTabletMessage = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS },
Spacing = new Vector2(5f),
Children = new Drawable[]
Padding = SettingsPanel.ContentPaddingV2,
Child = new Container
{
new OsuSpriteText
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Masking = true,
CornerRadius = 5,
CornerExponent = 2.5f,
Children = new Drawable[]
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = TabletSettingsStrings.NoTabletDetected,
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Dark2,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(5f),
Padding = new MarginPadding { Horizontal = 8, Vertical = 10 },
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = TabletSettingsStrings.NoTabletDetected,
Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold),
Colour = colourProvider.Content2,
},
new LinkFlowContainer(cp =>
{
cp.Colour = colours.Orange1;
cp.Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold);
})
{
TextAnchor = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
}.With(t =>
{
t.NewLine();
const string url = @"https://opentabletdriver.net/Wiki/FAQ/General";
var formattedSource = MessageFormatter.FormatText(localisation.GetLocalisedString(TabletSettingsStrings.NoTabletDetectedDescription(url)));
t.AddLinks(formattedSource.Text, formattedSource.Links);
}),
}
},
},
new LinkFlowContainer(cp => cp.Colour = colours.Yellow)
{
TextAnchor = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
}.With(t =>
{
t.NewLine();
const string url = @"https://opentabletdriver.net/Wiki/FAQ/General";
var formattedSource = MessageFormatter.FormatText(localisation.GetLocalisedString(TabletSettingsStrings.NoTabletDetectedDescription(url)));
t.AddLinks(formattedSource.Text, formattedSource.Links);
}),
}
},
},
mainSettings = new FillFlowContainer
{
Alpha = 0,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0, SettingsSection.ITEM_SPACING),
Spacing = new Vector2(0, SettingsSection.ITEM_SPACING_V2),
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
@@ -146,7 +171,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
RelativeSizeAxes = Axes.X,
Height = 300,
},
new DangerousSettingsButton
new DangerousSettingsButtonV2
{
Text = TabletSettingsStrings.ResetToFullArea,
Action = () =>
@@ -156,9 +181,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input
areaOffset.SetDefault();
areaSize.SetDefault();
},
CanBeShown = { BindTarget = enabled }
},
new SettingsButton
new SettingsButtonV2
{
Text = TabletSettingsStrings.ConformToCurrentGameAspectRatio,
Action = () =>
@@ -174,73 +198,62 @@ namespace osu.Game.Overlays.Settings.Sections.Input
forceAspectRatio(gameplayWidth / gameplayHeight);
},
CanBeShown = { BindTarget = enabled }
},
new SettingsSlider<float>
new SettingsItemV2(new FormSliderBar<float>
{
TransferValueOnCommit = true,
LabelText = TabletSettingsStrings.XOffset,
Caption = TabletSettingsStrings.XOffset,
Current = offsetX,
CanBeShown = { BindTarget = enabled }
},
new SettingsSlider<float>
}),
new SettingsItemV2(new FormSliderBar<float>
{
TransferValueOnCommit = true,
LabelText = TabletSettingsStrings.YOffset,
Caption = TabletSettingsStrings.YOffset,
Current = offsetY,
CanBeShown = { BindTarget = enabled }
},
new SettingsSlider<float>
}),
new SettingsItemV2(new FormSliderBar<float>
{
TransferValueOnCommit = true,
LabelText = TabletSettingsStrings.Rotation,
Caption = TabletSettingsStrings.Rotation,
Current = rotation,
CanBeShown = { BindTarget = enabled }
},
}),
new RotationPresetButtons(tabletHandler)
{
Padding = new MarginPadding
{
Horizontal = SettingsPanel.CONTENT_MARGINS
}
Padding = SettingsPanel.ContentPaddingV2,
},
new SettingsSlider<float>
new SettingsItemV2(new FormSliderBar<float>
{
TransferValueOnCommit = true,
LabelText = TabletSettingsStrings.AspectRatio,
Caption = TabletSettingsStrings.AspectRatio,
Current = aspectRatio,
CanBeShown = { BindTarget = enabled }
},
new SettingsCheckbox
}),
new SettingsItemV2(new FormCheckBox
{
LabelText = TabletSettingsStrings.LockAspectRatio,
Caption = TabletSettingsStrings.LockAspectRatio,
Current = aspectLock,
CanBeShown = { BindTarget = enabled }
},
new SettingsSlider<float>
}),
new SettingsItemV2(new FormSliderBar<float>
{
TransferValueOnCommit = true,
LabelText = CommonStrings.Width,
Caption = CommonStrings.Width,
Current = sizeX,
CanBeShown = { BindTarget = enabled }
},
new SettingsSlider<float>
}),
new SettingsItemV2(new FormSliderBar<float>
{
TransferValueOnCommit = true,
LabelText = CommonStrings.Height,
Caption = CommonStrings.Height,
Current = sizeY,
CanBeShown = { BindTarget = enabled }
},
new SettingsPercentageSlider<float>
}),
new SettingsItemV2(new FormSliderBar<float>
{
TransferValueOnCommit = true,
LabelText = TabletSettingsStrings.TipPressureForClick,
Caption = TabletSettingsStrings.TipPressureForClick,
Current = pressureThreshold,
CanBeShown = { BindTarget = enabled }
},
DisplayAsPercentage = true,
}),
}
},
};
});
}
protected override void LoadComplete()
@@ -8,6 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Input.Handlers;
using osu.Framework.Localisation;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Localisation;
namespace osu.Game.Overlays.Settings.Sections.Input
@@ -15,34 +16,25 @@ namespace osu.Game.Overlays.Settings.Sections.Input
/// <summary>
/// Touch input settings subsection common to all touch handlers (even on different platforms).
/// </summary>
public partial class TouchSettings : SettingsSubsection
public partial class TouchSettings : InputSubsection
{
private readonly InputHandler handler;
protected override LocalisableString Header => TouchSettingsStrings.Touch;
protected override bool IsToggleable => !RuntimeInfo.IsMobile;
public TouchSettings(InputHandler handler)
: base(handler)
{
this.handler = handler;
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager osuConfig)
{
if (!RuntimeInfo.IsMobile) // don't allow disabling the only input method (touch) on mobile.
Add(new SettingsItemV2(new FormCheckBox
{
Add(new SettingsCheckbox
{
LabelText = CommonStrings.Enabled,
Current = handler.Enabled
});
}
Add(new SettingsCheckbox
{
LabelText = TouchSettingsStrings.DisableTapsDuringGameplay,
Caption = TouchSettingsStrings.DisableTapsDuringGameplay,
Current = osuConfig.GetBindable<bool>(OsuSetting.TouchDisableGameplayTaps)
});
}));
}
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { @"touchscreen" });
@@ -4,7 +4,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Handlers;
using osu.Framework.Localisation;
using osu.Framework.Platform;
using osu.Game.Graphics;
@@ -45,30 +44,5 @@ namespace osu.Game.Overlays.Settings.Sections
Add(handlerSection);
}
}
public partial class HandlerSection : SettingsSubsection
{
private readonly InputHandler handler;
public HandlerSection(InputHandler handler)
{
this.handler = handler;
}
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[]
{
new SettingsCheckbox
{
LabelText = CommonStrings.Enabled,
Current = handler.Enabled
},
};
}
protected override LocalisableString Header => handler.Description;
}
}
}
@@ -0,0 +1,179 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Input.Handlers;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osuTK;
namespace osu.Game.Overlays.Settings.Sections
{
public partial class InputSubsection : SettingsSubsection
{
private readonly InputHandler handler;
protected override LocalisableString Header => handler.Description;
/// <summary>
/// Whether the input handler can be toggled on/off by the user.
/// </summary>
protected virtual bool IsToggleable => true;
private readonly BindableBool handlerEnabled = new BindableBool();
public InputSubsection(InputHandler handler)
{
this.handler = handler;
FlowContent.AlwaysPresent = true;
}
[BackgroundDependencyLoader]
private void load()
{
HeaderContainer.Child = new ToggleableHeader(Header, IsToggleable)
{
Current = { BindTarget = handlerEnabled },
};
}
protected override void LoadComplete()
{
base.LoadComplete();
handlerEnabled.BindTo(handler.Enabled);
handlerEnabled.BindValueChanged(v => updateEnabledState(), true);
}
private void updateEnabledState()
{
// set negative bottom margin to not have too much vertical gap between disabled input subsections.
bool negativeBottomMargin = !handlerEnabled.Value || FlowContent.Count == 0;
HeaderContainer.TransformTo(nameof(Margin), new MarginPadding { Bottom = negativeBottomMargin ? -15 : 0 }, 300, Easing.OutQuint);
FlowContent.ClearTransforms();
if (!handlerEnabled.Value)
{
FlowContent.AutoSizeAxes = Axes.None;
FlowContent.ResizeHeightTo(0, 300, Easing.OutQuint);
FlowContent.FadeOut(200, Easing.OutQuint);
}
else
{
// enable auto size transform momentarily for smooth pop in animation, and disable it right after the transform is added.
// we don't want this specification to apply when a dropdown in the input settings is being open, it causes too slow animation.
// (try removing the schedule below then watch a settings dropdown menu opening animation).
FlowContent.AutoSizeDuration = 300;
FlowContent.AutoSizeEasing = Easing.OutQuint;
FlowContent.AutoSizeAxes = Axes.Y;
ScheduleAfterChildren(() => FlowContent.AutoSizeDuration = 0);
FlowContent.FadeIn(300, Easing.OutQuint);
}
}
private partial class ToggleableHeader : CompositeDrawable
{
private readonly LocalisableString header;
private readonly bool toggleable;
public readonly BindableBool Current = new BindableBool(true);
public ToggleableHeader(LocalisableString header, bool toggleable)
{
this.header = header;
this.toggleable = toggleable;
}
private SwitchButton switchButton = null!;
private OsuSpriteText headerText = null!;
[Resolved]
private OverlayColourProvider colourProvider { get; set; } = null!;
[BackgroundDependencyLoader]
private void load()
{
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
switchButton = new SwitchButton
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Scale = new Vector2(0.6f),
Position = new Vector2(12, 8),
Rotation = 90,
},
headerText = new OsuSpriteText
{
Text = header,
Font = OsuFont.GetFont(size: 20),
Margin = new MarginPadding { Vertical = 12 },
X = 20,
},
new HoverSounds(),
};
}
protected override void LoadComplete()
{
base.LoadComplete();
switchButton.Current.ValueChanged += v => Current.Value = v.NewValue;
Current.BindValueChanged(v =>
{
switchButton.Current.Disabled = false;
switchButton.Current.Value = v.NewValue;
switchButton.Current.Disabled = !toggleable;
updateDisplay();
}, true);
}
protected override bool OnHover(HoverEvent e)
{
updateDisplay();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateDisplay();
base.OnHoverLost(e);
}
protected override bool OnClick(ClickEvent e)
{
if (toggleable)
{
Current.Toggle();
switchButton.PlaySample(Current.Value);
}
updateDisplay();
return true;
}
private void updateDisplay()
{
if (toggleable && IsHovered)
headerText.FadeColour(colourProvider.Light1, 300, Easing.OutQuint);
else
headerText.FadeColour(Current.Value ? colourProvider.Content1 : colourProvider.Foreground1, 300, Easing.OutQuint);
}
}
}
}
@@ -18,6 +18,8 @@ namespace osu.Game.Overlays.Settings
protected readonly FillFlowContainer FlowContent;
protected Container HeaderContainer { get; private set; } = null!;
protected abstract LocalisableString Header { get; }
public virtual IEnumerable<LocalisableString> FilterTerms => new[] { Header };
@@ -53,11 +55,20 @@ namespace osu.Game.Overlays.Settings
{
AddRangeInternal(new Drawable[]
{
new OsuSpriteText
HeaderContainer = new Container
{
Text = Header,
Margin = new MarginPadding { Vertical = (header_height - header_font_size) * 0.5f, Horizontal = SettingsPanel.CONTENT_MARGINS },
Font = OsuFont.GetFont(size: header_font_size),
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = SettingsPanel.ContentPaddingV2,
Children = new[]
{
new OsuSpriteText
{
Text = Header,
Font = OsuFont.GetFont(size: header_font_size),
Margin = new MarginPadding { Vertical = (header_height - header_font_size) * 0.5f },
},
},
},
FlowContent
});