Fix atmos UI decimal separator bug (#23009)

* Fix atmos UI inconsistent culture usage

* Remove the need to pass localization manager
This commit is contained in:
Kot
2024-01-05 09:18:09 +04:00
committed by GitHub
parent 920d7068e5
commit 3d911a64c7
14 changed files with 117 additions and 19 deletions

View File

@@ -1,8 +1,8 @@
using Content.Client.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Trinary.Components;
using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.Atmos.UI
{
@@ -50,7 +50,7 @@ namespace Content.Client.Atmos.UI
private void OnFilterTransferRatePressed(string value)
{
float rate = float.TryParse(value, out var parsed) ? parsed : 0f;
var rate = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
SendMessage(new GasFilterChangeRateMessage(rate));
}

View File

@@ -9,7 +9,7 @@
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-gas-filter-ui-filter-transfer-rate}"/>
<LineEdit Name="FilterTransferRateInput" MinSize="60 0" />
<LineEdit Name="FilterTransferRateInput" MinSize="70 0" />
<Button Name="SetFilterRate" Text="{Loc comp-gas-filter-ui-filter-set-rate}" Disabled="True"/>
</BoxContainer>

View File

@@ -49,7 +49,7 @@ namespace Content.Client.Atmos.UI
public void SetTransferRate(float rate)
{
FilterTransferRateInput.Text = rate.ToString(CultureInfo.InvariantCulture);
FilterTransferRateInput.Text = rate.ToString(CultureInfo.CurrentCulture);
}
public void SetFilterStatus(bool enabled)

View File

@@ -1,5 +1,6 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Trinary.Components;
using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
@@ -47,7 +48,7 @@ namespace Content.Client.Atmos.UI
private void OnMixerOutputPressurePressed(string value)
{
var pressure = float.TryParse(value, out var parsed) ? parsed : 0f;
var pressure = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
if (pressure > MaxPressure)
pressure = MaxPressure;
@@ -57,7 +58,7 @@ namespace Content.Client.Atmos.UI
private void OnMixerSetPercentagePressed(string value)
{
// We don't need to send both nodes because it's just 100.0f - node
var node = float.TryParse(value, out var parsed) ? parsed : 1.0f;
var node = UserInputParser.TryFloat(value, out var parsed) ? parsed : 1.0f;
node = Math.Clamp(node, 0f, 100.0f);

View File

@@ -10,7 +10,7 @@
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-gas-mixer-ui-mixer-output-pressure}"/>
<Control MinSize="5 0" />
<LineEdit Name="MixerPressureOutputInput" MinSize="60 0" />
<LineEdit Name="MixerPressureOutputInput" MinSize="70 0" />
<Control MinSize="5 0" />
<Button Name="SetMaxPressureButton" Text="{Loc comp-gas-mixer-ui-mixer-max}" />
<Control MinSize="5 0" />

View File

@@ -45,7 +45,7 @@ namespace Content.Client.Atmos.UI
SetMaxPressureButton.OnPressed += _ =>
{
MixerPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.InvariantCulture);
MixerPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.CurrentCulture);
SetOutputPressureButton.Disabled = false;
};
@@ -69,16 +69,16 @@ namespace Content.Client.Atmos.UI
public void SetOutputPressure(float pressure)
{
MixerPressureOutputInput.Text = pressure.ToString(CultureInfo.InvariantCulture);
MixerPressureOutputInput.Text = pressure.ToString(CultureInfo.CurrentCulture);
}
public void SetNodePercentages(float nodeOne)
{
nodeOne *= 100.0f;
MixerNodeOneInput.Text = nodeOne.ToString("0.##", CultureInfo.InvariantCulture);
MixerNodeOneInput.Text = nodeOne.ToString("0.##", CultureInfo.CurrentCulture);
float nodeTwo = 100.0f - nodeOne;
MixerNodeTwoInput.Text = nodeTwo.ToString("0.##", CultureInfo.InvariantCulture);
MixerNodeTwoInput.Text = nodeTwo.ToString("0.##", CultureInfo.CurrentCulture);
}
public void SetMixerStatus(bool enabled)

View File

@@ -1,5 +1,6 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
@@ -46,7 +47,7 @@ namespace Content.Client.Atmos.UI
private void OnPumpOutputPressurePressed(string value)
{
float pressure = float.TryParse(value, out var parsed) ? parsed : 0f;
var pressure = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
if (pressure > MaxPressure) pressure = MaxPressure;
SendMessage(new GasPressurePumpChangeOutputPressureMessage(pressure));

View File

@@ -11,7 +11,7 @@
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-gas-pump-ui-pump-output-pressure}"/>
<Control MinSize="5 0" />
<LineEdit Name="PumpPressureOutputInput" MinSize="60 0" />
<LineEdit Name="PumpPressureOutputInput" MinSize="70 0" />
<Control MinSize="5 0" />
<Button Name="SetMaxPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-max}" />
<Control MinSize="5 0" />

View File

