multiple improvements: multi-line buffering, editing interface

This commit is contained in:
jorenchik
2024-10-20 13:58:56 +03:00
parent 83e731e3de
commit b9a81b6484
4 changed files with 219 additions and 156 deletions

View File

@@ -1,16 +1,28 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <QSettings>
#include "parser.h" #include "parser.h"
void update(bool isChanged = false); void update(bool isChanged = false);
void saveMdem(); void saveMdem();
struct MdemBuffer { struct MdemBuffer {
std::vector<Question*> questions = std::vector<Question*>(); std::vector<Question*> questions = std::vector<Question*>();
time_t trainedAt = 0; time_t trainedAt = 0;
bool error = false;
bool isModified = false;
}; };
void updateMdemInfo(std::string filename = "", bool isChanged = true); void updateMdemInfo(std::string filename = "", bool isChanged = true);
extern QSettings *settings;
#define SETTING_TIMEZONE "timezone"
#define SETTING_CHARACTER_WRAP "characterWrap"
#define SETTING_NOT_REMEMBERED "notRemembered"
#define SETTING_HARD "hard"
#define SETTING_MEDIUM "medium"
#define SETTING_EASY "easy"

View File

@@ -3,6 +3,7 @@
#include <format> #include <format>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <map>
#include <qabstractbutton.h> #include <qabstractbutton.h>
#include <qboxlayout.h> #include <qboxlayout.h>
#include <qcombobox.h> #include <qcombobox.h>
@@ -24,7 +25,6 @@
#include <Qsci/qsciscintilla.h> #include <Qsci/qsciscintilla.h>
#include <Qsci/qscilexercpp.h> #include <Qsci/qscilexercpp.h>
#include <QApplication> #include <QApplication>
#include <QMainWindow> #include <QMainWindow>
#include <QVBoxLayout> #include <QVBoxLayout>
@@ -53,6 +53,7 @@
#include <QLineEdit> #include <QLineEdit>
#include <QComboBox> #include <QComboBox>
#include <QSpinBox> #include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox> #include <QCheckBox>
#include <QPushButton> #include <QPushButton>
#include <QStandardPaths> #include <QStandardPaths>
@@ -69,7 +70,6 @@
#define DISTANCE 2 #define DISTANCE 2
#define PER_PAGE 8 #define PER_PAGE 8
#define MDEM_BACKGROUND "#F7F7F7" #define MDEM_BACKGROUND "#F7F7F7"
#define WRAP_WIDTH 50
struct Mdem { struct Mdem {
QWidget wMdem; QWidget wMdem;
@@ -107,16 +107,14 @@ QFileSystemModel *model;
QTreeView *mdemList; QTreeView *mdemList;
QLabel *membaseLabel; QLabel *membaseLabel;
// @Improvement: make it into a hashmap with different buffers; std::map<std::string, MdemBuffer*> buffers;
MdemBuffer *mdemBuffer; MdemBuffer *currentMdemBuffer;
bool isBufferModified = false;
// Mdem scroll list. // Mdem scroll list.
QList<Mdem*> mdems = QList<Mdem*>(); QList<Mdem*> mdems = QList<Mdem*>();
QVBoxLayout *hMdemScroll; QVBoxLayout *hMdemScroll;
QSpacerItem *mdemSpacer; QSpacerItem *mdemSpacer;
std::vector<ErrorView*> errorPool; ErrorView* errorView;
std::vector<ErrorView*> errorViews;
// Editor // Editor
Mdem* editMdem; Mdem* editMdem;
@@ -162,8 +160,13 @@ void showBacklabels(Mdem *mdem) {
std::string outputMdem(std::vector<Question*> questions, time_t time = 0) { std::string outputMdem(std::vector<Question*> questions, time_t time = 0) {
std::stringstream ss; std::stringstream ss;
int wrap_width = 80;
if (settings->contains(SETTING_CHARACTER_WRAP)) {
wrap_width = settings->value(SETTING_CHARACTER_WRAP).toInt();
}
if (time > 0) { if (time > 0) {
auto timezoneOffset = settings->value("timezone").toInt(); auto timezoneOffset = settings->value(SETTING_TIMEZONE).toInt();
/*time = time + 3600 * timezoneOffset;*/ /*time = time + 3600 * timezoneOffset;*/
std::tm* tm = std::localtime(&time); std::tm* tm = std::localtime(&time);
char buffer[100]; char buffer[100];
@@ -182,7 +185,7 @@ std::string outputMdem(std::vector<Question*> questions, time_t time = 0) {
std::format("-{}{} >\n", std::format("-{}{} >\n",
cooldownPart, cooldownPart,
" " + escapeText(question->QuestionText)), " " + escapeText(question->QuestionText)),
WRAP_WIDTH wrap_width
); );
if (MultiElementQuestion* mw = dynamic_cast<MultiElementQuestion*>(question)) { if (MultiElementQuestion* mw = dynamic_cast<MultiElementQuestion*>(question)) {
for (auto choice: mw->Choices) { for (auto choice: mw->Choices) {
@@ -204,7 +207,7 @@ std::string outputMdem(std::vector<Question*> questions, time_t time = 0) {
orderModifier, orderModifier,
escapeText(choice.Answer) escapeText(choice.Answer)
) )
, WRAP_WIDTH); , wrap_width);
} }
} else if (GroupQuestion* gq = dynamic_cast<GroupQuestion*>(question)) { } else if (GroupQuestion* gq = dynamic_cast<GroupQuestion*>(question)) {
for (auto group: gq->Groups) { for (auto group: gq->Groups) {
@@ -213,14 +216,14 @@ std::string outputMdem(std::vector<Question*> questions, time_t time = 0) {
"\t- {}:\n", "\t- {}:\n",
escapeText(group.name) escapeText(group.name)
) )
, WRAP_WIDTH); , wrap_width);
for (auto element: group.elements) { for (auto element: group.elements) {
ss << wrapText( ss << wrapText(
std::format( std::format(
"\t\t- {}\n", "\t\t- {}\n",
escapeText(element) escapeText(element)
) )
, WRAP_WIDTH); , wrap_width);
} }
} }
} }
@@ -230,7 +233,7 @@ std::string outputMdem(std::vector<Question*> questions, time_t time = 0) {
void makePages() { void makePages() {
pages.clear(); pages.clear();
auto len = mdemBuffer->questions.size(); auto len = currentMdemBuffer->questions.size();
auto pageAmount = len / PER_PAGE; auto pageAmount = len / PER_PAGE;
if (len % PER_PAGE != 0) { if (len % PER_PAGE != 0) {
pageAmount += 1; pageAmount += 1;
@@ -238,8 +241,8 @@ void makePages() {
for (int i = 0; i < pageAmount; i++) { for (int i = 0; i < pageAmount; i++) {
auto startingIndex = PER_PAGE * i ; auto startingIndex = PER_PAGE * i ;
auto amount = PER_PAGE; auto amount = PER_PAGE;
if (i == mdemBuffer->questions.size() / PER_PAGE) { if (i == currentMdemBuffer->questions.size() / PER_PAGE) {
amount = mdemBuffer->questions.size() % PER_PAGE; amount = currentMdemBuffer->questions.size() % PER_PAGE;
} }
pages.push_back(Page{startingIndex, startingIndex + amount}); pages.push_back(Page{startingIndex, startingIndex + amount});
} }
@@ -316,15 +319,15 @@ std::string getFilename(std::string path) {
} }
void updateMdemInfo(std::string filename, bool isChanged) { void updateMdemInfo(std::string filename, bool isChanged) {
isBufferModified = isBufferModified; currentMdemBuffer->isModified = isChanged;
if (filename.length() > 0) { if (filename.length() > 0) {
std::stringstream ss; std::stringstream ss;
ss << std::format("mdem: {}", filename); ss << std::format("mdem: {}", filename);
if (isChanged) { if (isChanged) {
ss << "*"; ss << "*";
} }
if (mdemBuffer->trainedAt > 0) { if (currentMdemBuffer->trainedAt > 0) {
std::tm* tm = std::localtime(&mdemBuffer->trainedAt); std::tm* tm = std::localtime(&currentMdemBuffer->trainedAt);
char buffer[100]; char buffer[100];
std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H:%M", tm); std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H:%M", tm);
ss << std::endl << "Last practiced: " << std::string(buffer); ss << std::endl << "Last practiced: " << std::string(buffer);
@@ -388,9 +391,9 @@ Mdem* makeMdem() {
[mdem](bool checked) { [mdem](bool checked) {
if (mdem->question) { if (mdem->question) {
Question* deleted = nullptr; Question* deleted = nullptr;
for (int i = 0; i < mdemBuffer->questions.size(); ++i) { for (int i = 0; i < currentMdemBuffer->questions.size(); ++i) {
if (mdemBuffer->questions[i] == mdem->question) { if (currentMdemBuffer->questions[i] == mdem->question) {
mdemBuffer->questions.erase(mdemBuffer->questions.begin() + i); currentMdemBuffer->questions.erase(currentMdemBuffer->questions.begin() + i);
delete mdem->question; delete mdem->question;
mdem->question = nullptr; mdem->question = nullptr;
updateMdemInfo(getFilename(currentMdem), true); updateMdemInfo(getFilename(currentMdem), true);
@@ -417,7 +420,7 @@ Mdem* makeMdem() {
// Add Back Content // Add Back Content
for (int i = 0; i < 20; ++i) { for (int i = 0; i < 20; ++i) {
// @Fix: back label pooling // @Improve: back label pooling
QLabel *elBackText = new QLabel(); QLabel *elBackText = new QLabel();
mdem->hBack.addWidget(elBackText); mdem->hBack.addWidget(elBackText);
mdem->backLabels.append(elBackText); mdem->backLabels.append(elBackText);
@@ -547,7 +550,6 @@ void SwitchPage(int pageIdx) {
prevButton->hide(); prevButton->hide();
} }
// Handle page slice
Page page; Page page;
if (pages.size() <= 0) { if (pages.size() <= 0) {
page = Page(); page = Page();
@@ -562,67 +564,64 @@ void SwitchPage(int pageIdx) {
} }
std::vector<Question*> pageSlice( std::vector<Question*> pageSlice(
mdemBuffer->questions.begin() + page.start, currentMdemBuffer->questions.begin() + page.start,
mdemBuffer->questions.begin() + page.end currentMdemBuffer->questions.begin() + page.end
); );
CreateMdems(pageSlice); CreateMdems(pageSlice);
} }
void reloadMdem(std::string path) {
ErrorView *makeErrorView() { if (path == "") {
auto errorView = new ErrorView; return;
errorView->box.setObjectName("error-box"); }
errorView->box.setLayout(&errorView->layout);
errorView->box.setMinimumHeight(30); auto toRemove = std::vector<std::string>();
errorView->box.setStyleSheet( for (auto it = buffers.begin(); it != buffers.end(); ++it) {
QString( auto pair = *it;
"QWidget#error-box {" if (currentMdem.compare(path) != 0 &&
"border: 1px solid red;" (!pair.second->isModified || pair.second->error)) {
"background: %2;" toRemove.push_back(pair.first);
"}"
).arg(MDEM_BACKGROUND)
);
errorView->layout.addWidget(&errorView->label);
errorView->label.setWordWrap(true);
return errorView;
};
ErrorView* acquireError() {
if (errorPool.size() <= 0) {
for (int i = 0; i < ERROR_POOL_CHUNK; ++i) {
auto errorView = makeErrorView();
errorPool.push_back(errorView);
hMdemScroll->addWidget(&errorView->box);
errorView->box.hide();
} }
} }
auto item = errorPool.back(); for (auto key: toRemove) {
errorPool.pop_back(); buffers.erase(key);
errorViews.push_back(item); }
std::cout << std::format("Acquired, current pool size: {}\n", errorPool.size());
return item;
}
void releaseError(ErrorView** item) { MdemBuffer *buffer;
errorPool.push_back(*item); auto filename = getFilename(path);
(*item) = nullptr; if (currentMdem.compare(path) == 0) {
std::cout << std::format("Released, current pool size: {}\n", errorPool.size()); buffer = currentMdemBuffer;
} currentMdem = path;
if (buffers.contains(path)) {
buffers.erase(path);
}
} else if (!buffers.contains(path)) {
buffer = new MdemBuffer;
buffers[path] = buffer;
currentMdemBuffer = buffer;
currentMdem = path;
} else {
buffer = buffers[path];
currentMdemBuffer = buffer;
makePages();
SwitchPage(0);
updateMdemInfo(getFilename(filename), buffer->isModified);
currentMdem = path;
errorView->box.hide();
return;
}
void reloadMdem() { auto file = std::ifstream(path);
auto file = std::ifstream(currentMdem);
std::string content; std::string content;
// Reset the mdem list. // Reset the mdem list.
for (auto mdem: mdems) { for (auto mdem: mdems) {
mdem->wMdem.hide(); mdem->wMdem.hide();
} }
for (auto question: mdemBuffer->questions) { for (auto question: currentMdemBuffer->questions) {
delete question; delete question;
} }
mdemBuffer->questions.clear(); currentMdemBuffer->questions.clear();
auto filename = getFilename(currentMdem);
if (file) { if (file) {
std::stringstream buffer; std::stringstream buffer;
@@ -630,28 +629,20 @@ void reloadMdem() {
content = buffer.str(); content = buffer.str();
auto res = transpile(content, true); auto res = transpile(content, true);
while (errorViews.size() > 0) { currentMdemBuffer->error = res.error.length() > 0;
auto errorView = errorViews.back();
errorViews.pop_back();
errorView->box.hide();
releaseError(&errorView);
}
if (res.error == "") { if (res.error == "") {
if (res.value.lastTrainedAt == 0) { if (res.value.lastTrainedAt == 0) {
mdemBuffer->trainedAt = 0; currentMdemBuffer->trainedAt = 0;
} else { } else {
auto timezoneOffset = settings->value("timezone").toInt(); auto timezoneOffset = settings->value("timezone").toInt();
mdemBuffer->trainedAt = res.value.lastTrainedAt - 3600 * timezoneOffset; currentMdemBuffer->trainedAt = res.value.lastTrainedAt - 3600 * timezoneOffset;
} }
std::cout << std::format("Last trained at: {}", mdemBuffer->trainedAt) << std::endl; std::cout << std::format("Last trained at: {}", currentMdemBuffer->trainedAt) << std::endl;
mdemBuffer->questions = res.value.questions; currentMdemBuffer->questions = res.value.questions;
makePages(); errorView->box.hide();
SwitchPage(0);
updateMdemInfo(filename, false);
} else { } else {
mdemBuffer->trainedAt = 0; currentMdemBuffer->trainedAt = 0;
std::cout << std::format("Compilation error: {}", res.error) << std::endl; std::cout << std::format("Compilation error: {}", res.error) << std::endl;
for (auto question: res.value.questions) { for (auto question: res.value.questions) {
@@ -660,7 +651,6 @@ void reloadMdem() {
// Show errors. // Show errors.
hMdemScroll->removeItem(mdemSpacer); hMdemScroll->removeItem(mdemSpacer);
auto errorView = acquireError();
errorView->label.setText( errorView->label.setText(
QString::fromStdString( QString::fromStdString(
std::format( std::format(
@@ -673,9 +663,13 @@ void reloadMdem() {
) )
); );
errorView->box.show(); errorView->box.show();
hMdemScroll->addWidget(&errorView->box); hMdemScroll->addWidget(&errorView->box);
hMdemScroll->addItem(mdemSpacer); hMdemScroll->addItem(mdemSpacer);
} }
makePages();
SwitchPage(0);
updateMdemInfo(filename, false);
} else { } else {
std::cout << std::format("Could not open the file: {}", currentPath) << std::endl; std::cout << std::format("Could not open the file: {}", currentPath) << std::endl;
} }
@@ -703,14 +697,13 @@ void pickDirectory(QString directory) {
) )
)); ));
currentMdem = ""; reloadMdem("");
reloadMdem();
} }
void setupEditorSave() { void setupEditorSave() {
auto res = transpile(editor->text().toStdString(), true); auto res = transpile(editor->text().toStdString(), true);
if (res.error.length() > 0) { if (res.error.length() > 0) {
mdemBuffer->trainedAt = 0; currentMdemBuffer->trainedAt = 0;
for (auto question: res.value.questions) { for (auto question: res.value.questions) {
delete question; delete question;
} }
@@ -730,11 +723,11 @@ void setupEditorSave() {
} else if (res.value.questions.size() == 1) { } else if (res.value.questions.size() == 1) {
auto oldQuestion = editMdem->question; auto oldQuestion = editMdem->question;
editMdem->question = res.value.questions[0]; editMdem->question = res.value.questions[0];
for (int i = 0; i < mdemBuffer->questions.size(); ++i) { for (int i = 0; i < currentMdemBuffer->questions.size(); ++i) {
if (mdemBuffer->questions[i] == oldQuestion) { if (currentMdemBuffer->questions[i] == oldQuestion) {
mdemBuffer->questions.erase(mdemBuffer->questions.begin() + i); currentMdemBuffer->questions.erase(currentMdemBuffer->questions.begin() + i);
delete oldQuestion; delete oldQuestion;
mdemBuffer->questions.insert(mdemBuffer->questions.begin() + i, res.value.questions[0]); currentMdemBuffer->questions.insert(currentMdemBuffer->questions.begin() + i, res.value.questions[0]);
break; break;
} }
} }
@@ -757,8 +750,8 @@ void setupEditorSave() {
"There are no questions in your input." "There are no questions in your input."
); );
} else { } else {
mdemBuffer->questions.insert( currentMdemBuffer->questions.insert(
mdemBuffer->questions.begin(), currentMdemBuffer->questions.begin(),
res.value.questions.begin(), res.value.questions.begin(),
res.value.questions.end() res.value.questions.end()
); );
@@ -774,12 +767,12 @@ void setupEditorSave() {
void saveMdem() { void saveMdem() {
auto filename = getFilename(currentMdem); auto filename = getFilename(currentMdem);
std::ofstream out(currentMdem); std::ofstream out(currentMdem);
out << outputMdem(mdemBuffer->questions, mdemBuffer->trainedAt); out << outputMdem(currentMdemBuffer->questions, currentMdemBuffer->trainedAt);
updateMdemInfo(getFilename(currentMdem), false); updateMdemInfo(getFilename(currentMdem), false);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
mdemBuffer = new MdemBuffer;
QApplication app(argc, argv); QApplication app(argc, argv);
QMainWindow window; QMainWindow window;
@@ -825,50 +818,60 @@ int main(int argc, char *argv[]) {
auto formLayout = new QFormLayout; auto formLayout = new QFormLayout;
auto characterWrap = new QSpinBox; auto characterWrap = new QSpinBox;
characterWrap->setRange(50, 150); characterWrap->setRange(30, 150);
formLayout->addRow("Character wrap in code gen:", characterWrap); formLayout->addRow("Character wrap in code gen [30-150]:", characterWrap);
auto* timezone = new QSpinBox; auto* timezone = new QSpinBox;
timezone->setRange(-12, 12); timezone->setRange(-12, 12);
formLayout->addRow("Timezone as number (e.g. +2 as 2):", timezone); formLayout->addRow("Timezone as number (e.g. +2 as 2):", timezone);
auto* notRemembered = new QSpinBox; auto* notRemembered = new QDoubleSpinBox;
notRemembered->setRange(0, 100); notRemembered->setRange(0, 100);
formLayout->addRow("Not remembered:", notRemembered); formLayout->addRow("Not remembered:", notRemembered);
auto* hard = new QSpinBox; auto* hard = new QDoubleSpinBox;
hard->setRange(0, 100); hard->setRange(0, 100);
formLayout->addRow("Hard:", hard); formLayout->addRow("Hard:", hard);
auto* medium = new QSpinBox; auto* medium = new QDoubleSpinBox;
medium->setRange(0, 100); medium->setRange(0, 100);
formLayout->addRow("Medium:", medium); formLayout->addRow("Medium:", medium);
auto* easy = new QSpinBox; auto* easy = new QDoubleSpinBox;
easy->setRange(0, 100); easy->setRange(0, 100);
formLayout->addRow("Easy:", easy); formLayout->addRow("Easy:", easy);
auto* btnSaveSettings = new QPushButton("Save"); auto* btnSaveSettings = new QPushButton("Save");
auto* mainLayout = new QVBoxLayout; auto* mainLayout = new QVBoxLayout;
// TODO: make defaults and validate settings values // @Improve: validate setting values
characterWrap->setValue(settings->value("characterWrap").toInt()); characterWrap->setValue(settings->value(SETTING_CHARACTER_WRAP).toInt());
timezone->setValue(settings->value("timezone").toInt()); timezone->setValue(settings->value(SETTING_TIMEZONE).toInt());
notRemembered->setValue(settings->value("notRemembered").toInt()); notRemembered->setValue(settings->value(SETTING_NOT_REMEMBERED).toDouble());
hard->setValue(settings->value("hard").toInt()); hard->setValue(settings->value(SETTING_HARD).toDouble());
medium->setValue(settings->value("medium").toInt()); medium->setValue(settings->value(SETTING_MEDIUM).toDouble());
easy->setValue(settings->value("easy").toInt()); easy->setValue(settings->value(SETTING_EASY).toDouble());
auto saveSettings = [characterWrap, timezone, notRemembered, hard, medium, easy]() {
settings->setValue(SETTING_CHARACTER_WRAP, characterWrap->value());
settings->setValue(SETTING_TIMEZONE, timezone->value());
settings->setValue(SETTING_NOT_REMEMBERED, notRemembered->value());
settings->setValue(SETTING_HARD, hard->value());
settings->setValue(SETTING_MEDIUM, medium->value());
settings->setValue(SETTING_EASY, easy->value());
};
QObject::connect( QObject::connect(
btnSaveSettings, btnSaveSettings,
&QPushButton::clicked, &QPushButton::clicked,
[characterWrap, timezone, notRemembered, hard, medium, easy]() { [saveSettings]() {
settings->setValue("characterWrap", characterWrap->value()); saveSettings();
settings->setValue("timezone", timezone->value()); }
settings->setValue("notRemembered", notRemembered->value()); );
settings->setValue("hard", hard->value());
settings->setValue("medium", medium->value()); QShortcut* shortcutSave = new QShortcut(QKeySequence("Ctrl+S"), settingsWindow);
settings->setValue("easy", easy->value()); QObject::connect(shortcutSave, &QShortcut::activated, [saveSettings]() {
saveSettings();
}); });
mainLayout->addLayout(formLayout); mainLayout->addLayout(formLayout);
@@ -947,8 +950,7 @@ int main(int argc, char *argv[]) {
&QTreeView::doubleClicked, &QTreeView::doubleClicked,
[](const QModelIndex &index) { [](const QModelIndex &index) {
auto fileInfo = model->fileInfo(index); auto fileInfo = model->fileInfo(index);
currentMdem = fileInfo.filePath().toStdString(); reloadMdem(fileInfo.filePath().toStdString());
reloadMdem();
} }
); );
@@ -1020,7 +1022,9 @@ int main(int argc, char *argv[]) {
editorWindow->show(); editorWindow->show();
editor->setText(""); editor->setText("");
}); });
QObject::connect(load, &QToolButton::clicked, &reloadMdem); QObject::connect(load, &QToolButton::clicked, []() {
reloadMdem(currentMdem);
});
QObject::connect(btnSaveFile, &QToolButton::clicked, []() { QObject::connect(btnSaveFile, &QToolButton::clicked, []() {
saveMdem(); saveMdem();
}); });
@@ -1031,13 +1035,30 @@ int main(int argc, char *argv[]) {
trainWindow->show(); trainWindow->show();
trainWindow->resize(600, 300); trainWindow->resize(600, 300);
initiatePractice( initiatePractice(
mdemBuffer, currentMdemBuffer,
static_cast<PracticeAlgorithm>(cbAlgorithm->currentData().toInt()) static_cast<PracticeAlgorithm>(cbAlgorithm->currentData().toInt())
); );
} }
); );
} }
{ // Error.
errorView = new ErrorView;
errorView->box.setObjectName("error-box");
errorView->box.setLayout(&errorView->layout);
errorView->box.setMinimumHeight(30);
errorView->box.setStyleSheet(
QString(
"QWidget#error-box {"
"border: 1px solid red;"
"background: %2;"
"}"
).arg(MDEM_BACKGROUND)
);
errorView->layout.addWidget(&errorView->label);
errorView->label.setWordWrap(true);
}
{ // Mdems { // Mdems
QScrollArea *mdemScroll = new QScrollArea(); QScrollArea *mdemScroll = new QScrollArea();
QWidget *mdemContainer = new QWidget(); QWidget *mdemContainer = new QWidget();

View File

@@ -231,7 +231,7 @@ QVBoxLayout *vGroups;
std::vector<QStandardItemModel*> groupModels; std::vector<QStandardItemModel*> groupModels;
// Questions & State // Questions & State
MdemBuffer *currentBuffer; MdemBuffer *practiceBuffer;
int32_t currentQuestionIndex = -1; int32_t currentQuestionIndex = -1;
std::vector<GroupView*> groupViews; std::vector<GroupView*> groupViews;
@@ -318,15 +318,19 @@ void setupAnswerQuestion(MultiElementQuestion *question) {
if (answerText->isVisible()) { if (answerText->isVisible()) {
answerText->hide(); answerText->hide();
} }
auto checkAnswerClicked = []() {
answerText->show();
btnTriggerAnswer->hide();
if (practiceAlgoritm == SPACED) {
showFeedBackButtons();
}
};
QObject::connect( QObject::connect(
btnTriggerAnswer, btnTriggerAnswer,
&QToolButton::clicked, &QToolButton::clicked,
[](bool checked) { [checkAnswerClicked](bool checked) {
answerText->show(); checkAnswerClicked();
btnTriggerAnswer->hide();
if (practiceAlgoritm == SPACED) {
showFeedBackButtons();
}
} }
); );
btnTriggerAnswer->show(); btnTriggerAnswer->show();
@@ -533,7 +537,7 @@ void setupQuestion(Question *question) {
} }
void updatePaginationVisibility() { void updatePaginationVisibility() {
if (currentQuestionIndex == currentBuffer->questions.size() - 1) { if (currentQuestionIndex == practiceBuffer->questions.size() - 1) {
btnNext->hide(); btnNext->hide();
} else { } else {
btnNext->show(); btnNext->show();
@@ -554,27 +558,27 @@ time_t getTime() {
} }
void setupNextQuestion() { void setupNextQuestion() {
if (currentBuffer->questions.size() <= 0) { if (practiceBuffer->questions.size() <= 0) {
return; return;
} }
switch (practiceAlgoritm) { switch (practiceAlgoritm) {
case PRIMARY: { case PRIMARY: {
currentQuestionIndex++; currentQuestionIndex++;
if (currentQuestionIndex < currentBuffer->questions.size()) { if (currentQuestionIndex < practiceBuffer->questions.size()) {
setupQuestion(currentBuffer->questions[currentQuestionIndex]); setupQuestion(practiceBuffer->questions[currentQuestionIndex]);
} }
updatePaginationVisibility(); updatePaginationVisibility();
} break; } break;
case RANDOM: { case RANDOM: {
auto questionCandidates = currentBuffer->questions; auto questionCandidates = practiceBuffer->questions;
if (currentQuestionIndex > -1) { if (currentQuestionIndex > -1) {
questionCandidates.erase(questionCandidates.begin() + currentQuestionIndex); questionCandidates.erase(questionCandidates.begin() + currentQuestionIndex);
} }
if (questionCandidates.size() > 0) { if (questionCandidates.size() > 0) {
auto i = randomIndex(&questionCandidates); auto i = randomIndex(&questionCandidates);
setupQuestion(questionCandidates[i]); setupQuestion(questionCandidates[i]);
for (int k = 0; k < currentBuffer->questions.size(); ++k) { for (int k = 0; k < practiceBuffer->questions.size(); ++k) {
if (currentBuffer->questions[k] == questionCandidates[i]) { if (practiceBuffer->questions[k] == questionCandidates[i]) {
currentQuestionIndex = k; currentQuestionIndex = k;
break; break;
} }
@@ -584,27 +588,27 @@ void setupNextQuestion() {
case SPACED: { case SPACED: {
auto questionCandidates = std::vector<Question*>(); auto questionCandidates = std::vector<Question*>();
time_t time = getTime(); time_t time = getTime();
auto lastTrainedAt = currentBuffer->trainedAt; auto lastTrainedAt = practiceBuffer->trainedAt;
currentBuffer->trainedAt = time; practiceBuffer->trainedAt = time;
for (int i = 0; i < currentBuffer->questions.size(); ++i) { for (int i = 0; i < practiceBuffer->questions.size(); ++i) {
auto cooldownSeconds = currentBuffer->questions[i]->Cooldown * 3600; auto cooldownSeconds = practiceBuffer->questions[i]->Cooldown * 3600;
auto cooldownEndsAt = lastTrainedAt + cooldownSeconds; auto cooldownEndsAt = lastTrainedAt + cooldownSeconds;
if (i != currentQuestionIndex && cooldownEndsAt <= time) { if (i != currentQuestionIndex && cooldownEndsAt <= time) {
questionCandidates.push_back(currentBuffer->questions[i]); questionCandidates.push_back(practiceBuffer->questions[i]);
} }
auto newCooldown = cooldownEndsAt - time; auto newCooldown = cooldownEndsAt - time;
if (newCooldown < 0) { if (newCooldown < 0) {
newCooldown = 0; newCooldown = 0;
} }
currentBuffer->questions[i]->Cooldown = (double)newCooldown / 3600; practiceBuffer->questions[i]->Cooldown = (double)newCooldown / 3600;
} }
if (questionCandidates.size() > 0) { if (questionCandidates.size() > 0) {
auto i = randomIndex(&questionCandidates); auto i = randomIndex(&questionCandidates);
setupQuestion( setupQuestion(
questionCandidates[randomIndex(&questionCandidates)] questionCandidates[randomIndex(&questionCandidates)]
); );
for (int k = 0; k < currentBuffer->questions.size(); ++k) { for (int k = 0; k < practiceBuffer->questions.size(); ++k) {
if (currentBuffer->questions[k] == questionCandidates[i]) { if (practiceBuffer->questions[k] == questionCandidates[i]) {
currentQuestionIndex = k; currentQuestionIndex = k;
break; break;
} }
@@ -620,9 +624,9 @@ void initiatePractice(
PracticeAlgorithm algorithm PracticeAlgorithm algorithm
) { ) {
practiceAlgoritm = algorithm; practiceAlgoritm = algorithm;
currentBuffer = mdemBuffer; practiceBuffer = mdemBuffer;
if (currentBuffer->questions.size() <= 0) { if (practiceBuffer->questions.size() <= 0) {
return; return;
} }
@@ -632,10 +636,10 @@ void initiatePractice(
setupNextQuestion(); setupNextQuestion();
} }
void setCooldownHours(int cooldown) { void setCooldownHours(double cooldown) {
time_t time = getTime(); time_t time = getTime();
currentBuffer->trainedAt = time; practiceBuffer->trainedAt = time;
auto question = currentBuffer->questions[currentQuestionIndex]; auto question = practiceBuffer->questions[currentQuestionIndex];
question->Cooldown = cooldown; question->Cooldown = cooldown;
update(true); update(true);
} }
@@ -759,19 +763,39 @@ void initTrainWindow() {
btnTriggerAnswer = new QToolButton(); btnTriggerAnswer = new QToolButton();
btnNotRemembered = new QToolButton(); btnNotRemembered = new QToolButton();
QObject::connect(btnNotRemembered, &QToolButton::clicked, []() { QObject::connect(btnNotRemembered, &QToolButton::clicked, []() {
setCooldownHours(0); QString key = SETTING_NOT_REMEMBERED;
if (settings->contains(key)) {
setCooldownHours(settings->value(key).toDouble());
} else {
setCooldownHours(0);
}
}); });
btnHard = new QToolButton(); btnHard = new QToolButton();
QObject::connect(btnHard, &QToolButton::clicked, []() { QObject::connect(btnHard, &QToolButton::clicked, []() {
setCooldownHours(12); QString key = SETTING_HARD;
if (settings->contains(key)) {
setCooldownHours(settings->value(key).toDouble());
} else {
setCooldownHours(12);
}
}); });
btnMedium = new QToolButton(); btnMedium = new QToolButton();
QObject::connect(btnMedium, &QToolButton::clicked, []() { QObject::connect(btnMedium, &QToolButton::clicked, []() {
setCooldownHours(48); QString key = SETTING_MEDIUM;
if (settings->contains(key)) {
setCooldownHours(settings->value(key).toDouble());
} else {
setCooldownHours(24);
}
}); });
btnEasy = new QToolButton(); btnEasy = new QToolButton();
QObject::connect(btnEasy, &QToolButton::clicked, []() { QObject::connect(btnEasy, &QToolButton::clicked, []() {
setCooldownHours(72); QString key = SETTING_EASY;
if (settings->contains(key)) {
setCooldownHours(settings->value(key).toDouble());
} else {
setCooldownHours(48);
}
}); });
btnNotRemembered->setText("Not remembered"); btnNotRemembered->setText("Not remembered");
btnHard->setText("Hard"); btnHard->setText("Hard");
@@ -780,6 +804,11 @@ void initTrainWindow() {
btnTriggerAnswer = new QToolButton(); btnTriggerAnswer = new QToolButton();
QShortcut* shortcutShowAnswer = new QShortcut(QKeySequence("Return"), trainWindow);
QObject::connect(shortcutShowAnswer, &QShortcut::activated, []() {
btnTriggerAnswer->click();
});
rightSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum); rightSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);
btnNext = new QToolButton(); btnNext = new QToolButton();

View File

@@ -152,7 +152,8 @@ time_t parseToUTCTime(const std::string datetime, std::string format) {
return time; return time;
} }
// @Fix: Prevent duplicate group names and questions in ordered question (to
// simplify checking in practice).
Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) { Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
auto questions = std::vector<Question*>(); auto questions = std::vector<Question*>();
time_t time = 0; time_t time = 0;