Reject invalid Array and transform input with optional results

Rename the localized float parser to lcParseFloatValueLocalized and return std::optional<float> so conversion failures and nonfinite values cannot silently become zero. Return std::optional<lcVector3> from GetTransformAmount and apply transforms only when every nonempty axis is valid, preserving blank axes as zero.

Validate all Array offsets and rotations before updating dialog results, and show an informational message with focus on invalid input. Verified with a macOS Qt debug build and actual Array/transform checks in US English, German, and Arabic locales, including blank input, overflow, NaN, and infinity.
This commit is contained in:
Leonardo Zide committed 2026-10-23 13:04:18 -07:00
1 parent 2125868dc6
commit 5c1126519d
5 files changed
+80 -32

No files matched your search

+38 -21
View File
@@ -76,27 +76,44 @@ void lcArrayDialog::accept()
return;
}
mCounts[0] = Counts[0];
mCounts[1] = Counts[1];
mCounts[2] = Counts[2];
mOffsets[0].x = lcParseValueLocalized(ui->offset1x->text());
mOffsets[0].y = lcParseValueLocalized(ui->offset1y->text());
mOffsets[0].z = lcParseValueLocalized(ui->offset1z->text());
mOffsets[1].x = lcParseValueLocalized(ui->offset2x->text());
mOffsets[1].y = lcParseValueLocalized(ui->offset2y->text());
mOffsets[1].z = lcParseValueLocalized(ui->offset2z->text());
mOffsets[2].x = lcParseValueLocalized(ui->offset3x->text());
mOffsets[2].y = lcParseValueLocalized(ui->offset3y->text());
mOffsets[2].z = lcParseValueLocalized(ui->offset3z->text());
mRotations[0].x = lcParseValueLocalized(ui->rotation1x->text());
mRotations[0].y = lcParseValueLocalized(ui->rotation1y->text());
mRotations[0].z = lcParseValueLocalized(ui->rotation1z->text());
mRotations[1].x = lcParseValueLocalized(ui->rotation2x->text());
mRotations[1].y = lcParseValueLocalized(ui->rotation2y->text());
mRotations[1].z = lcParseValueLocalized(ui->rotation2z->text());
mRotations[2].x = lcParseValueLocalized(ui->rotation3x->text());
mRotations[2].y = lcParseValueLocalized(ui->rotation3y->text());
mRotations[2].z = lcParseValueLocalized(ui->rotation3z->text());
QLineEdit* Edits[2][3][3] =
{
{
{ ui->offset1x, ui->offset1y, ui->offset1z },
{ ui->offset2x, ui->offset2y, ui->offset2z },
{ ui->offset3x, ui->offset3y, ui->offset3z }
},
{
{ ui->rotation1x, ui->rotation1y, ui->rotation1z },
{ ui->rotation2x, ui->rotation2y, ui->rotation2z },
{ ui->rotation3x, ui->rotation3y, ui->rotation3z }
}
};
lcVector3 Values[2][3];
for (int Type = 0; Type < 2; Type++)
for (int Dimension = 0; Dimension < 3; Dimension++)
for (int Axis = 0; Axis < 3; Axis++)
{
QLineEdit* Edit = Edits[Type][Dimension][Axis];
const std::optional<float> Value = lcParseFloatValueLocalized(Edit->text(), Edit->validator()->locale());
if (!Value || !Edit->hasAcceptableInput())
{
QMessageBox::information(this, windowTitle(), tr("Please enter a valid value for '%1'.").arg(Edit->accessibleName()));
Edit->setFocus();
return;
}
Values[Type][Dimension][Axis] = *Value;
}
for (int Dimension = 0; Dimension < 3; Dimension++)
{
mCounts[Dimension] = Counts[Dimension];
mOffsets[Dimension] = Values[0][Dimension];
mRotations[Dimension] = Values[1][Dimension];
}
QDialog::accept();
}
+32 -7
View File
@@ -2073,15 +2073,35 @@ void lcMainWindow::SetInsertPieceData(lcPieceData* PieceData)
lcGetPiecesLibrary()->RequestPieceDataLoad(mInsertPieceData.Get(), lcLoadPriority::Normal);
}
lcVector3 lcMainWindow::GetTransformAmount()
std::optional<lcVector3> lcMainWindow::GetTransformAmount()
{
lcVector3 Transform;
QLineEdit* Edits[] = { mTransformXEdit, mTransformYEdit, mTransformZEdit };
lcVector3 Values;
Transform.x = lcParseValueLocalized(mTransformXEdit->text());
Transform.y = lcParseValueLocalized(mTransformYEdit->text());
Transform.z = lcParseValueLocalized(mTransformZEdit->text());
for (int Axis = 0; Axis < 3; Axis++)
{
QLineEdit* Edit = Edits[Axis];
const QString Text = Edit->text().trimmed();
return Transform;
if (Text.isEmpty())
{
Values[Axis] = 0.0f;
continue;
}
const std::optional<float> Value = lcParseFloatValueLocalized(Text, Edit->locale());
if (!Value)
{
QMessageBox::information(this, tr("Transform"), tr("Please enter a valid value for '%1'.").arg(Edit->accessibleName()));
Edit->setFocus();
return std::nullopt;
}
Values[Axis] = *Value;
}
return Values;
}
void lcMainWindow::SplitView(Qt::Orientation Orientation)
@@ -3730,7 +3750,12 @@ void lcMainWindow::HandleCommand(lcCommandId CommandId)
case LC_EDIT_TRANSFORM:
if (ActiveModel)
ActiveModel->TransformSelectedObjects(GetTransformType(), GetTransformAmount());
{
const std::optional<lcVector3> Transform = GetTransformAmount();
if (Transform)
ActiveModel->TransformSelectedObjects(GetTransformType(), *Transform);
}
break;
case LC_EDIT_TRANSFORM_ABSOLUTE_TRANSLATION:
+1 -1
View File
@@ -259,7 +259,7 @@ public:
void UpdateModified(bool Modified);
void UpdateShortcuts();
lcVector3 GetTransformAmount();
std::optional<lcVector3> GetTransformAmount();
bool eventFilter(QObject* Object, QEvent* Event) override;
+8 -2
View File
@@ -90,9 +90,15 @@ QString lcFormatIntegerLocalized(qlonglong Value, QLocale Locale)
return Locale.toString(Value);
}
float lcParseValueLocalized(const QString& Value)
std::optional<float> lcParseFloatValueLocalized(const QString& Value, QLocale Locale)
{
return QLocale::system().toFloat(Value);
bool Ok;
const float Number = Locale.toFloat(Value, &Ok);
if (!Ok || !std::isfinite(Number))
return std::nullopt;
return Number;
}
#ifdef Q_OS_WIN
+1 -1
View File
@@ -14,7 +14,7 @@ constexpr int LC_MAX_AREA_LIGHT_GRID = 64;
QString lcFormatValue(float Value, int Precision);
QString lcFormatValueLocalized(float Value);
QString lcFormatIntegerLocalized(qlonglong Value, QLocale Locale);
float lcParseValueLocalized(const QString& Value);
std::optional<float> lcParseFloatValueLocalized(const QString& Value, QLocale Locale);
void lcSetupDialogHelp(QDialog* Dialog, QDialogButtonBox* ButtonBox);
void lcSetupWidgetHelp(QWidget* Root, QDialogButtonBox* ButtonBox);