]> git.mar77i.info Git - bigintmandel/commitdiff
improve settingswidget, allow changing all fields
authormar77i <mar77i@protonmail.ch>
Sun, 5 May 2024 14:11:26 +0000 (16:11 +0200)
committermar77i <mar77i@protonmail.ch>
Sun, 5 May 2024 19:02:03 +0000 (21:02 +0200)
CMakeLists.txt
bigintmandelwidget.cpp
bigintmandelwidget.h
mandel.cpp
mandel.h
menubar.cpp
menubar.h
settingswidget.cpp
settingswidget.h
uint64validator.cpp [new file with mode: 0644]
uint64validator.h [new file with mode: 0644]

index 7d4929c759c97f35e2c80bf859ec827d8490ef91..1c44864c79432a2d5731233aeead5934dd57a9a8 100644 (file)
@@ -24,6 +24,8 @@ set(PROJECT_SOURCES
         menubar.h
         mandellabel.cpp
         mandellabel.h
+        uint64validator.cpp
+        uint64validator.h
 )
 
 if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
index 6280e547ebca5b874317721eb09c3946e4efd5a7..06d8b4f38921bceef862a6aa27898641aa4992fb 100644 (file)
@@ -51,8 +51,7 @@ BigintMandelWidget::BigintMandelWidget(QWidget *parent)
 }
 
 BigintMandelWidget::~BigintMandelWidget() {
-    fw->cancel();
-    fw->waitForFinished();
+    stop();
 }
 
 void BigintMandelWidget::finished_cell(int num) {
@@ -65,7 +64,6 @@ void BigintMandelWidget::finished_cell(int num) {
 
 void BigintMandelWidget::finished() {
     mandel_label->set_draw_progress(-1);
-    settings_widget->set_finished(true);
     update();
 }
 
@@ -89,6 +87,7 @@ void BigintMandelWidget::update_status_bar() {
 }
 
 void BigintMandelWidget::start() {
+    stop();
     fw->setFuture(
         QtConcurrent::mapped(
             settings.get_cells(),
@@ -99,6 +98,11 @@ void BigintMandelWidget::start() {
     update();
 }
 
+void BigintMandelWidget::stop() {
+    fw->cancel();
+    fw->waitForFinished();
+}
+
 void BigintMandelWidget::mousePressEvent(QMouseEvent *event) {
     QSize size(settings.get_params().get_size());
     QPoint pos(event->pos());
@@ -163,19 +167,25 @@ void BigintMandelWidget::export_img() {
 }
 
 void BigintMandelWidget::exec_settings_widget() {
-    settings_widget->update_fields(settings.get_params(), fw->isFinished());
+    settings_widget->update_fields(settings.get_params());
     settings_widget->exec();
 }
 
 void BigintMandelWidget::reset() {
-    fw->cancel();
-    fw->waitForFinished();
     settings.reset(128, get_ideal_size());
     start();
 }
 
 void BigintMandelWidget::settings_widget_accepted() {
-    settings.set_max_iter(settings_widget->get_max_iter().toULongLong());
+    if (settings_widget->clear_image()) {
+        settings.set_params(settings_widget->get_params());
+        settings.clear();
+    } else {
+        quint64 max_iter = settings_widget->get_max_iter();
+        if (max_iter <= settings.get_max_iter())
+            return;
+        settings.set_max_iter(max_iter);
+    }
     start();
 }
 
@@ -230,3 +240,12 @@ void BigintMandelWidget::update() {
     update_status_bar();
     QWidget::update();
 }
+
+void BigintMandelWidget::resize_to_window() {
+    QSize size = get_ideal_size();
+    if (size == settings.get_params().get_size())
+        return;
+    settings.set_size(size);
+    settings.clear();
+    start();
+}
index 8f2042593b23a39bccd4c9edc03a236636a89d8a..db2f2d637b494b5f89a1c6c93129aeffeebd8314 100644 (file)
@@ -29,6 +29,7 @@ class BigintMandelWidget : public QWidget {
 
     void update_status_bar();
     void start();
+    void stop();
     inline const QSize get_ideal_size() const {
         return scroll_area->size().shrunkBy(scroll_area->contentsMargins());
     }
@@ -56,6 +57,7 @@ public Q_SLOTS:
     void load_data();
     void save_data();
     void update();
+    void resize_to_window();
 };
 
 #endif // BIGINTMANDELWIDGET_H
index a6946a25549e9bcca113dde431eb0c389b5de7c9..74a1a110a8a7bb4ffcadb7626b0550f5476fd0c5 100644 (file)
@@ -8,25 +8,37 @@
 #include "colors.h"
 #include "mandel.h"
 
+static inline const MpzPoint calculate_offset(
+    const MpzPoint center_f, const QSize size
+) {
+    return MpzPoint(
+        center_f.get_x() - size.width() / 2,
+        center_f.get_y() + size.height() / 2
+    );
+}
+
 MandelParams::MandelParams() {}
 MandelParams::MandelParams(
-    size_t max_iter,
+    quint64 max_iter,
     QSize size,
     MpzPoint center_f,
     mpz_class one
 ) : max_iter(max_iter),
     size(size),
     center_f(center_f),
-    one(one),
-    left(center_f.get_x() - size.width() / 2.),
-    top(center_f.get_y() + size.height() / 2.) {}
+    offset(calculate_offset(center_f, size)),
+    one(one) {}
 MandelParams::MandelParams(const MandelParams &other)
 :   max_iter(other.max_iter),
     size(other.size),
     center_f(other.center_f),
-    one(other.one),
-    left(center_f.get_x() - size.width() / 2.),
-    top(center_f.get_y() + size.height() / 2.) {}
+    offset(calculate_offset(other.center_f, other.size)),
+    one(other.one) {}
+
+void MandelParams::set_size(const QSize &size) {
+    this->size = size;
+    this->offset = calculate_offset(center_f, size);
+}
 
 MandelParams MandelParams::from_json(const QJsonObject &json) {
     return MandelParams(
@@ -80,11 +92,17 @@ static inline QImage *setup_image(QImage *img, const QSize &size) {
     return img;
 }
 
-MandelSettings::MandelSettings(size_t max_iter, const QSize size)
+MandelSettings::MandelSettings(quint64 max_iter, const QSize size)
 :   img(nullptr) {
     reset(max_iter, size);
 }
 
+void MandelSettings::clear() {
+    img = setup_image(img, params.get_size());
+    img->fill(Qt::GlobalColor::black);
+    setup_cells(cells, &params);
+}
+
 void MandelSettings::zoom(const QSize size, int zoom_factor, const QPoint pos) {
     MpzPoint p = cells[img->width() * pos.y() + pos.x()].get_rpos0();
     params = MandelParams(
@@ -93,9 +111,7 @@ void MandelSettings::zoom(const QSize size, int zoom_factor, const QPoint pos) {
         MpzPoint(p.get_x() * zoom_factor, p.get_y() * zoom_factor),
         params.get_one() * zoom_factor
     );
-    img = setup_image(img, size);
-    img->fill(Qt::GlobalColor::black);
-    setup_cells(cells, &params);
+    clear();
 }
 
 void MandelSettings::finished_cell(int num, const MandelResultCell &result) {
@@ -112,7 +128,7 @@ void MandelSettings::save_img(QString file_name) {
         img->save(file_name);
 }
 
-void MandelSettings::reset(size_t max_iter, const QSize size) {
+void MandelSettings::reset(quint64 max_iter, const QSize size) {
     mpz_class one;
     MpzPoint center_f;
     int ione = std::min(size.width(), size.height()) / 3;
@@ -125,8 +141,7 @@ void MandelSettings::reset(size_t max_iter, const QSize size) {
     one = mpz_class(ione + 1);
     center_f = MpzPoint(one * -3 / 4, 0);
     params = MandelParams(max_iter, size, center_f, one);
-    img = setup_image(img, size);
-    setup_cells(cells, &params);
+    clear();
 }
 
 void MandelSettings::from_json(const QJsonObject &json) {
@@ -159,8 +174,9 @@ MandelCell::MandelCell(const MandelCell &cell)
     rpos0(cell.rpos0) {}
 
 void MandelCell::setup(const QPoint pos) {
+    const MpzPoint &offset = params->get_offset();
     this->pos = pos;
-    rpos0 = MpzPoint(params->get_left() + pos.x(), params->get_top() - pos.y());
+    rpos0 = MpzPoint(offset.get_x() + pos.x(), offset.get_y() - pos.y());
     result = MandelResultCell();
 }
 
@@ -168,7 +184,7 @@ MandelResultCell MandelCell::iterate() const {
     MpzPoint rpos = result.get_rpos(), sq;
     mpz_class one = params->get_one(), four = one * one * 4;
     mpz_class sqx, sqy;
-    size_t iter = result.get_iter();
+    quint64 iter = result.get_iter();
     for (; iter < params->get_max_iter(); iter++) {
         sqx = rpos.get_x() * rpos.get_x();
         sqy = rpos.get_y() * rpos.get_y();
index 3d31b7d9354cdaa3015ded510daa0a2ea5770a55..3522cc6a72faaa9288ed953d408a58e288ed061d 100644 (file)
--- a/mandel.h
+++ b/mandel.h
@@ -21,39 +21,43 @@ public:
 };
 
 class MandelResultCell {
-    size_t iter;
+    quint64 iter;
     MpzPoint rpos;
 public:
     MandelResultCell() : iter(0) {}
-    explicit MandelResultCell(size_t iter, MpzPoint rpos)
+    explicit MandelResultCell(quint64 iter, MpzPoint rpos)
     : iter(iter), rpos(rpos) {}
-    inline const size_t get_iter() const { return iter; }
+    inline const quint64 get_iter() const { return iter; }
     inline const MpzPoint get_rpos() const { return rpos; }
 };
 
 class MandelParams {
 private:
-    size_t max_iter;
+    quint64 max_iter;
     QSize size;
-    MpzPoint center_f;
-    mpz_class left, top, one;
+    MpzPoint center_f, offset;
+    mpz_class one;
 
 public:
     // required by the MandelSettings constructor
     MandelParams();
     explicit MandelParams(
-        size_t max_iter,
+        quint64 max_iter,
         QSize size,
         MpzPoint center_f,
         mpz_class one
     );
     MandelParams(const MandelParams &other);
-    inline const size_t get_max_iter() const { return max_iter; }
-    inline void set_max_iter(const size_t max_iter) { this->max_iter = max_iter; }
+    inline const quint64 get_max_iter() const { return max_iter; }
+    inline void set_max_iter(const quint64 max_iter) {
+        this->max_iter = max_iter;
+    }
     inline const QSize get_size() const { return size; }
+    void set_size(const QSize &size);
     inline const MpzPoint get_center_f() const { return center_f; }
-    inline const mpz_class get_left() const { return left; }
-    inline const mpz_class get_top() const { return top; }
+    inline const MpzPoint &get_offset() const {
+        return offset;
+    }
     inline const mpz_class get_one() const { return one; }
 
     static MandelParams from_json(const QJsonObject &json);
@@ -68,16 +72,26 @@ class MandelSettings {
     QVector<MandelCell> cells;
 
 public:
-    explicit MandelSettings(size_t max_iter, QSize size);
+    explicit MandelSettings(quint64 max_iter, QSize size);
+    void clear();
     inline QPixmap get_pixmap() const { return QPixmap::fromImage(*img); }
     inline const QVector<MandelCell> get_cells() const { return cells; }
     inline const MandelParams &get_params() const { return params; }
-    inline void set_max_iter(size_t max_iter) { params.set_max_iter(max_iter); }
+    inline void set_params(const MandelParams &params) {
+        this->params = params;
+    }
+    inline quint64 get_max_iter() const { return params.get_max_iter(); }
+    inline void set_max_iter(quint64 max_iter) {
+        params.set_max_iter(max_iter);
+    }
+    inline void set_size(const QSize &size) {
+        params.set_size(size);
+    }
 
     void zoom(const QSize size, int zoom_factor, const QPoint pos);
     void finished_cell(int num, const MandelResultCell &cell);
     void save_img(QString file_name);
-    void reset(size_t max_iter, QSize size);
+    void reset(quint64 max_iter, QSize size);
     void from_json(const QJsonObject &json);
     QJsonObject to_json();
 };
@@ -102,7 +116,7 @@ public:
     inline const QPoint get_pos() const { return pos; }
     inline const MpzPoint get_rpos0() const { return rpos0; }
     inline const QColor get_color() const {
-        size_t iter = result.get_iter();
+        quint64 iter = result.get_iter();
         return iter < params->get_max_iter()
             ? colors[iter % colors.size()]
             : Qt::GlobalColor::black;
index 39683c36db6ee72edb859ee2fe35d7c2cabfb8c4..f43fd13baea8175d77a833bc27f3d6f3e9e22e6c 100644 (file)
@@ -46,6 +46,12 @@ MenuBar::MenuBar(BigintMandelWidget *parent)
     reset_action = calc_menu->addAction(
         "&Reset", no_key, parent, &BigintMandelWidget::reset
     );
+    resize_action = calc_menu->addAction(
+        "Resi&ze to Window",
+        no_key,
+        parent,
+        &BigintMandelWidget::resize_to_window
+    );
     settings_action = calc_menu->addAction(
         "&Settings", no_key, parent, &BigintMandelWidget::exec_settings_widget
     );
index e1b0702206f20a5e3827171599e8e61bf726838d..e1377f6ef4d26f82993ee17b611a266f57a129a1 100644 (file)
--- a/menubar.h
+++ b/menubar.h
@@ -15,7 +15,7 @@ class MenuBar : public QMenuBar {
     QAction *load_action, *save_action;
     QAction *export_action, *exit_action;
     QMenu *calc_menu;
-    QAction *reset_action, *settings_action;
+    QAction *reset_action, *resize_action, *settings_action;
     QMenu *zoom_menu;
     QAction *no_action, *two_action, *four_action;
     QAction *eight_action, *sixteen_action;
index f87086fdca48bbc23784338b031a3c1848ca3743..dce5e990d785dca06f81eb39d5fdef29ddf3ad49 100644 (file)
 
 // settingswidget.cpp
 
+#include <gmpxx.h>
 #include <QPushButton>
 #include <QLabel>
-#include <QLayout>
 #include <QIntValidator>
 
 #include "settingswidget.h"
+#include "uint64validator.h"
+
+SettingsWidgetFieldBase::SettingsWidgetFieldBase(
+    const QString &label, QWidget *parent
+) : edit(new QLineEdit("", parent)),
+    label(label)
+{
+    edit->setMinimumWidth(256);
+}
+
+void SettingsWidgetFieldBase::add_to_layout(QGridLayout *grid_layout) {
+    int row_count = grid_layout->rowCount();
+    grid_layout->addWidget(
+        new QLabel(label, edit->parentWidget()), row_count, 0
+    );
+    grid_layout->addWidget(edit, row_count, 1);
+}
+
+void SettingsWidgetFieldBase::add_on_editing_finished(
+    SettingsWidget *settings_widget, const char *method_name
+) {
+    QObject::connect(edit, SIGNAL(editingFinished()), settings_widget, method_name);
+}
+
+void SettingsWidgetFieldBase::reset() {
+    edit->setText(orig_value);
+}
+
+SettingsWidgetField<quint64>::SettingsWidgetField(
+    const QString &label, QWidget *parent
+) : SettingsWidgetFieldBase(label, parent) {
+    edit->setValidator(new UInt64Validator(edit));
+}
+
+void SettingsWidgetField<quint64>::set_value(quint64 value) {
+    orig_value = QString::number(value);
+    reset();
+}
+
+SettingsWidgetField<int>::SettingsWidgetField(
+    const QString &label, QWidget *parent
+) : SettingsWidgetFieldBase(label, parent) {
+    edit->setValidator(new QIntValidator(edit));
+}
+
+void SettingsWidgetField<int>::set_value(int value) {
+    orig_value = QString::number(value);
+    reset();
+}
+
+SettingsWidgetField<mpz_class>::SettingsWidgetField(
+    const QString &label, QWidget *parent
+) : SettingsWidgetFieldBase(label, parent) {
+    static QRegularExpression number_re("^\\d+$");
+    edit->setValidator(new QRegularExpressionValidator(number_re));
+}
+
+void SettingsWidgetField<mpz_class>::set_value(const mpz_class &value) {
+    orig_value = QString::QString::fromStdString(value.get_str());
+    reset();
+}
 
 SettingsWidget::SettingsWidget(QWidget *parent)
 :   QDialog(parent),
-    max_iter(new QLineEdit("", this)),
-    width(new QLineEdit("", this)),
-    height(new QLineEdit("", this)),
-    center_f_x(new QLineEdit("", this)),
-    center_f_y(new QLineEdit("", this)),
-    one(new QLineEdit("", this))
+    max_iter("max_iter", this),
+    width("width", this),
+    height("height", this),
+    center_f_x("x", this),
+    center_f_y("y", this),
+    one("one", this),
+    clear_image_checkbox(new QCheckBox("Clear image", this))
 {
-    static QStringList labels = {
-        "max_iter", "width", "height", "x", "y", "one"
-    };
-    static QList<QLineEdit*> line_edits = {
-        max_iter, width, height, center_f_x, center_f_y, one
-    };
-    QPushButton *apply_button = new QPushButton("Apply");
+    QPushButton *accept_button = new QPushButton("&Accept");
+    QPushButton *reject_button = new QPushButton("&Reject");
     QGridLayout *grid_layout = new QGridLayout(this);
+    QBoxLayout *button_row = new QBoxLayout(QBoxLayout::Direction::RightToLeft);
     int i;
-    setLayout(grid_layout);
-    for (i = 0; i < line_edits.size(); i++) {
-        grid_layout->addWidget(new QLabel(labels[i], this), i, 0);
-        if (i == 0)
-            line_edits[i]->setValidator(
-                new QIntValidator(0, INT_MAX, line_edits[i])
-            );
-        else
-            line_edits[i]->setReadOnly(true);
-        line_edits[i]->setMinimumWidth(256);
-        grid_layout->addWidget(line_edits[i], i, 1);
-    }
+    max_iter.add_to_layout(grid_layout);
+    width.add_to_layout(grid_layout);
+    width.add_on_editing_finished(this, SLOT(update_form()));
+    height.add_to_layout(grid_layout);
+    height.add_on_editing_finished(this, SLOT(update_form()));
+    center_f_x.add_to_layout(grid_layout);
+    center_f_x.add_on_editing_finished(this, SLOT(update_form()));
+    center_f_y.add_to_layout(grid_layout);
+    center_f_y.add_on_editing_finished(this, SLOT(update_form()));
+    one.add_to_layout(grid_layout);
+    one.add_on_editing_finished(this, SLOT(update_form()));
+    connect(
+        clear_image_checkbox,
+        &QCheckBox::checkStateChanged,
+        this,
+        &SettingsWidget::clear_image_checkbox_changed
+    );
+    grid_layout->addWidget(clear_image_checkbox, grid_layout->rowCount(), 1);
     connect(
-        apply_button,
+        accept_button,
         &QPushButton::clicked,
         this,
-        &SettingsWidget::apply
+        &SettingsWidget::accept
     );
-    grid_layout->addWidget(apply_button, i, 1);
+    button_row->addWidget(accept_button);
+    connect(
+        reject_button,
+        &QPushButton::clicked,
+        this,
+        &SettingsWidget::reject
+    );
+    button_row->addWidget(reject_button);
+    grid_layout->addLayout(button_row, grid_layout->rowCount(), 0, 1, 2);
 }
 
-void SettingsWidget::update_fields(
-    const MandelParams &params, const bool is_finished
-) {
+void SettingsWidget::update_fields(const MandelParams &params) {
     QSize size = params.get_size();
     MpzPoint center_f = params.get_center_f();
-    max_iter->setText(QString::number(params.get_max_iter()));
-    width->setText(QString::number(size.width()));
-    height->setText(QString::number(size.height()));
-    center_f_x->setText(QString::fromStdString(center_f.get_x().get_str()));
-    center_f_y->setText(QString::fromStdString(center_f.get_y().get_str()));
-    one->setText(QString::fromStdString(params.get_one().get_str()));
-    set_finished(is_finished);
+    max_iter.set_value(params.get_max_iter());
+    width.set_value(size.width());
+    height.set_value(size.height());
+    center_f_x.set_value(center_f.get_x());
+    center_f_y.set_value(center_f.get_y());
+    one.set_value(params.get_one());
+    clear_image_checkbox->setChecked(false);
+}
+
+void SettingsWidget::update_form() {
+    if (
+        width.is_edited()
+        || height.is_edited()
+        || center_f_x.is_edited()
+        || center_f_y.is_edited()
+        || one.is_edited()
+    )
+        // todo: add a messagebox that we do this
+        clear_image_checkbox->setChecked(true);
 }
 
-void SettingsWidget::apply() {
-    if (is_finished)
-        accept();
-    else
-        reject();
+void SettingsWidget::clear_image_checkbox_changed(Qt::CheckState state) {
+    if (state == Qt::Unchecked) {
+        // todo: add a messagebox if we should do this
+        width.reset();
+        height.reset();
+        center_f_x.reset();
+        center_f_y.reset();
+        one.reset();
+    }
 }
index 0fab8c12f1643c0f6e978d5551d202a68f67b5cd..450c12ce7b7c9ce5a6dfe85cb969e440b1c1eb0e 100644 (file)
@@ -4,30 +4,96 @@
 #ifndef SETTINGSWIDGET_H
 #define SETTINGSWIDGET_H
 
+#include <QCheckBox>
 #include <QDialog>
 #include <QLabel>
+#include <QLayout>
 #include <QLineEdit>
 
 #include "mandel.h"
 
+class SettingsWidget;
+
+class SettingsWidgetFieldBase {
+protected:
+    QLineEdit *edit;
+    const QString label;
+    QString orig_value;
+
+public:
+    SettingsWidgetFieldBase(const QString &label, QWidget *parent = nullptr);
+    inline bool is_edited() const { return edit->text() != orig_value; }
+    void add_to_layout(QGridLayout *grid_layout);
+    void add_on_editing_finished(
+        SettingsWidget *settings_widget, const char *method_name
+    );
+    void reset();
+};
+
+template <typename T> class SettingsWidgetField;
+template <>
+class SettingsWidgetField<quint64> : public SettingsWidgetFieldBase {
+public:
+    explicit SettingsWidgetField(
+        const QString &label, QWidget *parent = nullptr
+    );
+    void set_value(quint64 value);
+    inline const quint64 get_value() const {
+        return edit->text().toULongLong();
+    }
+};
+
+template <>
+class SettingsWidgetField<int> : public SettingsWidgetFieldBase {
+public:
+    explicit SettingsWidgetField(
+        const QString &label, QWidget *parent = nullptr
+    );
+    void set_value(int value);
+    inline const int get_value() const {
+        return edit->text().toInt();
+    }
+};
+
+template <>
+class SettingsWidgetField<mpz_class> : public SettingsWidgetFieldBase {
+public:
+    explicit SettingsWidgetField(
+        const QString &label, QWidget *parent = nullptr
+    );
+    void set_value(const mpz_class &value);
+    inline const mpz_class get_value() const {
+        return mpz_class(edit->text().toStdString());
+    }
+};
+
 class SettingsWidget : public QDialog {
     Q_OBJECT
 
-    QLineEdit *max_iter, *width, *height, *center_f_x, *center_f_y, *one;
-    bool is_finished;
+    SettingsWidgetField<quint64> max_iter;
+    SettingsWidgetField<int> width, height;
+    SettingsWidgetField<mpz_class> center_f_x, center_f_y, one;
+    QCheckBox *clear_image_checkbox;
 
 public:
     explicit SettingsWidget(QWidget *parent = nullptr);
-    void update_fields(const MandelParams &params, const bool is_finished);
-    inline const QString get_max_iter() const { return max_iter->text(); };
-
-    inline void set_finished(const bool is_finished) {
-        this->is_finished = is_finished;
-        max_iter->setReadOnly(!is_finished);
+    void update_fields(const MandelParams &params);
+    inline const quint64 get_max_iter() const { return max_iter.get_value(); }
+    inline const MandelParams get_params() {
+        return MandelParams(
+            max_iter.get_value(),
+            QSize(width.get_value(), height.get_value()),
+            MpzPoint(center_f_x.get_value(), center_f_y.get_value()),
+            one.get_value()
+        );
+    }
+    inline bool clear_image() const {
+        return clear_image_checkbox->isChecked();
     }
 
 public Q_SLOTS:
-    void apply();
+    void update_form();
+    void clear_image_checkbox_changed(Qt::CheckState state);
 };
 
 #endif // SETTINGSWIDGET_H
diff --git a/uint64validator.cpp b/uint64validator.cpp
new file mode 100644 (file)
index 0000000..6b78d2c
--- /dev/null
@@ -0,0 +1,26 @@
+
+// uint64validator.cpp
+
+#include "uint64validator.h"
+
+UInt64Validator::UInt64Validator(
+    quint64 bottom, quint64 top, QObject *parent
+) : QValidator(parent), bottom(bottom), top(top) {}
+UInt64Validator::UInt64Validator(QObject *parent)
+:   UInt64Validator(
+        std::numeric_limits<quint64>::min(),
+        std::numeric_limits<quint64>::max(),
+        parent
+    ) {}
+
+QValidator::State UInt64Validator::validate(QString &input, int&) const {
+    bool ok = false;
+    quint64 num = input.toULongLong(&ok);
+    if (!ok)
+        return QValidator::Invalid;
+    if (num < bottom)
+        return QValidator::Intermediate;
+    if (num > top)
+        return QValidator::Invalid;
+    return QValidator::Acceptable;
+}
diff --git a/uint64validator.h b/uint64validator.h
new file mode 100644 (file)
index 0000000..1e20666
--- /dev/null
@@ -0,0 +1,35 @@
+
+// uint64validator.h
+
+#ifndef UINT64VALIDATOR_H
+#define UINT64VALIDATOR_H
+
+#include <QValidator>
+
+class UInt64Validator : public QValidator {
+    Q_OBJECT
+    quint64 bottom;
+    quint64 top;
+
+public:
+    UInt64Validator(quint64 bottom, quint64 top, QObject *parent = nullptr);
+    UInt64Validator(QObject *parent = nullptr);
+    inline quint64 get_bottom() const { return bottom; }
+    inline quint64 get_top() const { return top; }
+    inline void set_bottom(quint64 bottom) {
+        set_range(bottom, get_top());
+    }
+    inline void set_top(quint64 top) {
+        set_range(get_bottom(), top);
+    }
+    void set_range(quint64 bottom, quint64 top) {
+        if(this->bottom == bottom && this->top == top)
+            return;
+        this->bottom = bottom;
+        this->top = top;
+        emit changed();
+    }
+    QValidator::State validate(QString &input, int&) const override;
+};
+
+#endif // UINT64VALIDATOR_H