Skip to content

Commit 2c7851c

Browse files
authored
Merge pull request #25782 from Eism/text_field_fix
Added removal of trailing zeros for Text input field
2 parents 0e7740f + 2649dc7 commit 2c7851c

File tree

5 files changed

+170
-25
lines changed

5 files changed

+170
-25
lines changed

src/framework/uicomponents/CMakeLists.txt

+5
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ if (MUSE_MODULE_UI_DISABLE_MODALITY)
120120
endif()
121121

122122
set(MODULE_USE_UNITY OFF)
123+
123124
setup_module()
124125

126+
if (MUSE_MODULE_UI_TESTS)
127+
add_subdirectory(tests)
128+
endif()
129+
125130

src/framework/uicomponents/qml/Muse/UiComponents/TextInputField.qml

+4-5
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,12 @@ FocusScope {
197197
event.accepted = false
198198

199199
root.focus = false
200-
root.textEditingFinished(valueInput.text)
201200
}
202201
}
203202

204203
Keys.onPressed: function(event) {
205204
var isAcceptKey = event.key === Qt.Key_Enter || event.key === Qt.Key_Return
206205
var isEscapeKey = event.key === Qt.Key_Escape
207-
if (isAcceptKey || isEscapeKey) {
208-
root.textEditingFinished(valueInput.text)
209-
}
210206

211207
if (isAcceptKey) {
212208
root.accepted()
@@ -227,7 +223,6 @@ FocusScope {
227223
selectAll()
228224
} else {
229225
deselect()
230-
root.textEditingFinished(valueInput.text)
231226
}
232227
}
233228

@@ -246,6 +241,10 @@ FocusScope {
246241

247242
root.textEdited(text)
248243
}
244+
245+
onEditingFinished: {
246+
root.textEditingFinished(valueInput.text)
247+
}
249248
}
250249

