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:
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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)
|
||||
|
||||
54
Content.Shared/Localizations/UserInputParser.cs
Normal file
54
Content.Shared/Localizations/UserInputParser.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
41
Content.Tests/Shared/Localizations/UserInputParserTest.cs
Normal file
41
Content.Tests/Shared/Localizations/UserInputParserTest.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user