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

Use new sliders-with-text-input in editor toolboxes

Addresses https://github.com/ppy/osu/discussions/35732.

And yes, I renamed "perfect curve threshold" to "bias" so that the text
can fit. Sue me.
This commit is contained in:
Bartłomiej Dach
2025-11-19 13:22:07 +01:00
Unverified
parent f284864f96
commit 4b59a4657f
7 changed files with 81 additions and 82 deletions
@@ -68,15 +68,18 @@ namespace osu.Game.Rulesets.Osu.Edit
{
toleranceSlider = new ExpandableSlider<int>
{
Current = displayTolerance
Current = displayTolerance,
ExpandedLabelText = "Control point spacing",
},
cornerThresholdSlider = new ExpandableSlider<int>
{
Current = displayCornerThreshold
Current = displayCornerThreshold,
ExpandedLabelText = "Corner bias",
},
circleThresholdSlider = new ExpandableSlider<int>
{
Current = displayCircleThreshold
Current = displayCircleThreshold,
ExpandedLabelText = "Perfect curve bias"
}
};
}
@@ -88,24 +91,18 @@ namespace osu.Game.Rulesets.Osu.Edit
displayTolerance.BindValueChanged(tolerance =>
{
toleranceSlider.ContractedLabelText = $"C. P. S.: {tolerance.NewValue:N0}";
toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {tolerance.NewValue:N0}";
Tolerance.Value = displayToInternalTolerance(tolerance.NewValue);
}, true);
displayCornerThreshold.BindValueChanged(threshold =>
{
cornerThresholdSlider.ContractedLabelText = $"C. T.: {threshold.NewValue:N0}";
cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {threshold.NewValue:N0}";
cornerThresholdSlider.ContractedLabelText = $"C. B.: {threshold.NewValue:N0}";
CornerThreshold.Value = displayToInternalCornerThreshold(threshold.NewValue);
}, true);
displayCircleThreshold.BindValueChanged(threshold =>
{
circleThresholdSlider.ContractedLabelText = $"P. C. T.: {threshold.NewValue:N0}";
circleThresholdSlider.ExpandedLabelText = $"Perfect Curve Threshold: {threshold.NewValue:N0}";
circleThresholdSlider.ContractedLabelText = $"P. C. B.: {threshold.NewValue:N0}";
CircleThreshold.Value = displayToInternalCircleThreshold(threshold.NewValue);
}, true);
@@ -127,21 +127,25 @@ namespace osu.Game.Rulesets.Osu.Edit
{
Current = StartPositionX,
KeyboardStep = 1,
ExpandedLabelText = "X offset",
},
startPositionYSlider = new ExpandableSlider<float>
{
Current = StartPositionY,
KeyboardStep = 1,
ExpandedLabelText = "Y offset",
},
spacingSlider = new ExpandableSlider<float>
{
Current = Spacing,
KeyboardStep = 1,
ExpandedLabelText = "Spacing",
},
gridLinesRotationSlider = new ExpandableSlider<float>
{
Current = GridLinesRotation,
KeyboardStep = 1,
ExpandedLabelText = "Rotation",
},
new FillFlowContainer
{
@@ -182,14 +186,12 @@ namespace osu.Game.Rulesets.Osu.Edit
StartPositionX.BindValueChanged(x =>
{
startPositionXSlider.ContractedLabelText = $"X: {x.NewValue:#,0.##}";
startPositionXSlider.ExpandedLabelText = $"X Offset: {x.NewValue:#,0.##}";
StartPosition.Value = new Vector2(x.NewValue, StartPosition.Value.Y);
}, true);
StartPositionY.BindValueChanged(y =>
{
startPositionYSlider.ContractedLabelText = $"Y: {y.NewValue:#,0.##}";
startPositionYSlider.ExpandedLabelText = $"Y Offset: {y.NewValue:#,0.##}";
StartPosition.Value = new Vector2(StartPosition.Value.X, y.NewValue);
}, true);
@@ -202,7 +204,6 @@ namespace osu.Game.Rulesets.Osu.Edit
Spacing.BindValueChanged(spacing =>
{
spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:#,0.##}";
spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:#,0.##}";
SpacingVector.Value = new Vector2(spacing.NewValue);
editorBeatmap.GridSize = (int)spacing.NewValue;
}, true);
@@ -210,7 +211,6 @@ namespace osu.Game.Rulesets.Osu.Edit
GridLinesRotation.BindValueChanged(rotation =>
{
gridLinesRotationSlider.ContractedLabelText = $"R: {rotation.NewValue:#,0.##}";
gridLinesRotationSlider.ExpandedLabelText = $"Rotation: {rotation.NewValue:#,0.##}";
}, true);
GridType.BindValueChanged(v =>
@@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Overlays.Settings.Sections;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
@@ -19,7 +18,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private TestExpandingContainer container;
private SettingsToolboxGroup toolboxGroup;
private ExpandableSlider<float, SizeSlider<float>> slider1;
private ExpandableSlider<float> slider1;
private ExpandableSlider<double> slider2;
[SetUp]
@@ -36,7 +35,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Width = 1,
Children = new Drawable[]
{
slider1 = new ExpandableSlider<float, SizeSlider<float>>
slider1 = new ExpandableSlider<float>
{
Current = new BindableFloat
{
@@ -62,13 +61,13 @@ namespace osu.Game.Tests.Visual.UserInterface
slider1.Current.BindValueChanged(v =>
{
slider1.ExpandedLabelText = $"Slider One ({v.NewValue:0.##x})";
slider1.ExpandedLabelText = "Slider One";
slider1.ContractedLabelText = $"S. 1. ({v.NewValue:0.##x})";
}, true);
slider2.Current.BindValueChanged(v =>
{
slider2.ExpandedLabelText = $"Slider Two ({v.NewValue:N2})";
slider2.ExpandedLabelText = "Slider Two";
slider2.ContractedLabelText = $"S. 2. ({v.NewValue:N2})";
}, true);
});
@@ -4,7 +4,7 @@
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Input;
using osu.Framework.Threading;
namespace osu.Game.Graphics.Containers
@@ -58,6 +58,8 @@ namespace osu.Game.Graphics.Containers
protected virtual OsuScrollContainer CreateScrollContainer() => new OsuScrollContainer();
private InputManager inputManager = null!;
private bool? lastMouseInBounds;
private ScheduledDelegate? hoverExpandEvent;
protected override void LoadComplete()
@@ -68,37 +70,35 @@ namespace osu.Game.Graphics.Containers
{
this.ResizeWidthTo(v.NewValue ? expandedWidth : contractedWidth, TRANSITION_DURATION, Easing.OutQuint);
}, true);
inputManager = GetContainingInputManager()!;
}
protected override bool OnHover(HoverEvent e)
protected override void Update()
{
updateHoverExpansion();
return true;
base.Update();
bool mouseInBounds = Contains(inputManager.CurrentState.Mouse.Position);
if (lastMouseInBounds != mouseInBounds)
updateExpansionState(mouseInBounds);
lastMouseInBounds = mouseInBounds;
}
protected override void OnHoverLost(HoverLostEvent e)
{
if (hoverExpandEvent != null)
{
hoverExpandEvent?.Cancel();
hoverExpandEvent = null;
Expanded.Value = false;
return;
}
base.OnHoverLost(e);
}
private void updateHoverExpansion()
private void updateExpansionState(bool mouseInBounds)
{
if (!ExpandOnHover)
return;
hoverExpandEvent?.Cancel();
hoverExpandEvent = null;
if (IsHovered && !Expanded.Value)
if (mouseInBounds && !Expanded.Value)
hoverExpandEvent = Scheduler.AddDelayed(() => Expanded.Value = true, HoverExpansionDelay);
if (!mouseInBounds && Expanded.Value)
Expanded.Value = false;
}
}
}
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Framework.Localisation;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterfaceV2;
using Vector2 = osuTK.Vector2;
namespace osu.Game.Graphics.UserInterface
@@ -19,49 +20,27 @@ namespace osu.Game.Graphics.UserInterface
/// </summary>
public partial class ExpandableSlider<T, TSlider> : CompositeDrawable, IExpandable, IHasCurrentValue<T>
where T : struct, INumber<T>, IMinMaxValue<T>
where TSlider : RoundedSliderBar<T>, new()
where TSlider : FormSliderBar<T>, new()
{
private readonly OsuSpriteText label;
private readonly OsuSpriteText contractedLabel;
private readonly TSlider slider;
private LocalisableString contractedLabelText;
/// <summary>
/// The label text to display when this slider is in a contracted state.
/// </summary>
public LocalisableString ContractedLabelText
{
get => contractedLabelText;
set
{
if (value == contractedLabelText)
return;
contractedLabelText = value;
if (!Expanded.Value)
label.Text = value;
}
get => contractedLabel.Text;
set => contractedLabel.Text = value;
}
private LocalisableString expandedLabelText;
/// <summary>
/// The label text to display when this slider is in an expanded state.
/// </summary>
public LocalisableString ExpandedLabelText
{
get => expandedLabelText;
set
{
if (value == expandedLabelText)
return;
expandedLabelText = value;
if (Expanded.Value)
label.Text = value;
}
get => slider.Caption;
set => slider.Caption = value;
}
public Bindable<T> Current
@@ -95,7 +74,7 @@ namespace osu.Game.Graphics.UserInterface
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
label = new OsuSpriteText(),
contractedLabel = new OsuSpriteText(),
slider = new TSlider
{
RelativeSizeAxes = Axes.X,
@@ -118,7 +97,8 @@ namespace osu.Game.Graphics.UserInterface
Expanded.BindValueChanged(v =>
{
label.Text = v.NewValue ? expandedLabelText : contractedLabelText;
contractedLabel.FadeTo(v.NewValue ? 0 : 1);
slider.FadeTo(v.NewValue ? Current.Disabled ? 0.3f : 1f : 0f, 500, Easing.OutQuint);
slider.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None;
}, true);
@@ -133,7 +113,7 @@ namespace osu.Game.Graphics.UserInterface
/// <summary>
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
/// </summary>
public partial class ExpandableSlider<T> : ExpandableSlider<T, RoundedSliderBar<T>>
public partial class ExpandableSlider<T> : ExpandableSlider<T, FormSliderBar<T>>
where T : struct, INumber<T>, IMinMaxValue<T>
{
}
@@ -58,26 +58,49 @@ namespace osu.Game.Graphics.UserInterfaceV2
}
}
private LocalisableString caption;
/// <summary>
/// Caption describing this slider bar, displayed on top of the controls.
/// </summary>
public LocalisableString Caption { get; init; }
public LocalisableString Caption
{
get => caption;
set
{
caption = value;
if (IsLoaded)
captionText.Caption = value;
}
}
/// <summary>
/// Hint text containing an extended description of this slider bar, displayed in a tooltip when hovering the caption.
/// </summary>
public LocalisableString HintText { get; init; }
private float keyboardStep;
/// <summary>
/// A custom step value for each key press which actuates a change on this control.
/// </summary>
public float KeyboardStep { get; init; }
public float KeyboardStep
{
get => keyboardStep;
set
{
keyboardStep = value;
if (IsLoaded)
slider.KeyboardStep = value;
}
}
private Box background = null!;
private Box flashLayer = null!;
private FormTextBox.InnerTextBox textBox = null!;
private InnerSlider slider = null!;
private FormFieldCaption caption = null!;
private FormFieldCaption captionText = null!;
private IFocusManager focusManager = null!;
[Resolved]
@@ -117,11 +140,10 @@ namespace osu.Game.Graphics.UserInterfaceV2
},
Children = new Drawable[]
{
caption = new FormFieldCaption
captionText = new FormFieldCaption
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Caption = Caption,
TooltipText = HintText,
},
textBox = new FormNumberBox.InnerNumberBox(allowDecimals: true)
@@ -145,7 +167,6 @@ namespace osu.Game.Graphics.UserInterfaceV2
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.X,
Width = 0.5f,
KeyboardStep = KeyboardStep,
Current = currentNumberInstantaneous,
OnCommit = () => current.Value = currentNumberInstantaneous.Value,
}
@@ -161,6 +182,9 @@ namespace osu.Game.Graphics.UserInterfaceV2
{
base.LoadComplete();
slider.KeyboardStep = keyboardStep;
captionText.Caption = caption;
focusManager = GetContainingFocusManager()!;
textBox.Focused.BindValueChanged(_ => updateState());
@@ -270,7 +294,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
textBox.Alpha = 1;
background.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background4 : colourProvider.Background5;
caption.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Foreground1 : colourProvider.Content2;
captionText.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Foreground1 : colourProvider.Content2;
textBox.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Foreground1 : colourProvider.Content1;
BorderThickness = childHasFocus || IsHovered || slider.IsDragging.Value ? 2 : 0;
@@ -20,7 +20,6 @@ using osu.Game.Input;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
using osu.Game.Overlays.OSD;
using osu.Game.Overlays.Settings.Sections;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
@@ -42,7 +41,7 @@ namespace osu.Game.Rulesets.Edit
Bindable<double> IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier;
private ExpandableSlider<double, SizeSlider<double>> distanceSpacingSlider = null!;
private ExpandableSlider<double> distanceSpacingSlider = null!;
private ExpandableButton currentDistanceSpacingButton = null!;
[Resolved]
@@ -78,11 +77,12 @@ namespace osu.Game.Rulesets.Edit
Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1,
Children = new Drawable[]
{
distanceSpacingSlider = new ExpandableSlider<double, SizeSlider<double>>
distanceSpacingSlider = new ExpandableSlider<double>
{
KeyboardStep = adjust_step,
// Manual binding in LoadComplete to handle one-way event flow.
Current = DistanceSpacingMultiplier.GetUnboundCopy(),
ExpandedLabelText = "Distance spacing",
},
currentDistanceSpacingButton = new ExpandableButton
{
@@ -104,7 +104,6 @@ namespace osu.Game.Rulesets.Edit
DistanceSpacingMultiplier.BindValueChanged(multiplier =>
{
distanceSpacingSlider.ContractedLabelText = $"D. S. ({multiplier.NewValue:0.##x})";
distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({multiplier.NewValue:0.##x})";
if (multiplier.NewValue != multiplier.OldValue)
onScreenDisplay?.Display(new DistanceSpacingToast(multiplier.NewValue.ToLocalisableString(@"0.##x"), multiplier));