@@ -39,14 +39,14 @@ namespace Content.Client.Atmos.UI
SetMaxPressureButton.OnPressed += _ =>
{
PumpPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.InvariantCulture);
PumpPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.CurrentCulture);
SetOutputPressureButton.Disabled = false;
};
}
public void SetOutputPressure(float pressure)
{
PumpPressureOutputInput.Text = pressure.ToString(CultureInfo.InvariantCulture);
PumpPressureOutputInput.Text = pressure.ToString(CultureInfo.CurrentCulture);
}
public void SetPumpStatus(bool enabled)

View File

@@ -1,5 +1,6 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
@@ -46,7 +47,7 @@ namespace Content.Client.Atmos.UI
private void OnPumpTransferRatePressed(string value)
{
var rate = float.TryParse(value, out var parsed) ? parsed : 0f;
var rate = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
if (rate > MaxTransferRate)
rate = MaxTransferRate;

View File

@@ -11,7 +11,7 @@
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-gas-pump-ui-pump-transfer-rate}"/>
<Control MinSize="5 0" />
<LineEdit Name="PumpTransferRateInput" MinSize="60 0" />
<LineEdit Name="PumpTransferRateInput" MinSize="70 0" />
<Control MinSize="5 0" />
<Button Name="SetMaxRateButton" Text="{Loc comp-gas-pump-ui-pump-set-max}" />
<Control MinSize="5 0" />

View File

@@ -39,14 +39,14 @@ namespace Content.Client.Atmos.UI
SetMaxRateButton.OnPressed += _ =>
{
PumpTransferRateInput.Text = Atmospherics.MaxTransferRate.ToString(CultureInfo.InvariantCulture);
PumpTransferRateInput.Text = Atmospherics.MaxTransferRate.ToString(CultureInfo.CurrentCulture);
SetTransferRateButton.Disabled = false;
};
}
public void SetTransferRate(float rate)
{
PumpTransferRateInput.Text = rate.ToString(CultureInfo.InvariantCulture);
PumpTransferRateInput.Text = rate.ToString(CultureInfo.CurrentCulture);
}
public void SetPumpStatus(bool enabled)

View File

@@ -0,0 +1,54 @@
using System.Globalization;
namespace Content.Shared.Localizations;
/// <summary>
/// Helpers for user input parsing.
/// </summary>
/// <remarks>
/// A wrapper around the <see cref="System.Single.TryParse(string, out float)"/> API,
/// with the goal of trying more options to make the user input parsing less restrictive.
/// For culture-invariant parsing use <see cref="Robust.Shared.Utility.Parse"/>.
/// </remarks>
public static class UserInputParser
{
private static readonly NumberFormatInfo[] StandardDecimalNumberFormats = new[]
{
new NumberFormatInfo()
{
NumberDecimalSeparator = "."
},
new NumberFormatInfo()
{
NumberDecimalSeparator = ","
}
};
public static bool TryFloat(ReadOnlySpan<char> text, out float result)
{
foreach (var format in StandardDecimalNumberFormats)
{
if (float.TryParse(text, NumberStyles.Integer | NumberStyles.AllowDecimalPoint, format, out result))
{
return true;
}
}
result = 0f;
return false;
}
public static bool TryDouble(ReadOnlySpan<char> text, out double result)
{
foreach (var format in StandardDecimalNumberFormats)
{
if (double.TryParse(text, NumberStyles.Integer | NumberStyles.AllowDecimalPoint, format, out result))
{
return true;
}
}
result = 0d;
return false;
}
}

View File

@@ -0,0 +1,41 @@
using Content.Shared.Localizations;
using NUnit.Framework;
namespace Content.Tests.Shared.Localizations
{
[TestFixture]
public sealed class UserInputParserTest
{
[Test]
[TestCase("1234.56", 1234.56f, true)]
[TestCase("1234,56", 1234.56f, true)]
[TestCase(" +1234.56 ", 1234.56f, true)]
[TestCase(" -1234.56 ", -1234.56f, true)]
[TestCase("1234.56e7", 0f, false)]
[TestCase("1,234.56", 0f, false)]
[TestCase("1 234,56", 0f, false)]
public void TryFloatTest(string input, float expectedOutput, bool expectedResult)
{
var result = UserInputParser.TryFloat(input, out var output);
Assert.That(result, Is.EqualTo(expectedResult));
Assert.That(output, Is.EqualTo(expectedOutput).Within(float.Epsilon));
}
[Test]
[TestCase("1234.56", 1234.56d, true)]
[TestCase("1234,56", 1234.56d, true)]
[TestCase(" +1234.56 ", 1234.56d, true)]
[TestCase(" -1234.56 ", -1234.56d, true)]
[TestCase("1234.56e7", 0d, false)]
[TestCase("1,234.56", 0d, false)]
[TestCase("1 234,56", 0d, false)]
public void TryDoubleTest(string input, double expectedOutput, bool expectedResult)
{
var result = UserInputParser.TryDouble(input, out var output);
Assert.That(result, Is.EqualTo(expectedResult));
Assert.That(output, Is.EqualTo(expectedOutput).Within(double.Epsilon));
}
}
}