mirror of
https://github.com/jorenchik/mdemory.git
synced 2026-03-22 00:26:21 +00:00
757 lines
20 KiB
C++
757 lines
20 KiB
C++
#include <QMainWindow>
|
|
#include <QWidget>
|
|
#include <QToolButton>
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <format>
|
|
#include <cstdlib>
|
|
#include <exception>
|
|
#include <format>
|
|
#include <qabstractitemview.h>
|
|
#include <qboxlayout.h>
|
|
#include <qcoreevent.h>
|
|
#include <qlabel.h>
|
|
#include <qlayoutitem.h>
|
|
#include <qlistview.h>
|
|
#include <qnamespace.h>
|
|
#include <qscrollarea.h>
|
|
#include <qstandarditemmodel.h>
|
|
#include <qstringlistmodel.h>
|
|
#include <qtoolbutton.h>
|
|
#include <Qt>
|
|
#include <random>
|
|
#include <qwidget.h>
|
|
#include <QStandardItemModel>
|
|
#include <QDropEvent>
|
|
#include <QDrag>
|
|
#include <iostream>
|
|
#include <QStyledItemDelegate>
|
|
#include <QPainter>
|
|
|
|
#include "trainWindow.h"
|
|
#include "parser.h"
|
|
|
|
enum ElementState {
|
|
NEUTRAL = 0,
|
|
CORRECT,
|
|
INCORRECT
|
|
};
|
|
|
|
const auto listStyle =
|
|
"QListView {"
|
|
" background-color: white;"
|
|
" border: 0px solid black;"
|
|
"}"
|
|
"QListView::item::first {"
|
|
" padding: 5px 0 0 0;"
|
|
"}"
|
|
"QListView::item {"
|
|
" color: black;"
|
|
" padding: 5px;"
|
|
" background-color: none;"
|
|
"}"
|
|
"QListView::item:selected {"
|
|
" background-color: transparent;"
|
|
" padding-left: 0px;"
|
|
" margin-left: 0px;"
|
|
"}"
|
|
"QListView::item:hover {"
|
|
" background-color: lightyellow;"
|
|
" padding-left: 0px;"
|
|
" margin-left: 0px;"
|
|
"}";
|
|
|
|
class CustomItemDelegate : public QStyledItemDelegate {
|
|
public:
|
|
CustomItemDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
|
|
|
|
// Override the paint method to apply custom styling
|
|
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
|
|
painter->save();
|
|
Qt::GlobalColor color;
|
|
auto colorIdx = index.data(Qt::UserRole + 1).toInt() ;
|
|
switch (colorIdx) {
|
|
case NEUTRAL:
|
|
color = Qt::lightGray;
|
|
break;
|
|
case INCORRECT:
|
|
color = Qt::red;
|
|
break;
|
|
case CORRECT:
|
|
color = Qt::green;
|
|
break;
|
|
}
|
|
painter->setOpacity(0.5);
|
|
if (color) {
|
|
painter->fillRect(option.rect, color);
|
|
}
|
|
painter->restore();
|
|
QStyledItemDelegate::paint(painter, option, index);
|
|
}
|
|
};
|
|
|
|
class MultiChoiceListView : public QListView
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit MultiChoiceListView(QWidget *parent = nullptr) : QListView(parent) {
|
|
setStyleSheet(listStyle);
|
|
setAcceptDrops(true);
|
|
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
setFocusPolicy(Qt::NoFocus);
|
|
setSelectionMode(QAbstractItemView::NoSelection);
|
|
}
|
|
};
|
|
|
|
class OrderListView : public QListView
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit OrderListView(QWidget *parent = nullptr) : QListView(parent) {
|
|
setStyleSheet(listStyle);
|
|
setAcceptDrops(true);
|
|
setFocusPolicy(Qt::NoFocus);
|
|
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
setDragDropMode(QAbstractItemView::InternalMove);
|
|
}
|
|
|
|
protected:
|
|
void dropEvent(QDropEvent *event) override
|
|
{
|
|
QModelIndex sourceIndex = currentIndex();
|
|
QModelIndex targetIndex = indexAt(event->pos());
|
|
|
|
if (sourceIndex.isValid() && targetIndex.isValid()) {
|
|
QStandardItemModel *model = qobject_cast<QStandardItemModel *>(this->model());
|
|
if (model) {
|
|
QVariant sourceData = model->data(sourceIndex);
|
|
QVariant targetData = model->data(targetIndex);
|
|
model->setData(sourceIndex, targetData);
|
|
model->setData(targetIndex, sourceData);
|
|
event->ignore();
|
|
}
|
|
} else {
|
|
event->ignore();
|
|
}
|
|
}
|
|
};
|
|
|
|
class MoveListView : public QListView
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit MoveListView(QWidget *parent = nullptr) : QListView(parent) {
|
|
setStyleSheet(listStyle);
|
|
setAcceptDrops(true);
|
|
setDragDropMode(QAbstractItemView::DragDrop);
|
|
setDefaultDropAction(Qt::MoveAction);
|
|
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
setFocusPolicy(Qt::NoFocus);
|
|
}
|
|
|
|
protected:
|
|
void dragEnterEvent(QDragEnterEvent *event) override {
|
|
event->acceptProposedAction();
|
|
}
|
|
|
|
void dragMoveEvent(QDragMoveEvent *event) override {
|
|
event->acceptProposedAction();
|
|
}
|
|
|
|
void dropEvent(QDropEvent *event) override {
|
|
QListView *source = qobject_cast<QListView *>(event->source());
|
|
QStandardItemModel *sourceModel = qobject_cast<QStandardItemModel *>(source->model());
|
|
QStandardItemModel *targetModel = qobject_cast<QStandardItemModel *>(this->model());
|
|
|
|
if (source != this) {
|
|
QList<QStandardItem *> items = sourceModel->takeRow(source->currentIndex().row());
|
|
targetModel->appendRow(items);
|
|
}
|
|
|
|
event->ignore();
|
|
}
|
|
};
|
|
|
|
#include "trainWindow.moc"
|
|
|
|
struct GroupView {
|
|
QWidget widget;
|
|
QVBoxLayout vWidget;
|
|
QLabel label;
|
|
QScrollArea itemScroll;
|
|
QStandardItemModel itemModel;
|
|
MoveListView itemList;
|
|
};
|
|
|
|
#define ITEM_POOL_CHUNK 200
|
|
|
|
// Main components
|
|
QMainWindow *trainWindow;
|
|
QWidget *trainWidget;
|
|
QVBoxLayout *vTrainWidget;
|
|
CustomItemDelegate *itemDelegate;
|
|
|
|
// Question
|
|
QWidget *questionBox;
|
|
QVBoxLayout *vQuestionBox;
|
|
QLabel *lQuestionText;
|
|
|
|
// Bottom buttons
|
|
QVBoxLayout *vButtonBox;
|
|
QWidget *actionButtons;
|
|
QHBoxLayout *hButtons;
|
|
QToolButton *btnPrev;
|
|
QSpacerItem *leftSpacer;
|
|
QToolButton *btnTriggerAnswer;
|
|
QSpacerItem *rightSpacer;
|
|
QToolButton *btnNext;
|
|
|
|
// Answer question
|
|
QLabel *answerText;
|
|
|
|
// Multi-choice question
|
|
QStandardItemModel *multiChoiceModel;
|
|
QListView *multiChoiceList;
|
|
|
|
// Order question
|
|
OrderListView *orderList;
|
|
QStandardItemModel *orderModel;
|
|
|
|
// Group question
|
|
QStandardItemModel *groupItemModel;
|
|
QListView *groupItemList;
|
|
QWidget *wGroupQuestion;
|
|
QVBoxLayout *vGroups;
|
|
std::vector<QStandardItemModel*> groupModels;
|
|
|
|
// Questions & State
|
|
std::vector<Question*> trainQuestions = std::vector<Question*>();
|
|
int32_t currentQuestionIndex = -1;
|
|
std::vector<GroupView*> groupViews;
|
|
PracticeAlgorithm practiceAlgoritm;
|
|
|
|
std::default_random_engine rng;
|
|
std::vector<QStandardItem*> itemPool;
|
|
|
|
QToolButton *btnNotRemembered;
|
|
QToolButton *btnHard;
|
|
QToolButton *btnMedium;
|
|
QToolButton *btnEasy;
|
|
|
|
QStandardItem* acquireItem() {
|
|
if (itemPool.size() <= 0) {
|
|
for (int i = 0; i < ITEM_POOL_CHUNK; ++i) {
|
|
itemPool.push_back(new QStandardItem);
|
|
}
|
|
}
|
|
auto item = itemPool.back();
|
|
itemPool.pop_back();
|
|
return item;
|
|
}
|
|
|
|
void releaseItem(QStandardItem** item) {
|
|
itemPool.push_back(*item);
|
|
(*item) = nullptr;
|
|
}
|
|
|
|
void releaseAllItems() {
|
|
std::cout <<
|
|
std::format("Starting release, currently in the pool: {}", itemPool.size()) <<
|
|
std::endl;
|
|
auto releaseAllInModel = [](QStandardItemModel *model) {
|
|
auto itemCount = model->rowCount();
|
|
for (int i = 0; i < itemCount; ++i) {
|
|
auto item = model->item(0);
|
|
model->takeRow(item->row());
|
|
releaseItem(&item);
|
|
}
|
|
};
|
|
releaseAllInModel(multiChoiceModel);
|
|
releaseAllInModel(groupItemModel);
|
|
releaseAllInModel(orderModel);
|
|
for (auto groupView: groupViews) {
|
|
releaseAllInModel(&groupView->itemModel);
|
|
}
|
|
std::cout << std::format("All items released, currently in the pool: {}", itemPool.size()) << std::endl;
|
|
}
|
|
|
|
void hideQuestionElements() {
|
|
lQuestionText->hide();
|
|
answerText->hide();
|
|
orderList->hide();
|
|
multiChoiceList->hide();
|
|
wGroupQuestion->hide();
|
|
}
|
|
|
|
void setupAnswerQuestion(MultiElementQuestion *question) {
|
|
lQuestionText->setText(
|
|
QString::fromStdString(question->QuestionText)
|
|
);
|
|
lQuestionText->show();
|
|
auto ss = std::stringstream();
|
|
for (auto answerEl: question->Choices) {
|
|
ss << std::format("- {}", answerEl.Answer) << std::endl;
|
|
}
|
|
answerText->setText(
|
|
QString::fromStdString(ss.str())
|
|
);
|
|
if (answerText->isVisible()) {
|
|
answerText->hide();
|
|
}
|
|
QObject::connect(
|
|
btnTriggerAnswer,
|
|
&QToolButton::clicked,
|
|
[](bool checked) {
|
|
answerText->show();
|
|
btnTriggerAnswer->hide();
|
|
}
|
|
);
|
|
btnTriggerAnswer->show();
|
|
}
|
|
|
|
void setupOrderQuestion(MultiElementQuestion *question) {
|
|
lQuestionText->setText(
|
|
QString::fromStdString(question->QuestionText)
|
|
);
|
|
lQuestionText->show();
|
|
orderModel->clear();
|
|
auto shuffledAnswers = question->Choices;
|
|
std::shuffle(shuffledAnswers.begin(), shuffledAnswers.end(), rng);
|
|
for (auto answerEl: shuffledAnswers) {
|
|
auto *item = acquireItem();
|
|
item->setData(NEUTRAL, Qt::UserRole + 1);
|
|
item->setData(QVariant(), Qt::CheckStateRole);
|
|
item->setCheckable(false);
|
|
item->setText(QString::fromStdString(answerEl.Answer));
|
|
orderModel->appendRow(item);
|
|
}
|
|
orderList->show();
|
|
QObject::connect(
|
|
btnTriggerAnswer,
|
|
&QToolButton::clicked,
|
|
[question](bool checked) {
|
|
for (int i = 0; i < orderModel->rowCount(); ++i) {
|
|
auto item = orderModel->item(i, 0);
|
|
auto text = item->text();
|
|
auto isCorrect = question->Choices[i].Answer.compare(text.toStdString()) == 0;
|
|
if (isCorrect) {
|
|
item->setData(CORRECT, Qt::UserRole + 1);
|
|
} else {
|
|
item->setData(INCORRECT, Qt::UserRole + 1);
|
|
}
|
|
orderList->update();
|
|
}
|
|
btnTriggerAnswer->hide();
|
|
|
|
if (practiceAlgoritm == SPACED) {
|
|
btnNotRemembered->show();
|
|
btnHard->show();
|
|
btnMedium->show();
|
|
btnEasy->show();
|
|
}
|
|
}
|
|
);
|
|
btnTriggerAnswer->show();
|
|
}
|
|
|
|
void setupMultiChoiceQuestion(MultiElementQuestion *question) {
|
|
lQuestionText->setText(
|
|
QString::fromStdString(question->QuestionText)
|
|
);
|
|
lQuestionText->show();
|
|
|
|
multiChoiceModel->clear();
|
|
for (auto answerEl: question->Choices) {
|
|
auto *item = acquireItem();
|
|
item->setText(QString::fromStdString(answerEl.Answer));
|
|
item->setData(NEUTRAL, Qt::UserRole + 1);
|
|
item->setCheckable(true);
|
|
item->setCheckState(Qt::CheckState::Unchecked);
|
|
multiChoiceModel->appendRow(item);
|
|
}
|
|
multiChoiceList->show();
|
|
|
|
QObject::connect(
|
|
btnTriggerAnswer,
|
|
&QToolButton::clicked,
|
|
[question](bool checked) {
|
|
for (int i = 0; i < multiChoiceModel->rowCount(); ++i) {
|
|
auto item = multiChoiceModel->item(i, 0);
|
|
auto isCorrect = question->Choices[i].IsCorrect == (item->checkState() == Qt::Checked);
|
|
if (isCorrect) {
|
|
item->setData(CORRECT, Qt::UserRole + 1);
|
|
} else {
|
|
item->setData(INCORRECT, Qt::UserRole + 1);
|
|
}
|
|
multiChoiceList->update();
|
|
}
|
|
btnTriggerAnswer->hide();
|
|
}
|
|
);
|
|
btnTriggerAnswer->show();
|
|
}
|
|
|
|
void setupGroupQuestion(GroupQuestion *question) {
|
|
auto groupSpacer = new QSpacerItem(
|
|
50, 50,
|
|
QSizePolicy::Minimum,
|
|
QSizePolicy::Expanding
|
|
);
|
|
|
|
lQuestionText->setText(
|
|
QString::fromStdString(question->QuestionText)
|
|
);
|
|
lQuestionText->show();
|
|
wGroupQuestion->show();
|
|
|
|
for (auto group: question->Groups) {
|
|
for (auto itemText: group.elements) {
|
|
auto *qItem = acquireItem();
|
|
qItem->setData(NEUTRAL, Qt::UserRole + 1);
|
|
qItem->setCheckable(false);
|
|
qItem->setData(QVariant(), Qt::CheckStateRole);
|
|
qItem->setText(QString::fromStdString(itemText));
|
|
groupItemModel->appendRow(qItem);
|
|
}
|
|
}
|
|
groupItemList->update();
|
|
|
|
auto makeGroup = []() {
|
|
auto groupView = new GroupView;
|
|
vGroups->addWidget(&groupView->label);
|
|
vGroups->addWidget(&groupView->itemList);
|
|
groupView->itemList.setModel(&groupView->itemModel);
|
|
groupView->itemList.setMaximumHeight(100);
|
|
groupView->itemList.setItemDelegate(itemDelegate);
|
|
groupView->widget.setLayout(&groupView->vWidget);
|
|
groupView->widget.layout()->addWidget(&groupView->label);
|
|
groupView->widget.layout()->addWidget(&groupView->itemList);
|
|
return groupView;
|
|
};
|
|
|
|
for (int k = 0; k < groupViews.size(); k++) {
|
|
groupViews[k]->widget.hide();
|
|
}
|
|
for (int i = 0; i < question->Groups.size(); i++) {
|
|
GroupView *groupView;
|
|
if (i < groupViews.size()) {
|
|
groupView = groupViews[i];
|
|
} else {
|
|
groupView = makeGroup();
|
|
groupViews.push_back(groupView);
|
|
}
|
|
groupView->label.setText(
|
|
QString::fromStdString(question->Groups[i].name)
|
|
);
|
|
vGroups->addWidget(&groupView->widget);
|
|
groupView->widget.show();
|
|
}
|
|
vGroups->addItem(groupSpacer);
|
|
|
|
QObject::connect(
|
|
btnTriggerAnswer,
|
|
&QToolButton::clicked,
|
|
[question](bool checked) {
|
|
for (int i = 0; i < groupItemModel->rowCount(); ++i) {
|
|
auto item = groupItemModel->item(i, 0);
|
|
item->setData(INCORRECT, Qt::UserRole + 1);
|
|
}
|
|
for (int i = 0; i < groupViews.size(); i++) {
|
|
auto groupView = groupViews[i];
|
|
auto group = question->Groups[i];
|
|
for (int j = 0; j < groupView->itemModel.rowCount(); ++j) {
|
|
auto item = groupView->itemModel.item(j, 0);
|
|
auto itemText = item->text().toStdString();
|
|
bool found = false;
|
|
for (int k = 0; k < group.elements.size(); ++k) {
|
|
if (group.elements[k].compare(itemText) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found) {
|
|
item->setData(CORRECT, Qt::UserRole + 1);
|
|
} else {
|
|
item->setData(INCORRECT, Qt::UserRole + 1);
|
|
}
|
|
}
|
|
groupView->itemList.update();
|
|
}
|
|
btnTriggerAnswer->hide();
|
|
}
|
|
);
|
|
btnTriggerAnswer->show();
|
|
}
|
|
|
|
void setupQuestion(Question *question) {
|
|
hideQuestionElements();
|
|
releaseAllItems();
|
|
QObject::disconnect(btnTriggerAnswer, 0, 0, 0);
|
|
if (auto *question = dynamic_cast<MultiElementQuestion*>(trainQuestions[currentQuestionIndex])) {
|
|
btnNotRemembered->hide();
|
|
btnHard->hide();
|
|
btnMedium->hide();
|
|
btnEasy->hide();
|
|
switch (question->type) {
|
|
case MultiElementType::Order:
|
|
setupOrderQuestion(question);
|
|
break;
|
|
case MultiElementType::MultiChoice:
|
|
setupMultiChoiceQuestion(question);
|
|
break;
|
|
case MultiElementType::Regular:
|
|
setupAnswerQuestion(question);
|
|
break;
|
|
}
|
|
} else if (auto *question = dynamic_cast<GroupQuestion*>(trainQuestions[currentQuestionIndex])) {
|
|
setupGroupQuestion(question);
|
|
}
|
|
}
|
|
|
|
void updatePaginationVisibility() {
|
|
if (currentQuestionIndex == 0) {
|
|
btnPrev->hide();
|
|
} else {
|
|
btnPrev->show();
|
|
}
|
|
if (currentQuestionIndex == trainQuestions.size() - 1) {
|
|
btnNext->hide();
|
|
} else {
|
|
btnNext->show();
|
|
}
|
|
}
|
|
|
|
void initiatePractice(
|
|
std::vector<Question*> questions,
|
|
PracticeAlgorithm algorithm,
|
|
time_t *trainedAt
|
|
) {
|
|
auto now = std::chrono::system_clock::now();
|
|
time_t unix_timestamp = std::chrono::duration_cast<std::chrono::seconds>(
|
|
now.time_since_epoch()
|
|
).count();
|
|
|
|
trainQuestions = questions;
|
|
if (questions.size() <= 0) {
|
|
return;
|
|
}
|
|
currentQuestionIndex = 0;
|
|
updatePaginationVisibility();
|
|
setupQuestion(trainQuestions[currentQuestionIndex]);
|
|
practiceAlgoritm = algorithm;
|
|
|
|
btnNotRemembered->hide();
|
|
btnHard->hide();
|
|
btnMedium->hide();
|
|
btnEasy->hide();
|
|
}
|
|
|
|
void initTrainWindow() {
|
|
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
|
|
rng = std::default_random_engine(seed);
|
|
|
|
trainWindow = new QMainWindow();
|
|
trainWidget = new QWidget();
|
|
vTrainWidget = new QVBoxLayout();
|
|
|
|
trainWidget->setLayout(vTrainWidget);
|
|
trainWindow->setCentralWidget(trainWidget);
|
|
trainWidget->setLayout(vTrainWidget);
|
|
vTrainWidget->setAlignment(Qt::AlignCenter);
|
|
trainWidget->setObjectName("answer-question-widget");
|
|
|
|
itemDelegate = new CustomItemDelegate(multiChoiceList);
|
|
|
|
{ // Make the question box.
|
|
questionBox = new QWidget();
|
|
vQuestionBox = new QVBoxLayout();
|
|
questionBox->setLayout(vQuestionBox);
|
|
questionBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
vQuestionBox->setAlignment(Qt::AlignCenter);
|
|
}
|
|
|
|
{ // Make question text.
|
|
lQuestionText = new QLabel();
|
|
lQuestionText->setText("What is the capital of Latvia?");
|
|
lQuestionText->setWordWrap(true);
|
|
lQuestionText->setStyleSheet(QString(
|
|
"QLabel {"
|
|
"font-size: 20px;"
|
|
"}"
|
|
));
|
|
lQuestionText->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
|
vQuestionBox->addWidget(lQuestionText);
|
|
lQuestionText->hide();
|
|
}
|
|
|
|
{ // Make multi-choice list.
|
|
multiChoiceModel = new QStandardItemModel();
|
|
multiChoiceList = new MultiChoiceListView();
|
|
multiChoiceList->setModel(multiChoiceModel);
|
|
multiChoiceList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
questionBox->layout()->addWidget(multiChoiceList);
|
|
multiChoiceList->hide();
|
|
multiChoiceList->setItemDelegate(itemDelegate);
|
|
}
|
|
|
|
{ // Make answer text.
|
|
answerText = new QLabel();
|
|
answerText->setText("- Riga\n- Second line");
|
|
answerText->setWordWrap(true);
|
|
answerText->setStyleSheet(QString(
|
|
"QLabel {"
|
|
"font-size: 15px;"
|
|
"}"
|
|
));
|
|
answerText->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
|
vQuestionBox->addWidget(answerText);
|
|
answerText->hide();
|
|
}
|
|
|
|
{ // Make order list.
|
|
orderModel = new QStandardItemModel();
|
|
orderList = new OrderListView();
|
|
orderList->setModel(orderModel);
|
|
orderList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
orderList->setItemDelegate(itemDelegate);
|
|
|
|
// Connect to handle the drop event properly
|
|
QObject::connect(
|
|
orderModel,
|
|
&QStandardItemModel::rowsMoved,
|
|
[](const QModelIndex &, int sourceRow, int, const QModelIndex &, int destinationRow) {
|
|
if (sourceRow != destinationRow - 1) {
|
|
auto *movedItem = orderModel->takeItem(sourceRow);
|
|
orderModel->insertRow(destinationRow > sourceRow ? destinationRow - 1 : destinationRow, movedItem);
|
|
}
|
|
}
|
|
);
|
|
|
|
questionBox->layout()->addWidget(orderList);
|
|
orderList->hide();
|
|
}
|
|
|
|
{ // Make buttons menu. There are left middle and right buttons.
|
|
vButtonBox = new QVBoxLayout();
|
|
actionButtons = new QWidget();
|
|
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);
|
|
actionButtons->setLayout(hButtons);
|
|
btnPrev->setText("Previous");
|
|
QObject::connect(btnPrev, &QToolButton::clicked, []() {
|
|
currentQuestionIndex--;
|
|
if (currentQuestionIndex > -1) {
|
|
setupQuestion(trainQuestions[currentQuestionIndex]);
|
|
}
|
|
updatePaginationVisibility();
|
|
});
|
|
|
|
btnTriggerAnswer->setText("Show answer");
|
|
|
|
btnNext->setText("Next");
|
|
QObject::connect(btnNext, &QToolButton::clicked, []() {
|
|
switch (practiceAlgoritm) {
|
|
case PRIMARY:
|
|
currentQuestionIndex++;
|
|
if (currentQuestionIndex < trainQuestions.size()) {
|
|
setupQuestion(trainQuestions[currentQuestionIndex]);
|
|
}
|
|
updatePaginationVisibility();
|
|
break;
|
|
case RANDOM:
|
|
// TODO: implement
|
|
break;
|
|
case SPACED:
|
|
// TODO: implement
|
|
break;
|
|
}
|
|
});
|
|
questionBox->setObjectName("question-box");
|
|
actionButtons->setStyleSheet(QString(
|
|
"QToolButton {"
|
|
"padding: 4px 5px;"
|
|
"font-size: 15px;"
|
|
"}"
|
|
));
|
|
}
|
|
|
|
{ // Make group question view.
|
|
wGroupQuestion = new QWidget();
|
|
wGroupQuestion->setStyleSheet(
|
|
"border:none;"
|
|
);
|
|
auto hGroupQuestion = new QHBoxLayout();
|
|
wGroupQuestion->setLayout(hGroupQuestion);
|
|
|
|
|
|
// Items on the left.
|
|
auto itemScroll = new QScrollArea();
|
|
auto vItems = new QVBoxLayout();
|
|
itemScroll->setLayout(vItems);
|
|
groupItemModel = new QStandardItemModel();
|
|
groupItemList = new MoveListView();
|
|
groupItemList->setItemDelegate(itemDelegate);
|
|
groupItemList->setModel(groupItemModel);
|
|
vItems->addWidget(groupItemList);
|
|
hGroupQuestion->addWidget(itemScroll);
|
|
|
|
// Items on the right.
|
|
auto groupScroll = new QScrollArea();
|
|
auto wGroupContainer = new QWidget();
|
|
vGroups = new QVBoxLayout();
|
|
groupScroll->setWidget(wGroupContainer);
|
|
groupScroll->setWidgetResizable(true);
|
|
wGroupContainer->setLayout(vGroups);
|
|
hGroupQuestion->addWidget(groupScroll);
|
|
|
|
|
|
wGroupQuestion->setStyleSheet("padding: 0; margin: 0;");
|
|
wGroupQuestion->hide();
|
|
questionBox->layout()->addWidget(wGroupQuestion);
|
|
}
|
|
|
|
vTrainWidget->addWidget(questionBox);
|
|
vTrainWidget->addWidget(actionButtons);
|
|
}
|
|
|