Add UIntSpinBox class
This commit is contained in:
parent
b00c644099
commit
603df4defa
5 changed files with 266 additions and 12 deletions
|
@ -321,7 +321,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="NoScrollSpinBox" name="spinBox_TerrainTypeMask">
|
||||
<widget class="UIntSpinBox" name="spinBox_TerrainTypeMask">
|
||||
<property name="toolTip">
|
||||
<string>The mask used to read/write Terrain Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||
</property>
|
||||
|
@ -378,7 +378,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="NoScrollSpinBox" name="spinBox_BehaviorMask">
|
||||
<widget class="UIntSpinBox" name="spinBox_BehaviorMask">
|
||||
<property name="toolTip">
|
||||
<string>The mask used to read/write Metatile Behavior from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||
</property>
|
||||
|
@ -411,7 +411,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="NoScrollSpinBox" name="spinBox_EncounterTypeMask">
|
||||
<widget class="UIntSpinBox" name="spinBox_EncounterTypeMask">
|
||||
<property name="toolTip">
|
||||
<string>The mask used to read/write Encounter Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||
</property>
|
||||
|
@ -438,7 +438,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="NoScrollSpinBox" name="spinBox_LayerTypeMask">
|
||||
<widget class="UIntSpinBox" name="spinBox_LayerTypeMask">
|
||||
<property name="toolTip">
|
||||
<string>The mask used to read/write Layer Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||
</property>
|
||||
|
@ -671,6 +671,11 @@
|
|||
<extends>QSpinBox</extends>
|
||||
<header>noscrollspinbox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>UIntSpinBox</class>
|
||||
<extends>QAbstractSpinBox</extends>
|
||||
<header>uintspinbox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../resources/images.qrc"/>
|
||||
|
|
67
include/ui/uintspinbox.h
Normal file
67
include/ui/uintspinbox.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
#ifndef UINTSPINBOX_H
|
||||
#define UINTSPINBOX_H
|
||||
|
||||
#include <QAbstractSpinBox>
|
||||
#include <QLineEdit>
|
||||
|
||||
/*
|
||||
QSpinBox stores minimum/maximum/value as ints. This class is a version of QAbstractSpinBox for values above 0x7FFFFFFF.
|
||||
It minimally implements some QSpinBox-specific features like prefixes to simplify support for hex value input.
|
||||
It also implements the scroll focus requirements of NoScrollSpinBox.
|
||||
*/
|
||||
|
||||
class UIntSpinBox : public QAbstractSpinBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit UIntSpinBox(QWidget *parent = nullptr);
|
||||
~UIntSpinBox() {};
|
||||
|
||||
uint32_t value() const { return m_value; }
|
||||
uint32_t minimum() const { return m_minimum; }
|
||||
uint32_t maximum() const { return m_maximum; }
|
||||
QString prefix() const { return m_prefix; }
|
||||
int displayIntegerBase() const { return m_displayIntegerBase; }
|
||||
bool hasPadding() const { return m_hasPadding; }
|
||||
|
||||
void setMinimum(uint32_t min);
|
||||
void setMaximum(uint32_t max);
|
||||
void setRange(uint32_t min, uint32_t max);
|
||||
void setPrefix(const QString &prefix);
|
||||
void setDisplayIntegerBase(int base);
|
||||
void setHasPadding(bool enabled);
|
||||
|
||||
private:
|
||||
uint32_t m_minimum;
|
||||
uint32_t m_maximum;
|
||||
uint32_t m_value;
|
||||
QString m_prefix;
|
||||
int m_displayIntegerBase;
|
||||
bool m_hasPadding;
|
||||
int m_numChars;
|
||||
|
||||
void updateEdit();
|
||||
QString stripped(QString input) const;
|
||||
|
||||
virtual void stepBy(int steps) override;
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
virtual void focusOutEvent(QFocusEvent *event) override;
|
||||
|
||||
protected:
|
||||
virtual uint32_t valueFromText(const QString &text) const;
|
||||
virtual QString textFromValue(uint32_t val) const;
|
||||
virtual QValidator::State validate(QString &input, int &pos) const override;
|
||||
virtual QAbstractSpinBox::StepEnabled stepEnabled() const override;
|
||||
|
||||
|
||||
public slots:
|
||||
void setValue(uint32_t val);
|
||||
void onEditFinished();
|
||||
|
||||
signals:
|
||||
void valueChanged(uint32_t val);
|
||||
void textChanged(const QString &text);
|
||||
};
|
||||
|
||||
#endif // UINTSPINBOX_H
|
|
@ -100,7 +100,8 @@ SOURCES += src/core/block.cpp \
|
|||
src/mainwindow.cpp \
|
||||
src/project.cpp \
|
||||
src/settings.cpp \
|
||||
src/log.cpp
|
||||
src/log.cpp \
|
||||
src/ui/uintspinbox.cpp
|
||||
|
||||
HEADERS += include/core/block.h \
|
||||
include/core/blockdata.h \
|
||||
|
@ -192,7 +193,8 @@ HEADERS += include/core/block.h \
|
|||
include/scripting.h \
|
||||
include/scriptutility.h \
|
||||
include/settings.h \
|
||||
include/log.h
|
||||
include/log.h \
|
||||
include/ui/uintspinbox.h
|
||||
|
||||
FORMS += forms/mainwindow.ui \
|
||||
forms/prefabcreationdialog.ui \
|
||||
|
|
|
@ -42,10 +42,12 @@ void ProjectSettingsEditor::connectSignals() {
|
|||
connect(combo, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::markEdited);
|
||||
for (auto checkBox : ui->centralwidget->findChildren<QCheckBox *>())
|
||||
connect(checkBox, &QCheckBox::stateChanged, this, &ProjectSettingsEditor::markEdited);
|
||||
for (auto spinBox : ui->centralwidget->findChildren<NoScrollSpinBox *>())
|
||||
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), [this](int) { this->markEdited(); });
|
||||
for (auto lineEdit : ui->centralwidget->findChildren<QLineEdit *>())
|
||||
connect(lineEdit, &QLineEdit::textEdited, this, &ProjectSettingsEditor::markEdited);
|
||||
for (auto spinBox : ui->centralwidget->findChildren<NoScrollSpinBox *>())
|
||||
connect(spinBox, &QSpinBox::textChanged, this, &ProjectSettingsEditor::markEdited);
|
||||
for (auto spinBox : ui->centralwidget->findChildren<UIntSpinBox *>())
|
||||
connect(spinBox, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::markEdited);
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::markEdited() {
|
||||
|
@ -71,10 +73,10 @@ void ProjectSettingsEditor::initUi() {
|
|||
ui->spinBox_FillMetatile->setMaximum(Project::getNumMetatilesTotal() - 1);
|
||||
|
||||
// TODO: These need to be subclassed (or changed to line edits) to handle larger values
|
||||
ui->spinBox_BehaviorMask->setMaximum(INT_MAX);
|
||||
ui->spinBox_EncounterTypeMask->setMaximum(INT_MAX);
|
||||
ui->spinBox_LayerTypeMask->setMaximum(INT_MAX);
|
||||
ui->spinBox_TerrainTypeMask->setMaximum(INT_MAX);
|
||||
ui->spinBox_BehaviorMask->setMaximum(UINT_MAX);
|
||||
ui->spinBox_EncounterTypeMask->setMaximum(UINT_MAX);
|
||||
ui->spinBox_LayerTypeMask->setMaximum(UINT_MAX);
|
||||
ui->spinBox_TerrainTypeMask->setMaximum(UINT_MAX);
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::createProjectPathsTable() {
|
||||
|
|
178
src/ui/uintspinbox.cpp
Normal file
178
src/ui/uintspinbox.cpp
Normal file
|
@ -0,0 +1,178 @@
|
|||
#include "uintspinbox.h"
|
||||
|
||||
UIntSpinBox::UIntSpinBox(QWidget *parent)
|
||||
: QAbstractSpinBox(parent)
|
||||
{
|
||||
// Don't let scrolling hijack focus.
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
m_minimum = 0;
|
||||
m_maximum = 99;
|
||||
m_value = m_minimum;
|
||||
m_displayIntegerBase = 10;
|
||||
m_numChars = 2;
|
||||
m_hasPadding = false;
|
||||
|
||||
this->updateEdit();
|
||||
connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(onEditFinished()));
|
||||
};
|
||||
|
||||
void UIntSpinBox::setValue(uint32_t val) {
|
||||
if (m_value != val) {
|
||||
m_value = val;
|
||||
emit valueChanged(m_value);
|
||||
}
|
||||
this->updateEdit();
|
||||
}
|
||||
|
||||
uint32_t UIntSpinBox::valueFromText(const QString &text) const {
|
||||
return this->stripped(text).toUInt(nullptr, m_displayIntegerBase);
|
||||
}
|
||||
|
||||
QString UIntSpinBox::textFromValue(uint32_t val) const {
|
||||
if (m_hasPadding)
|
||||
return m_prefix + QString("%1").arg(val, m_numChars, m_displayIntegerBase, QChar('0')).toUpper();
|
||||
|
||||
return m_prefix + QString::number(val, m_displayIntegerBase).toUpper();
|
||||
}
|
||||
|
||||
void UIntSpinBox::setMinimum(uint32_t min) {
|
||||
this->setRange(min, qMax(min, m_maximum));
|
||||
}
|
||||
|
||||
void UIntSpinBox::setMaximum(uint32_t max) {
|
||||
this->setRange(qMin(m_minimum, max), max);
|
||||
}
|
||||
|
||||
void UIntSpinBox::setRange(uint32_t min, uint32_t max) {
|
||||
max = qMax(min, max);
|
||||
|
||||
if (m_maximum == max && m_minimum == min)
|
||||
return;
|
||||
|
||||
if (m_maximum != max) {
|
||||
// Update number of characters for padding
|
||||
m_numChars = 0;
|
||||
for (uint32_t i = max; i != 0; i /= m_displayIntegerBase)
|
||||
m_numChars++;
|
||||
}
|
||||
|
||||
m_minimum = min;
|
||||
m_maximum = max;
|
||||
|
||||
if (m_value < min)
|
||||
m_value %= min;
|
||||
else if (m_value > max)
|
||||
m_value %= max;
|
||||
this->updateEdit();
|
||||
}
|
||||
|
||||
void UIntSpinBox::setPrefix(const QString &prefix) {
|
||||
if (m_prefix != prefix) {
|
||||
m_prefix = prefix;
|
||||
this->updateEdit();
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::setDisplayIntegerBase(int base) {
|
||||
if (base < 2 || base > 36)
|
||||
base = 10;
|
||||
if (m_displayIntegerBase != base) {
|
||||
m_displayIntegerBase = base;
|
||||
this->updateEdit();
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::setHasPadding(bool enabled) {
|
||||
if (m_hasPadding != enabled) {
|
||||
m_hasPadding = enabled;
|
||||
this->updateEdit();
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::updateEdit() {
|
||||
const QString text = this->textFromValue(m_value);
|
||||
if (text != this->lineEdit()->text()) {
|
||||
this->lineEdit()->setText(text);
|
||||
emit textChanged(this->lineEdit()->text());
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::onEditFinished() {
|
||||
int pos = this->lineEdit()->cursorPosition();
|
||||
QString input = this->lineEdit()->text();
|
||||
|
||||
auto state = this->validate(input, pos);
|
||||
if (state == QValidator::Acceptable) {
|
||||
// Valid input
|
||||
m_value = this->valueFromText(input);
|
||||
} else if (state == QValidator::Intermediate) {
|
||||
// User has deleted all the number text.
|
||||
// If they did this by selecting all text and then hitting delete
|
||||
// make sure to put the cursor back in front of the prefix.
|
||||
m_value = m_minimum;
|
||||
this->lineEdit()->setCursorPosition(m_prefix.length());
|
||||
}
|
||||
}
|
||||
|
||||
void UIntSpinBox::stepBy(int steps)
|
||||
{
|
||||
auto new_value = m_value;
|
||||
if (steps < 0 && new_value + steps > new_value) {
|
||||
new_value = 0;
|
||||
} else if (steps > 0 && new_value + steps < new_value) {
|
||||
new_value = UINT_MAX;
|
||||
} else {
|
||||
new_value += steps;
|
||||
}
|
||||
this->setValue(new_value);
|
||||
}
|
||||
|
||||
QString UIntSpinBox::stripped(QString input) const {
|
||||
if (m_prefix.length() && input.startsWith(m_prefix))
|
||||
input.remove(0, m_prefix.length());
|
||||
return input.trimmed();
|
||||
}
|
||||
|
||||
QValidator::State UIntSpinBox::validate(QString &input, int &pos) const {
|
||||
QString copy(input);
|
||||
input = m_prefix;
|
||||
|
||||
// Adjust for prefix
|
||||
copy = this->stripped(copy);
|
||||
if (copy.isEmpty())
|
||||
return QValidator::Intermediate;
|
||||
|
||||
// Editing the prefix (if not deleting all text) is not allowed
|
||||
if (pos < m_prefix.length())
|
||||
return QValidator::Invalid;
|
||||
|
||||
bool ok;
|
||||
uint32_t num = copy.toUInt(&ok, m_displayIntegerBase);
|
||||
if (!ok || num < m_minimum || num > m_maximum)
|
||||
return QValidator::Invalid;
|
||||
|
||||
input += copy.toUpper();
|
||||
return QValidator::Acceptable;
|
||||
}
|
||||
|
||||
QAbstractSpinBox::StepEnabled UIntSpinBox::stepEnabled() const {
|
||||
QAbstractSpinBox::StepEnabled flags = QAbstractSpinBox::StepNone;
|
||||
if (m_value < m_maximum)
|
||||
flags |= QAbstractSpinBox::StepUpEnabled;
|
||||
if (m_value > m_minimum)
|
||||
flags |= QAbstractSpinBox::StepDownEnabled;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
void UIntSpinBox::wheelEvent(QWheelEvent *event) {
|
||||
// Only allow scrolling to modify contents when it explicitly has focus.
|
||||
if (hasFocus())
|
||||
QAbstractSpinBox::wheelEvent(event);
|
||||
}
|
||||
|
||||
void UIntSpinBox::focusOutEvent(QFocusEvent *event) {
|
||||
this->updateEdit();
|
||||
QAbstractSpinBox::focusOutEvent(event);
|
||||
}
|
Loading…
Reference in a new issue