1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-18 20:30:27 +08:00
Files
osu-lazer/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs
T
De4n e05b6f44b9 Update editor slider controls to new design (#36346)
(partially) Closes: #36233
Surpasses: #36244 

This PR meant to be one of the last steps that finally make editor use
the new forms. Initially it meant to only change one
SliderWithTextBoxInput in "Effects section" in timing screen, however
soon after it was obvious that there's many other places that still
using it. This currently won't affect
IndeterminateSliderWithTextBoxInput that is being used in hitsounds, for
example, since I think it needs more consideration.
Anyways, with this PR, SliderWithTextBoxInput, will no longer be used at
all, as it's going to be replaced with modern FormSliderBar

Comparison:
|master|this PR|
|:---:|:---:|
|<img width="510" height="316"
alt="532203751-eb965923-d3a8-441d-a7c8-5c364a6328ad"
src="https://github.com/user-attachments/assets/268b45b8-e235-494f-91a5-d00db057dba8"
/>|<img width="540" height="321"
alt="535466527-3a700a8b-bc3c-4610-998f-a4e55ee03eed"
src="https://github.com/user-attachments/assets/20cd4b58-b0bd-49bc-8c48-7de5cf8556b3"
/>|
|<img width="694" height="639"
alt="534509844-f00e4da4-53c4-45e8-80ea-1be62da6c83b"
src="https://github.com/user-attachments/assets/398c4484-a867-4df1-9de3-0940aa748a01"
/>|<img width="720" height="433" alt="изображение"
src="https://github.com/user-attachments/assets/b6359443-a224-4a55-b171-07e8f013cf46"
/>|
|<img width="715" height="353"
alt="534509421-a6ac950f-16e8-4a16-bca6-1a781f82135f"
src="https://github.com/user-attachments/assets/4854312b-772f-4b81-a800-89e58d4c715d"
/>|<img width="710" height="296" alt="изображение"
src="https://github.com/user-attachments/assets/a7fed53e-e006-4285-92c9-bb84cb603f60"
/>|
|<img width="717" height="374"
alt="534509478-80222623-7766-481d-8682-088276d415ee"
src="https://github.com/user-attachments/assets/8143b6dc-4599-45d5-bd3b-f059caf3d93d"
/>|<img width="718" height="328" alt="изображение"
src="https://github.com/user-attachments/assets/bffa04de-983c-45ae-a1ec-373701ea0e49"
/>|
|<img width="702" height="446"
alt="534509935-58954060-7ac1-4392-8754-a58f909e86aa"
src="https://github.com/user-attachments/assets/2bb67a2d-3f57-42a1-96ce-b30b4891e1a4"
/>|<img width="722" height="386" alt="изображение"
src="https://github.com/user-attachments/assets/01b7fff4-7f31-4aac-90c9-353b15f4964e"
/>|
2026-01-16 20:08:52 +09:00

152 lines
5.5 KiB
C#

// 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 System.Numerics;
using System.Globalization;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Overlays.Settings;
using osu.Game.Utils;
using Vector2 = osuTK.Vector2;
namespace osu.Game.Screens.Edit.Timing
{
/// <summary>
/// Analogous to SliderWithTextBoxInput, but supports scenarios
/// where multiple objects with multiple different property values are selected
/// by providing an "indeterminate state".
/// </summary>
public partial class IndeterminateSliderWithTextBoxInput<T> : CompositeDrawable, IHasCurrentValue<T?>
where T : struct, INumber<T>, IMinMaxValue<T>
{
/// <summary>
/// A custom step value for each key press which actuates a change on this control.
/// </summary>
public float KeyboardStep
{
get => slider.KeyboardStep;
set => slider.KeyboardStep = value;
}
public CompositeDrawable TabbableContentContainer
{
set => textBox.TabbableContentContainer = value;
}
private readonly BindableWithCurrent<T?> current = new BindableWithCurrent<T?>();
public Bindable<T?> Current
{
get => current.Current;
set => current.Current = value;
}
private readonly SettingsSlider<T> slider;
private readonly LabelledTextBox textBox;
/// <summary>
/// Creates an <see cref="IndeterminateSliderWithTextBoxInput{T}"/>.
/// </summary>
/// <param name="labelText">The label text for the slider and text box.</param>
/// <param name="indeterminateValue">
/// Bindable to use for the slider until a non-null value is set for <see cref="Current"/>.
/// In particular, it can be used to control min/max bounds and precision in the case of <see cref="BindableNumber{T}"/>s.
/// </param>
public IndeterminateSliderWithTextBoxInput(LocalisableString labelText, Bindable<T> indeterminateValue)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
textBox = new LabelledTextBox
{
Label = labelText,
SelectAllOnFocus = true,
},
slider = new SettingsSlider<T>
{
TransferValueOnCommit = true,
RelativeSizeAxes = Axes.X,
Current = indeterminateValue
}
}
},
};
textBox.OnCommit += (t, isNew) =>
{
if (!isNew) return;
try
{
switch (slider.Current)
{
case Bindable<int> bindableInt:
bindableInt.Value = int.Parse(t.Text);
break;
case Bindable<double> bindableDouble:
bindableDouble.Value = double.Parse(t.Text);
break;
default:
slider.Current.Parse(t.Text, CultureInfo.CurrentCulture);
break;
}
}
catch
{
// TriggerChange below will restore the previous text value on failure.
}
// This is run regardless of parsing success as the parsed number may not actually trigger a change
// due to bindable clamping. Even in such a case we want to update the textbox to a sane visual state.
Current.TriggerChange();
};
slider.Current.BindValueChanged(val => Current.Value = val.NewValue);
Current.BindValueChanged(_ => updateState(), true);
}
public override bool AcceptsFocus => true;
protected override void OnFocus(FocusEvent e)
{
base.OnFocus(e);
GetContainingFocusManager()!.ChangeFocus(textBox);
}
private void updateState()
{
if (Current.Value is T nonNullValue)
{
slider.Current.Value = nonNullValue;
// use the value from the slider to ensure that any precision/min/max set on it via the initial indeterminate value have been applied correctly.
decimal decimalValue = decimal.CreateTruncating(slider.Current.Value);
textBox.Text = decimalValue.ToString($@"N{FormatUtils.FindPrecision(decimalValue)}");
textBox.PlaceholderText = string.Empty;
}
else
{
textBox.Text = null;
textBox.PlaceholderText = "(multiple)";
}
}
}
}