diff options
| author | Sebastiano Tronto <sebastiano@tronto.net> | 2025-04-25 16:18:26 +0200 |
|---|---|---|
| committer | Sebastiano Tronto <sebastiano@tronto.net> | 2025-04-25 16:19:02 +0200 |
| commit | 97b5778172572504c8dfbe173b18281e786270c0 (patch) | |
| tree | e775099577e9ef61465717ae453d71d8ff44559f /qt-widgets | |
| download | qt-experiments-97b5778172572504c8dfbe173b18281e786270c0.tar.gz qt-experiments-97b5778172572504c8dfbe173b18281e786270c0.zip | |
Initial commit
Diffstat (limited to 'qt-widgets')
| -rw-r--r-- | qt-widgets/README.md | 8 | ||||
| -rw-r--r-- | qt-widgets/adapter.cpp | 46 | ||||
| -rw-r--r-- | qt-widgets/adapter.h | 35 | ||||
| -rw-r--r-- | qt-widgets/main.cpp | 11 | ||||
| -rw-r--r-- | qt-widgets/nissyqt.pro | 18 | ||||
| -rw-r--r-- | qt-widgets/nissywindow.cpp | 35 | ||||
| -rw-r--r-- | qt-widgets/nissywindow.h | 36 | ||||
| -rw-r--r-- | qt-widgets/nissywindow.ui | 33 | ||||
| -rw-r--r-- | qt-widgets/solvercfgwidget.cpp | 90 | ||||
| -rw-r--r-- | qt-widgets/solvercfgwidget.h | 43 | ||||
| -rw-r--r-- | qt-widgets/solvercfgwidget.ui | 81 |
11 files changed, 436 insertions, 0 deletions
diff --git a/qt-widgets/README.md b/qt-widgets/README.md new file mode 100644 index 0000000..f19c5b4 --- /dev/null +++ b/qt-widgets/README.md | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # QT-widgets implementation of a nissy UI | ||
| 2 | |||
| 3 | This is a UI for nissy written using | ||
| 4 | [QT Widgets](https://doc.qt.io/qt-6/qtwidgets-index.html) | ||
| 5 | |||
| 6 | You can't actually build or run this as is, because it relies on | ||
| 7 | an older version of the C++ bindings for nissy. I'll fix it | ||
| 8 | at some point, maybe. | ||
diff --git a/qt-widgets/adapter.cpp b/qt-widgets/adapter.cpp new file mode 100644 index 0000000..c15181c --- /dev/null +++ b/qt-widgets/adapter.cpp | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | #include "adapter.h" | ||
| 2 | |||
| 3 | #include <array> | ||
| 4 | #include <filesystem> | ||
| 5 | #include <fstream> | ||
| 6 | #include <sstream> | ||
| 7 | #include <QDebug> | ||
| 8 | |||
| 9 | NissyAdapter::NissyAdapter() | ||
| 10 | { | ||
| 11 | auto sid = nissy::solverinfo(defaultOptimalSolver); | ||
| 12 | auto [sz, dataid] = std::get<std::pair<size_t, std::string>>(sid); | ||
| 13 | const std::string path = "../nissy-core/tables/" + dataid; | ||
| 14 | |||
| 15 | std::filesystem::path filePath(path); | ||
| 16 | if (std::filesystem::file_size(filePath) != static_cast<uintmax_t>(sz)) | ||
| 17 | qDebug("Error in file size!"); // TODO: better handle error, gentable | ||
| 18 | |||
| 19 | optimalSolverData.value.resize(sz); | ||
| 20 | std::ifstream ifs(path, std::ios::binary); | ||
| 21 | ifs.read(reinterpret_cast<char *>(optimalSolverData.value.data()), sz); | ||
| 22 | ifs.close(); | ||
| 23 | } | ||
| 24 | |||
| 25 | NissyAdapter::~NissyAdapter() {} | ||
| 26 | |||
| 27 | void NissyAdapter::solve(SolverConfiguration cfg) | ||
| 28 | { | ||
| 29 | auto se = nissy::solve(cfg.cube, defaultOptimalSolver, | ||
| 30 | nissy::nissflag::NORMAL, cfg.minmoves, cfg.maxmoves, | ||
| 31 | cfg.maxsolutions, cfg.optimal, cfg.threads, optimalSolverData); | ||
| 32 | if (std::holds_alternative<nissy::error_t>(se)) { | ||
| 33 | auto code = std::get<nissy::error_t>(se).value; | ||
| 34 | emit solveDone(QString("Error " + code)); | ||
| 35 | return; | ||
| 36 | } | ||
| 37 | |||
| 38 | auto [sols, stats] = std::get<nissy::solve_result_t>(se); | ||
| 39 | if (sols.size() == 0) { | ||
| 40 | emit solveDone(QString("No solution found")); | ||
| 41 | } else { | ||
| 42 | auto solstr = std::accumulate( | ||
| 43 | sols.begin(), sols.end(), std::string{}); | ||
| 44 | emit solveDone(QString::fromStdString(solstr)); | ||
| 45 | } | ||
| 46 | } | ||
diff --git a/qt-widgets/adapter.h b/qt-widgets/adapter.h new file mode 100644 index 0000000..9fc35fa --- /dev/null +++ b/qt-widgets/adapter.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #ifndef ADAPTER_H | ||
| 2 | #define ADAPTER_H | ||
| 3 | |||
| 4 | #include "../nissy-core/cpp/nissy.h" | ||
| 5 | |||
| 6 | #include <string> | ||
| 7 | #include <vector> | ||
| 8 | #include <QObject> | ||
| 9 | |||
| 10 | struct SolverConfiguration { | ||
| 11 | nissy::cube cube{nissy::cube::SOLVED}; | ||
| 12 | unsigned minmoves{0}; | ||
| 13 | unsigned maxmoves{20}; | ||
| 14 | unsigned maxsolutions{1}; | ||
| 15 | int optimal{-1}; | ||
| 16 | int threads{8}; | ||
| 17 | }; | ||
| 18 | |||
| 19 | class NissyAdapter : public QObject { | ||
| 20 | Q_OBJECT | ||
| 21 | |||
| 22 | public: | ||
| 23 | NissyAdapter(); | ||
| 24 | ~NissyAdapter(); | ||
| 25 | void solve(SolverConfiguration); | ||
| 26 | |||
| 27 | signals: | ||
| 28 | void solveDone(QString); | ||
| 29 | |||
| 30 | private: | ||
| 31 | static constexpr nissy::solver defaultOptimalSolver{"h48h3k2"}; | ||
| 32 | nissy::solver_data_t optimalSolverData; | ||
| 33 | }; | ||
| 34 | |||
| 35 | #endif | ||
diff --git a/qt-widgets/main.cpp b/qt-widgets/main.cpp new file mode 100644 index 0000000..5addb96 --- /dev/null +++ b/qt-widgets/main.cpp | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #include "nissywindow.h" | ||
| 2 | |||
| 3 | #include <QApplication> | ||
| 4 | |||
| 5 | int main(int argc, char *argv[]) | ||
| 6 | { | ||
| 7 | QApplication a(argc, argv); | ||
| 8 | NissyWindow w; | ||
| 9 | w.show(); | ||
| 10 | return a.exec(); | ||
| 11 | } | ||
diff --git a/qt-widgets/nissyqt.pro b/qt-widgets/nissyqt.pro new file mode 100644 index 0000000..43f198c --- /dev/null +++ b/qt-widgets/nissyqt.pro | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # Source files | ||
| 2 | SOURCES = main.cpp adapter.cpp nissywindow.cpp solvercfgwidget.cpp | ||
| 3 | HEADERS = adapter.h nissywindow.h solvercfgwidget.h | ||
| 4 | FORMS = nissywindow.ui solvercfgwidget.ui | ||
| 5 | |||
| 6 | # Add nissy backend headers and code | ||
| 7 | SOURCES += ../nissy-core/cpp/nissy.cpp | ||
| 8 | HEADERS += ../nissy-core/cpp/nissy.h | ||
| 9 | |||
| 10 | # Compiler configuration | ||
| 11 | CONFIG += qt debug c++20 | ||
| 12 | LIBS += ../nissy-core/nissy.o | ||
| 13 | QT += widgets concurrent | ||
| 14 | |||
| 15 | # Destination folders for generated files | ||
| 16 | MOC_DIR = generated_files | ||
| 17 | UI_DIR = generated_files | ||
| 18 | OBJECTS_DIR = build | ||
diff --git a/qt-widgets/nissywindow.cpp b/qt-widgets/nissywindow.cpp new file mode 100644 index 0000000..98cfc86 --- /dev/null +++ b/qt-widgets/nissywindow.cpp | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #include "nissywindow.h" | ||
| 2 | #include "./ui_nissywindow.h" | ||
| 3 | #include "nissy/cpp/nissy.h" | ||
| 4 | |||
| 5 | #include <QtConcurrent> | ||
| 6 | |||
| 7 | NissyWindow::NissyWindow(QWidget *parent) | ||
| 8 | : QMainWindow(parent), ui(new Ui::NissyWindow) | ||
| 9 | { | ||
| 10 | ui->setupUi(this); | ||
| 11 | |||
| 12 | QObject::connect(ui->solverCfgWidget, | ||
| 13 | SIGNAL(solveRequest(const SolverConfiguration&)), this, | ||
| 14 | SLOT(startSolve(const SolverConfiguration&))); | ||
| 15 | QObject::connect(&adapter, SIGNAL(solveDone(QString)), this, | ||
| 16 | SLOT(showSolutions(QString))); | ||
| 17 | } | ||
| 18 | |||
| 19 | NissyWindow::~NissyWindow() | ||
| 20 | { | ||
| 21 | delete ui; | ||
| 22 | } | ||
| 23 | |||
| 24 | void NissyWindow::startSolve(const SolverConfiguration& config) | ||
| 25 | { | ||
| 26 | ui->solverCfgWidget->lockSubmit(); | ||
| 27 | ui->solutionsLabel->setText("Loading solutions..."); | ||
| 28 | auto _ = QtConcurrent::run(&NissyAdapter::solve, &adapter, config); | ||
| 29 | } | ||
| 30 | |||
| 31 | void NissyWindow::showSolutions(QString solutions) { | ||
| 32 | QString header = QString("Solution(s) found:\n"); | ||
| 33 | ui->solutionsLabel->setText(header + solutions); | ||
| 34 | ui->solverCfgWidget->unlockSubmit(); | ||
| 35 | } | ||
diff --git a/qt-widgets/nissywindow.h b/qt-widgets/nissywindow.h new file mode 100644 index 0000000..c2e7b44 --- /dev/null +++ b/qt-widgets/nissywindow.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | #ifndef NISSYWINDOW_H | ||
| 2 | #define NISSYWINDOW_H | ||
| 3 | |||
| 4 | #include "adapter.h" | ||
| 5 | #include "solvercfgwidget.h" | ||
| 6 | |||
| 7 | #include <QMainWindow> | ||
| 8 | #include <QLabel> | ||
| 9 | #include <QLineEdit> | ||
| 10 | #include <QPushButton> | ||
| 11 | #include <QString> | ||
| 12 | |||
| 13 | QT_BEGIN_NAMESPACE | ||
| 14 | namespace Ui { | ||
| 15 | class NissyWindow; | ||
| 16 | } | ||
| 17 | QT_END_NAMESPACE | ||
| 18 | |||
| 19 | class NissyWindow : public QMainWindow | ||
| 20 | { | ||
| 21 | Q_OBJECT | ||
| 22 | |||
| 23 | public: | ||
| 24 | NissyWindow(QWidget *parent = nullptr); | ||
| 25 | ~NissyWindow(); | ||
| 26 | |||
| 27 | private slots: | ||
| 28 | void showSolutions(QString); | ||
| 29 | void startSolve(const SolverConfiguration&); | ||
| 30 | |||
| 31 | private: | ||
| 32 | Ui::NissyWindow *ui; | ||
| 33 | NissyAdapter adapter; | ||
| 34 | }; | ||
| 35 | |||
| 36 | #endif | ||
diff --git a/qt-widgets/nissywindow.ui b/qt-widgets/nissywindow.ui new file mode 100644 index 0000000..6b11229 --- /dev/null +++ b/qt-widgets/nissywindow.ui | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>NissyWindow</class> | ||
| 4 | <widget class="QMainWindow" name="nissyWindow"> | ||
| 5 | |||
| 6 | <property name="geometry"> | ||
| 7 | <rect> | ||
| 8 | <x>0</x> | ||
| 9 | <y>0</y> | ||
| 10 | <width>800</width> | ||
| 11 | <height>600</height> | ||
| 12 | </rect> | ||
| 13 | </property> | ||
| 14 | <property name="windowTitle"> | ||
| 15 | <string>Nissy 3.0 - preview</string> | ||
| 16 | </property> | ||
| 17 | |||
| 18 | <widget class="QWidget" name="centralWidget"> | ||
| 19 | |||
| 20 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 21 | <item> | ||
| 22 | <widget class="SolverCfgWidget" name="solverCfgWidget" /> | ||
| 23 | </item> | ||
| 24 | |||
| 25 | <item> | ||
| 26 | <widget class="QLabel" name="solutionsLabel" /> | ||
| 27 | </item> | ||
| 28 | </layout> | ||
| 29 | |||
| 30 | </widget> | ||
| 31 | |||
| 32 | </widget> | ||
| 33 | </ui> | ||
diff --git a/qt-widgets/solvercfgwidget.cpp b/qt-widgets/solvercfgwidget.cpp new file mode 100644 index 0000000..8027fe5 --- /dev/null +++ b/qt-widgets/solvercfgwidget.cpp | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | #include "solvercfgwidget.h" | ||
| 2 | #include "./ui_solvercfgwidget.h" | ||
| 3 | |||
| 4 | enum class ScrambleState { empty, valid, invalid }; | ||
| 5 | |||
| 6 | ScrambleState getScrambleState(const std::string&); | ||
| 7 | |||
| 8 | SolverCfgWidget::SolverCfgWidget(QWidget *parent) | ||
| 9 | : QWidget(parent), ui(new Ui::SolverCfgWidget) | ||
| 10 | { | ||
| 11 | ui->setupUi(this); | ||
| 12 | onScrambleChanged(ui->scrambleEditor->text()); | ||
| 13 | |||
| 14 | nmovesValidator = new QIntValidator(0, 20, this); | ||
| 15 | ui->minMovesEditor->setValidator(nmovesValidator); | ||
| 16 | ui->maxMovesEditor->setValidator(nmovesValidator); | ||
| 17 | |||
| 18 | QObject::connect(ui->scrambleEditor, | ||
| 19 | SIGNAL(textChanged(const QString&)), this, | ||
| 20 | SLOT(onScrambleChanged(const QString&))); | ||
| 21 | QObject::connect(ui->scrambleEditor, SIGNAL(returnPressed()), | ||
| 22 | this, SLOT(onScrambleSubmitted())); | ||
| 23 | QObject::connect(ui->solveButton, SIGNAL(clicked()), | ||
| 24 | this, SLOT(onScrambleSubmitted())); | ||
| 25 | } | ||
| 26 | |||
| 27 | SolverCfgWidget::~SolverCfgWidget() | ||
| 28 | { | ||
| 29 | delete nmovesValidator; | ||
| 30 | delete ui; | ||
| 31 | } | ||
| 32 | |||
| 33 | void SolverCfgWidget::lockSubmit() | ||
| 34 | { | ||
| 35 | submitLocked = true; | ||
| 36 | onScrambleChanged(ui->scrambleEditor->text()); | ||
| 37 | } | ||
| 38 | |||
| 39 | void SolverCfgWidget::unlockSubmit() | ||
| 40 | { | ||
| 41 | submitLocked = false; | ||
| 42 | onScrambleChanged(ui->scrambleEditor->text()); | ||
| 43 | } | ||
| 44 | |||
| 45 | void SolverCfgWidget::onScrambleChanged(const QString& text) | ||
| 46 | { | ||
| 47 | auto scrambleState = getScrambleState(text.toStdString()); | ||
| 48 | switch (scrambleState) { | ||
| 49 | case ScrambleState::empty: | ||
| 50 | ui->solveButton->setEnabled(false); | ||
| 51 | ui->scrambleEditor->setStyleSheet(""); | ||
| 52 | break; | ||
| 53 | case ScrambleState::valid: | ||
| 54 | ui->solveButton->setEnabled(!submitLocked); | ||
| 55 | ui->scrambleEditor->setStyleSheet(""); | ||
| 56 | break; | ||
| 57 | case ScrambleState::invalid: | ||
| 58 | ui->solveButton->setEnabled(false); | ||
| 59 | ui->scrambleEditor->setStyleSheet("border : 2px solid red"); | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | void SolverCfgWidget::onScrambleSubmitted() | ||
| 65 | { | ||
| 66 | std::string scramble = ui->scrambleEditor->text().toStdString(); | ||
| 67 | auto state = getScrambleState(scramble); | ||
| 68 | if (state != ScrambleState::valid) | ||
| 69 | return; | ||
| 70 | |||
| 71 | SolverConfiguration sc { | ||
| 72 | .cube = std::get<nissy::cube_t>(nissy::applymoves( | ||
| 73 | nissy::cube::SOLVED, nissy::moves_t{scramble})), | ||
| 74 | .minmoves = ui->minMovesEditor->text().toUInt(), | ||
| 75 | .maxmoves = ui->maxMovesEditor->text().toUInt(), | ||
| 76 | }; | ||
| 77 | emit solveRequest(sc); | ||
| 78 | } | ||
| 79 | |||
| 80 | ScrambleState getScrambleState(const std::string& s) | ||
| 81 | { | ||
| 82 | if (std::all_of(s.begin(), s.end(), | ||
| 83 | [](std::string::value_type c){ return std::isspace(c); })) | ||
| 84 | return ScrambleState::empty; | ||
| 85 | |||
| 86 | auto c = nissy::applymoves(nissy::cube::SOLVED, nissy::moves_t{s}); | ||
| 87 | return std::holds_alternative<nissy::error_t>(c) ? | ||
| 88 | ScrambleState::invalid : ScrambleState::valid; | ||
| 89 | } | ||
| 90 | |||
diff --git a/qt-widgets/solvercfgwidget.h b/qt-widgets/solvercfgwidget.h new file mode 100644 index 0000000..b6494c8 --- /dev/null +++ b/qt-widgets/solvercfgwidget.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | #ifndef SOLVERCFGWIDGET_H | ||
| 2 | #define SOLVERCFGWIDGET_H | ||
| 3 | |||
| 4 | #include "adapter.h" | ||
| 5 | |||
| 6 | #include <QWidget> | ||
| 7 | #include <QLineEdit> | ||
| 8 | #include <QPushButton> | ||
| 9 | #include <QString> | ||
| 10 | #include <QIntValidator> | ||
| 11 | |||
| 12 | QT_BEGIN_NAMESPACE | ||
| 13 | namespace Ui { | ||
| 14 | class SolverCfgWidget; | ||
| 15 | } | ||
| 16 | QT_END_NAMESPACE | ||
| 17 | |||
| 18 | class SolverCfgWidget : public QWidget | ||
| 19 | { | ||
| 20 | Q_OBJECT | ||
| 21 | |||
| 22 | public: | ||
| 23 | SolverCfgWidget(QWidget *parent); | ||
| 24 | ~SolverCfgWidget(); | ||
| 25 | |||
| 26 | void lockSubmit(); | ||
| 27 | void unlockSubmit(); | ||
| 28 | |||
| 29 | signals: | ||
| 30 | void solveRequest(const SolverConfiguration&); | ||
| 31 | |||
| 32 | private slots: | ||
| 33 | void onScrambleChanged(const QString&); | ||
| 34 | void onScrambleSubmitted(); | ||
| 35 | |||
| 36 | private: | ||
| 37 | bool submitLocked; | ||
| 38 | QIntValidator *nmovesValidator; | ||
| 39 | |||
| 40 | Ui::SolverCfgWidget *ui; | ||
| 41 | }; | ||
| 42 | |||
| 43 | #endif | ||
diff --git a/qt-widgets/solvercfgwidget.ui b/qt-widgets/solvercfgwidget.ui new file mode 100644 index 0000000..19ff082 --- /dev/null +++ b/qt-widgets/solvercfgwidget.ui | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>SolverCfgWidget</class> | ||
| 4 | <widget class="SolverCfgWidget" name="solverCfgWidget"> | ||
| 5 | |||
| 6 | <layout class="QVBoxLayout"> | ||
| 7 | |||
| 8 | <item> | ||
| 9 | <layout class="QHBoxLayout"> | ||
| 10 | <item> | ||
| 11 | <widget class="QLineEdit" name="scrambleEditor"> | ||
| 12 | <property name="placeholderText"> | ||
| 13 | <string>Enter your scramble here...</string> | ||
| 14 | </property> | ||
| 15 | </widget> | ||
| 16 | </item> | ||
| 17 | |||
| 18 | <item> | ||
| 19 | <widget class="QPushButton" name="solveButton"> | ||
| 20 | <property name="text"> | ||
| 21 | <string>Solve!</string> | ||
| 22 | </property> | ||
| 23 | </widget> | ||
| 24 | </item> | ||
| 25 | </layout> | ||
| 26 | </item> | ||
| 27 | |||
| 28 | <item> | ||
| 29 | <layout class="QHBoxLayout"> | ||
| 30 | <item> | ||
| 31 | <widget class="QLabel"> | ||
| 32 | <property name="text"> | ||
| 33 | <string>Min moves</string> | ||
| 34 | </property> | ||
| 35 | </widget> | ||
| 36 | </item> | ||
| 37 | |||
| 38 | <item> | ||
| 39 | <widget class="QLineEdit" name="minMovesEditor"> | ||
| 40 | <property name="maximumSize"> | ||
| 41 | <size> | ||
| 42 | <width>25</width> | ||
| 43 | <height>20</height> | ||
| 44 | </size> | ||
| 45 | </property> | ||
| 46 | |||
| 47 | <property name="text"> | ||
| 48 | <string>0</string> | ||
| 49 | </property> | ||
| 50 | </widget> | ||
| 51 | </item> | ||
| 52 | |||
| 53 | <item> | ||
| 54 | <widget class="QLabel"> | ||
| 55 | <property name="text"> | ||
| 56 | <string>Max moves</string> | ||
| 57 | </property> | ||
| 58 | </widget> | ||
| 59 | </item>> | ||
| 60 | |||
| 61 | <item> | ||
| 62 | <widget class="QLineEdit" name="maxMovesEditor"> | ||
| 63 | <property name="maximumSize"> | ||
| 64 | <size> | ||
| 65 | <width>25</width> | ||
| 66 | <height>20</height> | ||
| 67 | </size> | ||
| 68 | </property> | ||
| 69 | |||
| 70 | <property name="text"> | ||
| 71 | <string>20</string> | ||
| 72 | </property> | ||
| 73 | </widget> | ||
| 74 | </item> | ||
| 75 | </layout> | ||
| 76 | </item> | ||
| 77 | </layout> | ||
| 78 | |||
| 79 | |||
| 80 | </widget> | ||
| 81 | </ui> | ||
