mirror of
https://github.com/ppy/osu.git
synced 2026-05-16 15:43:04 +08:00
7f5f368dea
Closes https://github.com/ppy/osu/issues/37553. You can probably tell by the title that this is going to be a good one. As previously mentioned in https://github.com/ppy/osu/pull/35395, framework-side `TextBox` uses a bunch of `NumberFormat` properties from `CurrentCulture` to contextually allow decimal points or minus signs in a textbox. In some languages, namely (of the ones we support): Finnish, Croatian, Lithuanian, Norsk, Slovenian, and Swedish, `NumberFormat.NegativeSign` is not `U+002D HYPHEN MINUS`, but instead `U+2212 MINUS SIGN`. Therefore, in `FormSliderBar`, when `ToStandardFormattedString()` is attempted to be used to set the textbox value, the hardcoded `U+002D HYPHEN MINUS` is rejected on cultures that expect `U+2212 MINUS SIGN`, and thus due to a feedback loop, all negative values are no longer settable. This applies the obvious fix of applying `NumberFormat.NegativeSign`.
55 lines
2.7 KiB
C#
55 lines
2.7 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;
|
|
using System.Globalization;
|
|
using System.Numerics;
|
|
using osu.Game.Utils;
|
|
|
|
namespace osu.Game.Extensions
|
|
{
|
|
public static class NumberFormattingExtensions
|
|
{
|
|
/// <summary>
|
|
/// For a given numeric type, return a formatted string in the standard format we use for display everywhere.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Number formatting will abide by <see cref="CultureInfo.CurrentCulture"/>.
|
|
/// </remarks>
|
|
/// <param name="value">The numeric value.</param>
|
|
/// <param name="maxDecimalDigits">The maximum number of decimals to be considered in the original value.</param>
|
|
/// <param name="asPercentage">Whether the output should be a percentage. For integer types, 0-100 is mapped to 0-100%; for other types 0-1 is mapped to 0-100%.</param>
|
|
/// <returns>The formatted output.</returns>
|
|
public static string ToStandardFormattedString<T>(this T value, int maxDecimalDigits, bool asPercentage = false) where T : struct, INumber<T>, IMinMaxValue<T>
|
|
{
|
|
double floatValue = double.CreateTruncating(value);
|
|
|
|
decimal decimalPrecision = normalise(decimal.CreateTruncating(value), maxDecimalDigits);
|
|
|
|
// Find the number of significant digits (we could have less than maxDecimalDigits after normalize())
|
|
int significantDigits = FormatUtils.FindPrecision(decimalPrecision);
|
|
|
|
if (asPercentage)
|
|
{
|
|
if (value is int)
|
|
floatValue /= 100;
|
|
|
|
return floatValue.ToString($@"0.{new string('0', Math.Max(0, significantDigits - 2))}%", CultureInfo.CurrentCulture);
|
|
}
|
|
|
|
string negativeSign = Math.Round(floatValue, significantDigits) < 0 ? CultureInfo.CurrentCulture.NumberFormat.NegativeSign : string.Empty;
|
|
|
|
return $"{negativeSign}{Math.Abs(floatValue).ToString($"N{significantDigits}", CultureInfo.CurrentCulture)}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all non-significant digits, keeping at most a requested number of decimal digits.
|
|
/// </summary>
|
|
/// <param name="d">The decimal to normalize.</param>
|
|
/// <param name="sd">The maximum number of decimal digits to keep. The final result may have fewer decimal digits than this value.</param>
|
|
/// <returns>The normalised decimal.</returns>
|
|
private static decimal normalise(decimal d, int sd)
|
|
=> decimal.Parse(Math.Round(d, sd).ToString(string.Concat("0.", new string('#', sd)), CultureInfo.InvariantCulture), CultureInfo.InvariantCulture);
|
|
}
|
|
}
|