| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #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; |
| | } |
| |
|