| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <cstdlib> |
| #include <QLineEdit> |
| #include <QRegularExpression> |
| #include "citra_qt/util/spinbox.h" |
| #include "common/assert.h" |
|
|
| CSpinBox::CSpinBox(QWidget* parent) |
| : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) { |
| |
| |
| |
| connect(lineEdit(), &QLineEdit::textEdited, this, &CSpinBox::OnEditingFinished); |
|
|
| UpdateText(); |
| } |
|
|
| void CSpinBox::SetValue(qint64 val) { |
| auto old_value = value; |
| value = std::max(std::min(val, max_value), min_value); |
|
|
| if (old_value != value) { |
| UpdateText(); |
| emit ValueChanged(value); |
| } |
| } |
|
|
| void CSpinBox::SetRange(qint64 min, qint64 max) { |
| min_value = min; |
| max_value = max; |
|
|
| SetValue(value); |
| UpdateText(); |
| } |
|
|
| void CSpinBox::stepBy(int steps) { |
| auto new_value = value; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| constexpr qint64 qint64_min = std::numeric_limits<qint64>::min(); |
| constexpr qint64 qint64_max = std::numeric_limits<qint64>::max(); |
| if (steps < 0 && new_value < qint64_min - steps) { |
| new_value = qint64_min; |
| } else if (steps > 0 && new_value > qint64_max - steps) { |
| new_value = qint64_max; |
| } else { |
| new_value += steps; |
| } |
|
|
| SetValue(new_value); |
| UpdateText(); |
| } |
|
|
| QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const { |
| StepEnabled ret = StepNone; |
|
|
| if (value > min_value) |
| ret |= StepDownEnabled; |
|
|
| if (value < max_value) |
| ret |= StepUpEnabled; |
|
|
| return ret; |
| } |
|
|
| void CSpinBox::SetBase(int base) { |
| this->base = base; |
|
|
| UpdateText(); |
| } |
|
|
| void CSpinBox::SetNumDigits(int num_digits) { |
| this->num_digits = num_digits; |
|
|
| UpdateText(); |
| } |
|
|
| void CSpinBox::SetPrefix(const QString& prefix) { |
| this->prefix = prefix; |
|
|
| UpdateText(); |
| } |
|
|
| void CSpinBox::SetSuffix(const QString& suffix) { |
| this->suffix = suffix; |
|
|
| UpdateText(); |
| } |
|
|
| static QString StringToInputMask(const QString& input) { |
| QString mask = input; |
|
|
| |
| mask.replace(QStringLiteral("\\"), QStringLiteral("\\\\")); |
| mask.replace(QLatin1Char{'A'}, QStringLiteral("\\A")); |
| mask.replace(QLatin1Char{'a'}, QStringLiteral("\\a")); |
| mask.replace(QLatin1Char{'N'}, QStringLiteral("\\N")); |
| mask.replace(QLatin1Char{'n'}, QStringLiteral("\\n")); |
| mask.replace(QLatin1Char{'X'}, QStringLiteral("\\X")); |
| mask.replace(QLatin1Char{'x'}, QStringLiteral("\\x")); |
| mask.replace(QLatin1Char{'9'}, QStringLiteral("\\9")); |
| mask.replace(QLatin1Char{'0'}, QStringLiteral("\\0")); |
| mask.replace(QLatin1Char{'D'}, QStringLiteral("\\D")); |
| mask.replace(QLatin1Char{'d'}, QStringLiteral("\\d")); |
| mask.replace(QLatin1Char{'#'}, QStringLiteral("\\#")); |
| mask.replace(QLatin1Char{'H'}, QStringLiteral("\\H")); |
| mask.replace(QLatin1Char{'h'}, QStringLiteral("\\h")); |
| mask.replace(QLatin1Char{'B'}, QStringLiteral("\\B")); |
| mask.replace(QLatin1Char{'b'}, QStringLiteral("\\b")); |
| mask.replace(QLatin1Char{'>'}, QStringLiteral("\\>")); |
| mask.replace(QLatin1Char{'<'}, QStringLiteral("\\<")); |
| mask.replace(QLatin1Char{'!'}, QStringLiteral("\\!")); |
|
|
| return mask; |
| } |
|
|
| void CSpinBox::UpdateText() { |
| |
| |
| QString mask; |
| if (num_digits != 0) { |
| mask += StringToInputMask(prefix); |
|
|
| |
| if (HasSign()) |
| mask.append(QLatin1Char{'X'}); |
|
|
| |
| mask.append(QLatin1Char{'>'}); |
|
|
| |
| |
| mask.append(QStringLiteral("H").repeated(std::max(num_digits, 1))); |
|
|
| |
| mask.append(QLatin1Char{'!'}); |
|
|
| mask.append(StringToInputMask(suffix)); |
| } |
| lineEdit()->setInputMask(mask); |
|
|
| |
| |
| |
| int cursor_position = lineEdit()->cursorPosition(); |
| lineEdit()->setText(TextFromValue()); |
| lineEdit()->setCursorPosition(cursor_position); |
| } |
|
|
| QString CSpinBox::TextFromValue() { |
| return prefix + |
| (HasSign() ? ((value < 0) ? QStringLiteral("-") : QStringLiteral("+")) : QString{}) + |
| QStringLiteral("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper() + |
| suffix; |
| } |
|
|
| qint64 CSpinBox::ValueFromText() { |
| qsizetype strpos = prefix.length(); |
|
|
| QString num_string = text().mid(strpos, text().length() - strpos - suffix.length()); |
| return num_string.toLongLong(nullptr, base); |
| } |
|
|
| bool CSpinBox::HasSign() const { |
| return base == 10 && min_value < 0; |
| } |
|
|
| void CSpinBox::OnEditingFinished() { |
| |
| QString input = lineEdit()->text(); |
| int pos = 0; |
| if (QValidator::Acceptable == validate(input, pos)) |
| SetValue(ValueFromText()); |
| } |
|
|
| QValidator::State CSpinBox::validate(QString& input, int& pos) const { |
| if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) |
| return QValidator::Invalid; |
|
|
| qsizetype strpos = prefix.length(); |
|
|
| |
| if (strpos >= input.length() - HasSign() - suffix.length()) |
| return QValidator::Intermediate; |
|
|
| DEBUG_ASSERT(base <= 10 || base == 16); |
| QString regexp; |
|
|
| |
| if (HasSign()) |
| regexp.append(QStringLiteral("[+\\-]")); |
|
|
| |
| regexp.append(QStringLiteral("[0-%1").arg(std::min(base, 9))); |
| if (base == 16) { |
| regexp.append(QStringLiteral("a-fA-F")); |
| } |
| regexp.append(QLatin1Char(']')); |
|
|
| |
| if (num_digits > 0) { |
| regexp.append(QStringLiteral("{%1}").arg(num_digits)); |
| } else { |
| regexp.append(QLatin1Char{'+'}); |
| } |
|
|
| |
| QRegularExpression num_regexp(QRegularExpression::anchoredPattern(regexp)); |
| qsizetype num_pos = strpos; |
| QString sub_input = input.mid(strpos, input.length() - strpos - suffix.length()); |
|
|
| auto match = num_regexp.match(sub_input); |
| if (!match.hasMatch()) |
| return QValidator::Invalid; |
|
|
| sub_input = sub_input.left(match.capturedLength()); |
| bool ok; |
| qint64 val = sub_input.toLongLong(&ok, base); |
|
|
| if (!ok) |
| return QValidator::Invalid; |
|
|
| |
| if (val < min_value || val > max_value) |
| return QValidator::Invalid; |
|
|
| |
| strpos += match.capturedLength(); |
|
|
| if (!suffix.isEmpty() && input.mid(strpos) != suffix) { |
| return QValidator::Invalid; |
| } else { |
| strpos += suffix.length(); |
| } |
|
|
| if (strpos != input.length()) |
| return QValidator::Invalid; |
|
|
| |
| input.replace(num_pos, sub_input.length(), sub_input.toUpper()); |
|
|
| return QValidator::Acceptable; |
| } |
|
|