251250
StyledTextLabel {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# SPDX-License-Identifier: GPL-3.0-only
2+
# MuseScore-CLA-applies
3+
#
4+
# MuseScore
5+
# Music Composition & Notation
6+
#
7+
# Copyright (C) 2021 MuseScore BVBA and others
8+
#
9+
# This program is free software: you can redistribute it and/or modify
10+
# it under the terms of the GNU General Public License version 3 as
11+
# published by the Free Software Foundation.
12+
#
13+
# This program is distributed in the hope that it will be useful,
14+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License
19+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
21+
set(MODULE_TEST muse_uicomponents_tests)
22+
23+
set(MODULE_TEST_SRC
24+
${CMAKE_CURRENT_LIST_DIR}/doubleinputvalidator_tests.cpp
25+
)
26+
27+
set(MODULE_TEST_LINK
28+
muse_uicomponents
29+
)
30+
31+
include(SetupGTest)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* SPDX-License-Identifier: GPL-3.0-only
3+
* MuseScore-CLA-applies
4+
*
5+
* MuseScore
6+
* Music Composition & Notation
7+
*
8+
* Copyright (C) 2024 MuseScore BVBA and others
9+
*
10+
* This program is free software: you can redistribute it and/or modify
11+
* it under the terms of the GNU General Public License version 3 as
12+
* published by the Free Software Foundation.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU General Public License
20+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
21+
*/
22+
#include <gtest/gtest.h>
23+
24+
#include "uicomponents/view/validators/doubleinputvalidator.h"
25+
26+
using namespace muse;
27+
using namespace muse::uicomponents;
28+
29+
namespace muse::uicomponents {
30+
class DoubleInputValidatorTests : public ::testing::Test, public QObject
31+
{
32+
public:
33+
void SetUp() override
34+
{
35+
m_validator = new DoubleInputValidator(nullptr);
36+
m_validator->setTop(100.0);
37+
m_validator->setBottom(-100.0);
38+
m_validator->setDecimal(2);
39+
}
40+
41+
void TearDown() override
42+
{
43+
delete m_validator;
44+
}
45+
46+
protected:
47+
DoubleInputValidator* m_validator = nullptr;
48+
};
49+
50+
TEST_F(DoubleInputValidatorTests, Validate) {
51+
struct Input
52+
{
53+
QString str;
54+
QValidator::State expectedState;
55+
QString fixedStr;
56+
};
57+
58+
std::vector<Input> validInputs = {
59+
{ "-0.1", QValidator::Acceptable },
60+
{ "0", QValidator::Acceptable },
61+
{ "1.23", QValidator::Acceptable },
62+
{ "99.99", QValidator::Acceptable },
63+
{ "-100", QValidator::Acceptable },
64+
{ "2.5", QValidator::Acceptable },
65+
{ "0.0", QValidator::Intermediate, "0" },
66+
{ "00.", QValidator::Intermediate, "0" },
67+
{ "2.00", QValidator::Intermediate, "2" },
68+
{ "2.", QValidator::Intermediate, "2" },
69+
{ "-100.1", QValidator::Intermediate, "-100" },
70+
{ "100.1", QValidator::Intermediate, "100" },
71+
{ "1.123", QValidator::Intermediate, "1.12" },
72+
{ "abc", QValidator::Invalid, "" }
73+
};
74+
75+
int pos = 0;
76+
for (Input& input : validInputs) {
77+
EXPECT_EQ(m_validator->validate(input.str, pos), input.expectedState);
78+
79+
if (QValidator::Invalid == input.expectedState) {
80+
continue;
81+
}
82+
83+
QString fixInput = input.str;
84+
m_validator->fixup(fixInput);
85+
86+
QString expectedStr = QValidator::Acceptable == input.expectedState ? input.str : input.fixedStr;
87+
EXPECT_EQ(expectedStr, fixInput);
88+
}
89+
}
90+
}

src/framework/uicomponents/view/validators/doubleinputvalidator.cpp

+40-20
Original file line numberDiff line numberDiff line change
@@ -31,67 +31,87 @@ DoubleInputValidator::DoubleInputValidator(QObject* parent)
3131

3232
void DoubleInputValidator::fixup(QString& string) const
3333
{
34-
auto zeros = [](int num)->QString {
35-
QString s = QString();
36-
for (int i = 0; i < num; i++) {
37-
s.append("0");
34+
auto removeTrailingZeros = [](QString& str) {
35+
if (str.isEmpty()) {
36+
return;
3837
}
39-
return s;
40-
};
4138

42-
if (!string.contains(".")) {
43-
string.append("." + zeros(m_decimal));
44-
}
39+
if (!str.contains('.')) {
40+
return;
41+
}
42+
43+
size_t num = str.size();
44+
for (size_t i = num - 1; i > 0; i--) {
45+
if (str[i] == '0') {
46+
str.remove(i, 1);
47+
} else if (str[i] == '.') {
48+
str.remove(i, 1);
49+
break;
50+
} else {
51+
break;
52+
}
53+
}
54+
};
4555

4656
if (string.startsWith(".")) {
4757
string.prepend("0");
4858
}
4959

5060
if (string.endsWith(".")) {
51-
string.append(zeros(m_decimal));
61+
string.remove(string.size() - 1, 1);
5262
}
5363

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

5666
QString intPart = strList.at(0);
57-
QString floatPart = strList.at(1);
58-
59-
if (floatPart.length() < m_decimal) {
60-
floatPart.append(zeros(m_decimal - floatPart.length()));
61-
}
67+
QString floatPart = strList.size() > 1 ? strList.at(1) : 0;
6268

6369
if (intPart.contains(QRegularExpression("^0{1,3}$"))) {
6470
intPart = QString("0");
6571
} else if (intPart.contains(QRegularExpression("^\\-0{0,3}$"))) {
6672
intPart = QString("-0");
6773
}
6874

69-
if (intPart == QString("-0") && floatPart == zeros(m_decimal)) {
75+
if (intPart == QString("-0") && floatPart.isEmpty()) {
7076
intPart = QString("0");
7177
}
7278

79+
if (floatPart.size() > m_decimal) {
80+
floatPart = floatPart.remove(m_decimal, floatPart.size() - m_decimal);
81+
}
82+
7383
string = QString("%1.%2").arg(intPart).arg(floatPart);
7484

7585
if (string.toDouble() > m_top) {
7686
string = QString::number(m_top, 'f', m_decimal);
7787
} else if (string.toDouble() < m_bottom) {
7888
string = QString::number(m_bottom, 'f', m_decimal);
7989
}
90+
91+
removeTrailingZeros(string);
8092
}
8193

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

86-
if (inputStr.contains(QRegularExpression(QString("^\\-?\\d{1,3}\\.\\d{%1}$").arg(m_decimal)))) {
87-
if (inputStr.contains(QRegularExpression("^\\-?0{2,3}\\."))
88-
|| (inputStr.startsWith("-") && muse::RealIsNull(inputStr.toDouble()))) {
98+
const QRegularExpression validRegex(QString("^\\-?\\d{1,3}(\\.\\d{1,%1})?$").arg(m_decimal));
99+
100+
if (inputStr.contains(validRegex)) {
101+
static const QRegularExpression invalidZeroRegex("^\\-?0{2,3}\\."); // for '-000.'
102+
static const QRegularExpression invalidTrailingZeroRegex("^\\-?\\d+\\.0{1,}$"); // for '1.00'
103+
static const QRegularExpression invalidTrailingDotRegex("^\\-?\\d+\\.$"); // for '1.'
104+
105+
if (inputStr.contains(invalidZeroRegex)
106+
|| (inputStr.startsWith("-") && muse::RealIsNull(inputStr.toDouble())) // for "-000"
107+
|| inputStr.contains(invalidTrailingZeroRegex)
108+
|| inputStr.contains(invalidTrailingDotRegex)) {
89109
state = Intermediate;
90110
} else {
91111
state = Acceptable;
92112
}
93113
} else if (inputStr.contains(QRegularExpression("^\\-?\\d{0,3}\\.?$"))
94-
|| inputStr.contains(QRegularExpression(QString("^\\-?\\d{0,3}\\.\\d{0,%1}$").arg(m_decimal)))) {
114+
|| inputStr.contains(QRegularExpression(QString("^\\-?\\d{0,3}\\.\\d{%1,}$").arg(m_decimal)))) {
95115
state = Intermediate;
96116
} else {
97117
cursorPos = 0;

0 commit comments

Comments
 (0)