Skip to content

Commit

Permalink
added removal of trailing zeros
Browse files Browse the repository at this point in the history
  • Loading branch information
Eism committed Dec 10, 2024
1 parent dc9d0fb commit 2649dc7
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 20 deletions.
5 changes: 5 additions & 0 deletions src/framework/uicomponents/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ if (MUSE_MODULE_UI_DISABLE_MODALITY)
endif()

set(MODULE_USE_UNITY OFF)

setup_module()

if (MUSE_MODULE_UI_TESTS)
add_subdirectory(tests)
endif()


31 changes: 31 additions & 0 deletions src/framework/uicomponents/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SPDX-License-Identifier: GPL-3.0-only
# MuseScore-CLA-applies
#
# MuseScore
# Music Composition & Notation
#
# Copyright (C) 2021 MuseScore BVBA and others
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

set(MODULE_TEST muse_uicomponents_tests)

set(MODULE_TEST_SRC
${CMAKE_CURRENT_LIST_DIR}/doubleinputvalidator_tests.cpp
)

set(MODULE_TEST_LINK
muse_uicomponents
)

include(SetupGTest)
90 changes: 90 additions & 0 deletions src/framework/uicomponents/tests/doubleinputvalidator_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2024 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <gtest/gtest.h>

#include "uicomponents/view/validators/doubleinputvalidator.h"

using namespace muse;
using namespace muse::uicomponents;

namespace muse::uicomponents {
class DoubleInputValidatorTests : public ::testing::Test, public QObject
{
public:
void SetUp() override
{
m_validator = new DoubleInputValidator(nullptr);
m_validator->setTop(100.0);
m_validator->setBottom(-100.0);
m_validator->setDecimal(2);
}

void TearDown() override
{
delete m_validator;
}

protected:
DoubleInputValidator* m_validator = nullptr;
};

TEST_F(DoubleInputValidatorTests, Validate) {
struct Input
{
QString str;
QValidator::State expectedState;
QString fixedStr;
};

std::vector<Input> validInputs = {
{ "-0.1", QValidator::Acceptable },
{ "0", QValidator::Acceptable },
{ "1.23", QValidator::Acceptable },
{ "99.99", QValidator::Acceptable },
{ "-100", QValidator::Acceptable },
{ "2.5", QValidator::Acceptable },
{ "0.0", QValidator::Intermediate, "0" },
{ "00.", QValidator::Intermediate, "0" },
{ "2.00", QValidator::Intermediate, "2" },
{ "2.", QValidator::Intermediate, "2" },
{ "-100.1", QValidator::Intermediate, "-100" },
{ "100.1", QValidator::Intermediate, "100" },
{ "1.123", QValidator::Intermediate, "1.12" },
{ "abc", QValidator::Invalid, "" }
};

int pos = 0;
for (Input& input : validInputs) {
EXPECT_EQ(m_validator->validate(input.str, pos), input.expectedState);

if (QValidator::Invalid == input.expectedState) {
continue;
}

QString fixInput = input.str;
m_validator->fixup(fixInput);

QString expectedStr = QValidator::Acceptable == input.expectedState ? input.str : input.fixedStr;
EXPECT_EQ(expectedStr, fixInput);
}
}
}
60 changes: 40 additions & 20 deletions src/framework/uicomponents/view/validators/doubleinputvalidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,67 +31,87 @@ DoubleInputValidator::DoubleInputValidator(QObject* parent)

void DoubleInputValidator::fixup(QString& string) const
{
auto zeros = [](int num)->QString {
QString s = QString();
for (int i = 0; i < num; i++) {
s.append("0");
auto removeTrailingZeros = [](QString& str) {
if (str.isEmpty()) {
return;
}
return s;
};

if (!string.contains(".")) {
string.append("." + zeros(m_decimal));
}
if (!str.contains('.')) {
return;
}

size_t num = str.size();
for (size_t i = num - 1; i > 0; i--) {
if (str[i] == '0') {
str.remove(i, 1);
} else if (str[i] == '.') {
str.remove(i, 1);
break;
} else {
break;
}
}
};

if (string.startsWith(".")) {
string.prepend("0");
}

if (string.endsWith(".")) {
string.append(zeros(m_decimal));
string.remove(string.size() - 1, 1);
}

QStringList strList = string.split(".", Qt::SkipEmptyParts);

QString intPart = strList.at(0);
QString floatPart = strList.at(1);

if (floatPart.length() < m_decimal) {
floatPart.append(zeros(m_decimal - floatPart.length()));
}
QString floatPart = strList.size() > 1 ? strList.at(1) : 0;

if (intPart.contains(QRegularExpression("^0{1,3}$"))) {
intPart = QString("0");
} else if (intPart.contains(QRegularExpression("^\\-0{0,3}$"))) {
intPart = QString("-0");
}

if (intPart == QString("-0") && floatPart == zeros(m_decimal)) {
if (intPart == QString("-0") && floatPart.isEmpty()) {
intPart = QString("0");
}

if (floatPart.size() > m_decimal) {
floatPart = floatPart.remove(m_decimal, floatPart.size() - m_decimal);
}

string = QString("%1.%2").arg(intPart).arg(floatPart);

if (string.toDouble() > m_top) {
string = QString::number(m_top, 'f', m_decimal);
} else if (string.toDouble() < m_bottom) {
string = QString::number(m_bottom, 'f', m_decimal);
}

removeTrailingZeros(string);
}

QValidator::State DoubleInputValidator::validate(QString& inputStr, int& cursorPos) const
{
QValidator::State state = Invalid;

if (inputStr.contains(QRegularExpression(QString("^\\-?\\d{1,3}\\.\\d{%1}$").arg(m_decimal)))) {
if (inputStr.contains(QRegularExpression("^\\-?0{2,3}\\."))
|| (inputStr.startsWith("-") && muse::RealIsNull(inputStr.toDouble()))) {
const QRegularExpression validRegex(QString("^\\-?\\d{1,3}(\\.\\d{1,%1})?$").arg(m_decimal));

if (inputStr.contains(validRegex)) {
static const QRegularExpression invalidZeroRegex("^\\-?0{2,3}\\."); // for '-000.'
static const QRegularExpression invalidTrailingZeroRegex("^\\-?\\d+\\.0{1,}$"); // for '1.00'
static const QRegularExpression invalidTrailingDotRegex("^\\-?\\d+\\.$"); // for '1.'

if (inputStr.contains(invalidZeroRegex)
|| (inputStr.startsWith("-") && muse::RealIsNull(inputStr.toDouble())) // for "-000"
|| inputStr.contains(invalidTrailingZeroRegex)
|| inputStr.contains(invalidTrailingDotRegex)) {
state = Intermediate;
} else {
state = Acceptable;
}
} else if (inputStr.contains(QRegularExpression("^\\-?\\d{0,3}\\.?$"))
|| inputStr.contains(QRegularExpression(QString("^\\-?\\d{0,3}\\.\\d{0,%1}$").arg(m_decimal)))) {
|| inputStr.contains(QRegularExpression(QString("^\\-?\\d{0,3}\\.\\d{%1,}$").arg(m_decimal)))) {
state = Intermediate;
} else {
cursorPos = 0;
Expand Down

0 comments on commit 2649dc7

Please sign in to comment.