diff --git a/src/include/trainWindow.h b/src/include/trainWindow.h index eddd90f..2bebad8 100644 --- a/src/include/trainWindow.h +++ b/src/include/trainWindow.h @@ -13,5 +13,9 @@ enum PracticeAlgorithm { }; void initTrainWindow(); -void setQuestions(std::vector questions, PracticeAlgorithm algorithm); +void initiatePractice( + std::vector questions, + PracticeAlgorithm algorithm, + time_t *trainedAt +); diff --git a/src/qtapp/main.cpp b/src/qtapp/main.cpp index 819281c..d90d671 100644 --- a/src/qtapp/main.cpp +++ b/src/qtapp/main.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -85,6 +86,11 @@ struct Mdem { Question *question; }; +struct MdemBuffer { + std::vector questions = std::vector(); + time_t trainedAt = 0; +}; + struct ErrorView { QWidget box; QVBoxLayout layout; @@ -109,11 +115,10 @@ QTreeView *mdemList; QLabel *deckListLabel; QVBoxLayout *hMdemScroll; QList mdems = QList(); -time_t trainedAt = 0; -std::vector questions = std::vector(); QSpacerItem *mdemSpacer; std::vector errorPool; std::vector errorViews; +MdemBuffer *mdemBuffer; // Editor Mdem* editMdem; @@ -224,12 +229,12 @@ std::string outputMdem(std::vector questions, time_t time = 0) { void makePages() { pages.clear(); - auto len = questions.size(); + auto len = mdemBuffer->questions.size(); for (int i = 0; i < (len / PER_PAGE) + 1; i++) { auto startingIndex = PER_PAGE * i ; auto amount = PER_PAGE; - if (i == questions.size() / PER_PAGE) { - amount = questions.size() % PER_PAGE; + if (i == mdemBuffer->questions.size() / PER_PAGE) { + amount = mdemBuffer->questions.size() % PER_PAGE; } pages.push_back(Page{startingIndex, startingIndex + amount}); } @@ -300,9 +305,9 @@ void SwitchPage(int pageIdx); void deleteMdem(Mdem* mdem) { if (mdem->question) { Question* deleted = nullptr; - for (int i = 0; i < questions.size(); ++i) { - if (questions[i] == mdem->question) { - questions.erase(questions.begin() + i); + for (int i = 0; i < mdemBuffer->questions.size(); ++i) { + if (mdemBuffer->questions[i] == mdem->question) { + mdemBuffer->questions.erase(mdemBuffer->questions.begin() + i); delete mdem->question; mdem->question = nullptr; break; @@ -507,7 +512,10 @@ void SwitchPage(int pageIdx) { // Handle page slice const Page& page = pages[pageIdx]; - std::vector pageSlice(questions.begin() + page.start, questions.begin() + page.end); + std::vector pageSlice( + mdemBuffer->questions.begin() + page.start, + mdemBuffer->questions.begin() + page.end + ); CreateMdems(pageSlice); } @@ -558,6 +566,23 @@ std::string getFilename(std::string path) { return matches[2].str(); } + +void updateMdemInfo(std::string filename) { + if (filename.length() > 0) { + std::stringstream ss; + ss << std::format("mdem: {}", filename); + if (mdemBuffer->trainedAt > 0) { + std::tm* tm = std::localtime(&mdemBuffer->trainedAt); + char buffer[100]; + std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H:%M", tm); + ss << std::endl << "Last practiced: " << std::string(buffer); + } + deckListLabel->setText(QString::fromStdString(ss.str())); + } else { + deckListLabel->setText(""); + } +} + void reloadMdem() { auto file = std::ifstream(currentMdem); std::string content; @@ -566,20 +591,12 @@ void reloadMdem() { for (auto mdem: mdems) { mdem->wMdem.hide(); } - for (auto question: questions) { + for (auto question: mdemBuffer->questions) { delete question; } - questions.clear(); + mdemBuffer->questions.clear(); - // Get filename. auto filename = getFilename(currentMdem); - if (filename.length() > 0) { - deckListLabel->setText( - QString::fromStdString(std::format("mdem: {}", filename)) - ); - } else { - deckListLabel->setText(""); - } if (file) { std::stringstream buffer; @@ -596,17 +613,18 @@ void reloadMdem() { if (res.error == "") { if (res.value.lastTrainedAt == 0) { - trainedAt = 0; + mdemBuffer->trainedAt = 0; } else { auto timezoneOffset = settings->value("timezone").toInt(); - trainedAt = res.value.lastTrainedAt + 3600 * timezoneOffset; + mdemBuffer->trainedAt = res.value.lastTrainedAt + 3600 * timezoneOffset; } - std::cout << std::format("Last trained at: {}", trainedAt) << std::endl; + std::cout << std::format("Last trained at: {}", mdemBuffer->trainedAt) << std::endl; - questions = res.value.questions; + mdemBuffer->questions = res.value.questions; makePages(); SwitchPage(0); } else { + mdemBuffer->trainedAt = 0; std::cout << std::format("Compilation error: {}", res.error) << std::endl; for (auto question: res.value.questions) { @@ -634,6 +652,8 @@ void reloadMdem() { } else { std::cout << std::format("Could not open the file: {}", currentPath) << std::endl; } + + updateMdemInfo(filename); } void pickDirectory(QString directory) { @@ -662,6 +682,7 @@ void pickDirectory(QString directory) { void setupEditorSave(bool checked) { auto res = transpile(editor->text().toStdString(), true); if (res.error.length() > 0) { + mdemBuffer->trainedAt = 0; for (auto question: res.value.questions) { delete question; } @@ -681,11 +702,11 @@ void setupEditorSave(bool checked) { } else if (res.value.questions.size() == 1) { auto oldQuestion = editMdem->question; editMdem->question = res.value.questions[0]; - for (int i = 0; i < questions.size(); ++i) { - if (questions[i] == oldQuestion) { - questions.erase(questions.begin() + i); - delete questions[i]; - questions[i] = editMdem->question; + for (int i = 0; i < mdemBuffer->questions.size(); ++i) { + if (mdemBuffer->questions[i] == oldQuestion) { + mdemBuffer->questions.erase(mdemBuffer->questions.begin() + i); + delete mdemBuffer->questions[i]; + mdemBuffer->questions[i] = editMdem->question; break; } } @@ -707,8 +728,8 @@ void setupEditorSave(bool checked) { "There are no questions in your input." ); } else { - questions.insert( - questions.begin(), + mdemBuffer->questions.insert( + mdemBuffer->questions.begin(), res.value.questions.begin(), res.value.questions.end() ); @@ -721,6 +742,7 @@ void setupEditorSave(bool checked) { }; int main(int argc, char *argv[]) { + mdemBuffer = new MdemBuffer; QApplication app(argc, argv); QMainWindow window; @@ -751,9 +773,6 @@ int main(int argc, char *argv[]) { auto formLayout = new QFormLayout; - /*auto notificationsCheckBox = new QCheckBox("Enable notifications");*/ - /*formLayout->addRow(notificationsCheckBox);*/ - auto characterWrap = new QSpinBox; characterWrap->setRange(50, 150); formLayout->addRow("Character wrap in code gen:", characterWrap); @@ -917,20 +936,34 @@ int main(int argc, char *argv[]) { auto cbAlgorithm = new QComboBox; practice = new QToolButton; - hTop->addWidget(add); - hTop->addWidget(save); - hTop->addWidget(load); - hTop->addWidget(cbAlgorithm); - hTop->addWidget(practice); - // Buttons + auto buttons = new QWidget(); + auto vlButtons = new QVBoxLayout(); + buttons->setLayout(vlButtons); + auto buttonsTop = new QWidget(); + auto hlButtonsTop = new QHBoxLayout(); + buttonsTop->setLayout(hlButtonsTop); + auto buttonsBottom = new QWidget(); + auto hlButtonsBottom = new QHBoxLayout(); + buttonsBottom->setLayout(hlButtonsBottom); add->setText("Add"); save->setText("Save"); load->setText("Load"); + hlButtonsTop->addWidget(add); + hlButtonsTop->addWidget(save); + hlButtonsTop->addWidget(load); cbAlgorithm->addItem("Primary", PRIMARY); cbAlgorithm->addItem("Random", RANDOM); cbAlgorithm->addItem("Spaced", SPACED); practice->setText("Practice"); + hlButtonsBottom->addWidget(cbAlgorithm); + hlButtonsBottom->addWidget(practice); + vlButtons->addWidget(buttonsTop); + vlButtons->addWidget(buttonsBottom); + hlButtonsTop->setContentsMargins(0, 0, 0, 0); + hlButtonsBottom->setContentsMargins(0, 5, 0, 0); + + hTop->addWidget(buttons); QObject::connect(add, &QToolButton::clicked, []() { editMdem = nullptr; @@ -941,7 +974,7 @@ int main(int argc, char *argv[]) { QObject::connect(save, &QToolButton::clicked, []() { auto filename = getFilename(currentMdem); std::ofstream out(currentMdem); - out << outputMdem(questions, trainedAt); + out << outputMdem(mdemBuffer->questions, mdemBuffer->trainedAt); }); QObject::connect( practice, @@ -949,9 +982,10 @@ int main(int argc, char *argv[]) { [cbAlgorithm](bool checked) { trainWindow->show(); trainWindow->resize(600, 300); - setQuestions( - questions, - static_cast(cbAlgorithm->currentData().toInt()) + initiatePractice( + mdemBuffer->questions, + static_cast(cbAlgorithm->currentData().toInt()), + &mdemBuffer->trainedAt ); } ); diff --git a/src/qtapp/trainWindow.cpp b/src/qtapp/trainWindow.cpp index 2c87617..c6cc9ef 100644 --- a/src/qtapp/trainWindow.cpp +++ b/src/qtapp/trainWindow.cpp @@ -187,6 +187,7 @@ struct GroupView { MoveListView itemList; }; +#define ITEM_POOL_CHUNK 200 // Main components QMainWindow *trainWindow; @@ -229,15 +230,17 @@ std::vector groupModels; // Questions & State std::vector trainQuestions = std::vector(); -int32_t currentQuestionIndex = -1; +int32_t currentQuestionIndex = -1; std::vector groupViews; +PracticeAlgorithm practiceAlgoritm; std::default_random_engine rng; std::vector itemPool; -PracticeAlgorithm practiceAlgoritm; - -#define ITEM_POOL_CHUNK 200 +QToolButton *btnNotRemembered; +QToolButton *btnHard; +QToolButton *btnMedium; +QToolButton *btnEasy; QStandardItem* acquireItem() { if (itemPool.size() <= 0) { @@ -343,6 +346,13 @@ void setupOrderQuestion(MultiElementQuestion *question) { orderList->update(); } btnTriggerAnswer->hide(); + + if (practiceAlgoritm == SPACED) { + btnNotRemembered->show(); + btnHard->show(); + btnMedium->show(); + btnEasy->show(); + } } ); btnTriggerAnswer->show(); @@ -482,6 +492,10 @@ void setupQuestion(Question *question) { releaseAllItems(); QObject::disconnect(btnTriggerAnswer, 0, 0, 0); if (auto *question = dynamic_cast(trainQuestions[currentQuestionIndex])) { + btnNotRemembered->hide(); + btnHard->hide(); + btnMedium->hide(); + btnEasy->hide(); switch (question->type) { case MultiElementType::Order: setupOrderQuestion(question); @@ -511,7 +525,16 @@ void updatePaginationVisibility() { } } -void setQuestions(std::vector questions, PracticeAlgorithm algorithm) { +void initiatePractice( + std::vector questions, + PracticeAlgorithm algorithm, + time_t *trainedAt +) { + auto now = std::chrono::system_clock::now(); + time_t unix_timestamp = std::chrono::duration_cast( + now.time_since_epoch() + ).count(); + trainQuestions = questions; if (questions.size() <= 0) { return; @@ -520,6 +543,11 @@ void setQuestions(std::vector questions, PracticeAlgorithm algorithm) updatePaginationVisibility(); setupQuestion(trainQuestions[currentQuestionIndex]); practiceAlgoritm = algorithm; + + btnNotRemembered->hide(); + btnHard->hide(); + btnMedium->hide(); + btnEasy->hide(); } void initTrainWindow() { @@ -613,13 +641,38 @@ void initTrainWindow() { hButtons = new QHBoxLayout(); btnPrev = new QToolButton(); leftSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum); + btnTriggerAnswer = new QToolButton(); + + btnNotRemembered = new QToolButton(); + btnHard = new QToolButton(); + btnMedium = new QToolButton(); + btnEasy = new QToolButton(); + + btnNotRemembered->setText("Not remembered"); + btnHard->setText("Hard"); + btnMedium->setText("Medium"); + btnEasy->setText("Easy"); + + btnTriggerAnswer = new QToolButton(); + rightSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum); btnNext = new QToolButton(); hButtons->addWidget(btnPrev); hButtons->addItem(leftSpacer); hButtons->addWidget(btnTriggerAnswer); + + hButtons->addWidget(btnNotRemembered); + hButtons->addWidget(btnHard); + QObject::connect(btnHard, &QToolButton::clicked, []() { + const double hourCooldown = 25.0; + auto question = trainQuestions[currentQuestionIndex]; + question->Cooldown = hourCooldown; + }); + hButtons->addWidget(btnMedium); + hButtons->addWidget(btnEasy); + hButtons->addItem(rightSpacer); hButtons->addWidget(btnNext); vButtonBox->addWidget(actionButtons); diff --git a/src/transpiler/compile_commands.json b/src/transpiler/compile_commands.json deleted file mode 100644 index bced7fb..0000000 --- a/src/transpiler/compile_commands.json +++ /dev/null @@ -1,32 +0,0 @@ -[ -{ - "directory": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/transpiler", - "command": "/usr/bin/g++ -I/home/jorenchik/Code/mdemory/src/cpp/include -std=gnu++20 -Wall -Wextra -Wpedantic -o CMakeFiles/transpiler.dir/main.cpp.o -c /home/jorenchik/Code/mdemory/src/cpp/transpiler/main.cpp", - "file": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/main.cpp", - "output": "transpiler/CMakeFiles/transpiler.dir/main.cpp.o" -}, -{ - "directory": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/transpiler", - "command": "/usr/bin/g++ -I/home/jorenchik/Code/mdemory/src/cpp/include -std=gnu++20 -Wall -Wextra -Wpedantic -o CMakeFiles/transpiler.dir/lexer.cpp.o -c /home/jorenchik/Code/mdemory/src/cpp/transpiler/lexer.cpp", - "file": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/lexer.cpp", - "output": "transpiler/CMakeFiles/transpiler.dir/lexer.cpp.o" -}, -{ - "directory": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/transpiler", - "command": "/usr/bin/g++ -I/home/jorenchik/Code/mdemory/src/cpp/include -std=gnu++20 -Wall -Wextra -Wpedantic -o CMakeFiles/transpiler.dir/parser.cpp.o -c /home/jorenchik/Code/mdemory/src/cpp/transpiler/parser.cpp", - "file": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/parser.cpp", - "output": "transpiler/CMakeFiles/transpiler.dir/parser.cpp.o" -}, -{ - "directory": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/transpiler", - "command": "/usr/bin/g++ -I/home/jorenchik/Code/mdemory/src/cpp/include -std=gnu++20 -Wall -Wextra -Wpedantic -o CMakeFiles/transpiler.dir/time.cpp.o -c /home/jorenchik/Code/mdemory/src/cpp/transpiler/time.cpp", - "file": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/time.cpp", - "output": "transpiler/CMakeFiles/transpiler.dir/time.cpp.o" -}, -{ - "directory": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/transpiler", - "command": "/usr/bin/g++ -I/home/jorenchik/Code/mdemory/src/cpp/include -std=gnu++20 -Wall -Wextra -Wpedantic -o CMakeFiles/transpiler.dir/api.cpp.o -c /home/jorenchik/Code/mdemory/src/cpp/transpiler/api.cpp", - "file": "/home/jorenchik/Code/mdemory/src/cpp/transpiler/api.cpp", - "output": "transpiler/CMakeFiles/transpiler.dir/api.cpp.o" -} -] \ No newline at end of file