#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "settings.h" #include "mdemList.h" #include "trainWindow.h" #include "api.h" #include "parser.h" #include "qscilexer.h" // Memorybase. QString currentPath = ""; std::string currentMdem = ""; QFileSystemModel *model; QTreeView *mdemList; std::map buffers; MdemBuffer *currentMdemBuffer; // Mdem list. std::vector mdems = std::vector(); QVBoxLayout *hMdemScroll; QSpacerItem *mdemSpacer; ErrorView *errorView; Pagination *pagination; int perPage; // Editor QsciScintilla *editor; QMainWindow *editorWindow; Mdem *editMdem; // Top labels. QLabel *membaseLabel; QLabel *mdemLabel; QLabel *lastPracticeLabel; QMainWindow *trainWindow; void showBacklabels(Mdem *mdem) { for (size_t i = 0; i < mdem->backLabels.size(); ++i) { if (i < mdem->labelCount) { if (!mdem->backLabels[i]->isVisible()) { mdem->backLabels[i]->show(); } } else { if (mdem->backLabels[i]->isVisible()) { mdem->backLabels[i]->hide(); } } } } std::string outputMdem(std::vector questions, time_t time = 0) { std::stringstream ss; int wrap_width = 80; if (settings->contains(SETTING_CHARACTER_WRAP)) { wrap_width = settings->value(SETTING_CHARACTER_WRAP).toInt(); } if (time > 0) { auto timezoneOffset = settings->value(SETTING_TIMEZONE).toInt(); /*time = time + 3600 * timezoneOffset;*/ std::tm* tm = std::localtime(&time); char buffer[100]; std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H\\:%M", tm); auto time = std::string(buffer); ss << time << std::endl; } for (auto question: questions) { ss << std::endl; std::string cooldownPart; if (question->cooldown != 0) { cooldownPart = std::format(" [{:.2f}]", question->cooldown); } ss << wrapText( std::format("-{}{} >\n", cooldownPart, " " + escapeText(question->questionText)), wrap_width ); if (MultiElementQuestion* mw = dynamic_cast(question)) { for (auto choice: mw->choices) { char opener; if (choice.isCorrect) { opener = '+'; } else { opener = '-'; } std::string orderModifier; if (mw->type == MultiElementType::Order) { orderModifier = "^"; } ss << wrapText( std::format( "\t{}{} {}\n", opener, orderModifier, escapeText(choice.answer) ) , wrap_width); } } else if (GroupQuestion* gq = dynamic_cast(question)) { for (auto group: gq->groups) { ss << wrapText( std::format( "\t- {}:\n", escapeText(group.name) ) , wrap_width); for (auto element: group.elements) { ss << wrapText( std::format( "\t\t- {}\n", escapeText(element) ) , wrap_width); } } } } return ss.str(); } void makePages() { pagination->pages.clear(); auto len = currentMdemBuffer->questions.size(); perPage = settings->value(SETTING_PER_PAGE).toInt(); auto pageAmount = len / perPage; if (len % perPage != 0) { pageAmount += 1; } for (size_t i = 0; i < pageAmount; i++) { size_t startingIndex = perPage * i ; size_t amount = perPage; if (i == currentMdemBuffer->questions.size() / perPage) { amount = currentMdemBuffer->questions.size() % perPage; } pagination->pages.push_back( Page{startingIndex, startingIndex + amount} ); } } void setupMdem(Mdem *mdem, Question *question) { std::stringstream ss; if (question->cooldown > 0) { ss << std::format("[{:.2f}] ", question->cooldown); } ss << question->questionText; mdem->wFrontText.setText( QString::fromStdString(ss.str()) ); if (MultiElementQuestion* mw = dynamic_cast(question)) { auto choices = mw->choices; for (size_t k = 0; k < choices.size(); ++k) { auto answer = choices[k].answer; switch (mw->type) { case MultiElementType::Order: answer = std::format("{}. {}", k + 1, answer); break; case MultiElementType::MultiChoice: if (choices[k].isCorrect) { answer = std::format("+ {}", answer); } else { answer = std::format("- {}", answer); } break; case MultiElementType::Regular: answer = std::format("- {}", answer); break; } if (k < mdem->backLabels.size()) { mdem->backLabels[k]->setText(QString::fromStdString(answer)); } else { auto label = new QLabel(); label->setText(QString::fromStdString(answer)); mdem->backLabels.push_back(label); mdem->wBack.layout()->addWidget(label); } } mdem->labelCount = choices.size(); } else if (GroupQuestion* mw = dynamic_cast(question)) { auto groups = mw->groups; std::vector elements; for (size_t k = 0; k < groups.size(); ++k) { auto answer = groups[k].name; elements.push_back(std::format("- {}:", answer)); for (size_t l = 0; l < groups[k].elements.size(); ++l) { elements.push_back(std::format(" - {}", groups[k].elements[l])); } } for (size_t k = 0; k < elements.size(); ++k) { if (k < mdem->backLabels.size()) { mdem->backLabels[k]->setText(QString::fromStdString(elements[k])); } else { auto label = new QLabel(); label->setText(QString::fromStdString(elements[k])); mdem->backLabels.push_back(label); mdem->wBack.layout()->addWidget(label); } } mdem->labelCount = elements.size(); } } void switchPage(int pageIdx); std::string getFilename(std::string path) { static const std::regex lastPathElementExp = std::regex("(.+\\/)*(.+)"); std::smatch matches; auto filenameMatched = std::regex_search(path, matches, lastPathElementExp); return matches[2].str(); } void updateMdemInfo(std::string filename, bool isChanged) { currentMdemBuffer->isModified = isChanged; if (filename.length() > 0) { std::stringstream ss; ss << std::format("mdem: {}", filename); if (isChanged) { ss << "*"; } mdemLabel->setText(QString::fromStdString(ss.str())); ss.str(""); if (currentMdemBuffer->trainedAt > 0) { std::tm* tm = std::localtime(¤tMdemBuffer->trainedAt); char buffer[100]; std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H:%M", tm); ss << "Last practiced: " << std::string(buffer); } lastPracticeLabel->setText(QString::fromStdString(ss.str())); } else { mdemLabel->setText(""); lastPracticeLabel->setText(""); } } Mdem* makeMdem() { auto mdem = new Mdem; mdem->wMdem.setLayout(&mdem->vMdem); QString id = QString("mdem_%1").arg(1); mdem->wMdem.setObjectName(id); // Front mdem->wFront.setMinimumHeight(60); mdem->wFront.setLayout(&mdem->hFront); mdem->wFront.setProperty("first", "true"); mdem->wMdem.setStyleSheet(QString( "QWidget#%1 > QWidget {" "border-right: 1px solid gray;" "border-bottom: 1px solid gray;" "border-left: 1px solid gray;" "background: %2;" "} " "QWidget#%1 > QWidget[first=\"true\"] {" "border-top: 1px solid gray;" "}" ).arg(id, MDEM_BACKGROUND)); // Add Front Content mdem->hFront.addWidget(&mdem->wFrontText); mdem->hFront.addStretch(1); mdem->editButton.setText("Edit"); QObject::connect( &mdem->editButton, &QToolButton::clicked, [mdem]() { editMdem = mdem; if (mdem->question) { editor->setText( QString::fromStdString( outputMdem(std::vector{mdem->question}) ) ); editorWindow->show(); editor->setCursorPosition(1, 2); } } ); mdem->hFront.addWidget(&mdem->editButton); mdem->deleteButton.setText("Delete"); QObject::connect( &mdem->deleteButton, &QToolButton::clicked, [mdem]() { if (mdem->question) { Question* deleted = nullptr; for (size_t i = 0; i < currentMdemBuffer->questions.size(); ++i) { if (currentMdemBuffer->questions[i] == mdem->question) { currentMdemBuffer->questions.erase(currentMdemBuffer->questions.begin() + i); delete mdem->question; mdem->question = nullptr; updateMdemInfo(getFilename(currentMdem), true); break; } } makePages(); switchPage(pagination->currentPage); } if (editMdem == mdem) { editorWindow->hide(); } } ); mdem->hFront.addWidget(&mdem->deleteButton); mdem->toggleVisibility.setText("Show"); mdem->hFront.addWidget(&mdem->toggleVisibility); // Back QVBoxLayout *hBack = new QVBoxLayout(); mdem->wBack.setLayout(&mdem->hBack); mdem->vMdem.addWidget(&mdem->wBack); // Add Back Content for (size_t i = 0; i < 20; ++i) { // @Improve: back label pooling QLabel *elBackText = new QLabel(); mdem->hBack.addWidget(elBackText); mdem->backLabels.push_back(elBackText); } mdem->vMdem.addWidget(&mdem->wFront); mdem->vMdem.addWidget(&mdem->wBack); mdem->vMdem.setContentsMargins(0, 0, 0, 0); mdem->vMdem.setSpacing(0); mdem->wBack.hide(); mdem->wMdem.hide(); // Connect button to toggle view QObject::connect(&mdem->toggleVisibility, &QToolButton::clicked, [mdem]() { if (mdem->wBack.isVisible()) { mdem->wBack.hide(); mdem->toggleVisibility.setText("Show"); } else { mdem->wBack.show(); mdem->toggleVisibility.setText("Hide"); } showBacklabels(mdem); }); return mdem; } void CreateMdems(std::vector& questions) { hMdemScroll->removeItem(mdemSpacer); for (Mdem *mdem : mdems) { if (mdem->wMdem.isVisible()) { mdem->wMdem.hide(); } } perPage = settings->value(SETTING_PER_PAGE).toInt(); if (perPage > mdems.size()) { for (size_t i = mdems.size(); i < perPage; i++) { if (i >= mdems.size()) { auto mdem = makeMdem(); mdems.push_back(mdem); hMdemScroll->addWidget(&mdem->wMdem); } } } for (size_t i = 0; i < questions.size(); ++i) { mdems[i]->question = questions[i]; setupMdem(mdems[i], questions[i]); if (!mdems[i]->wMdem.isVisible()) { mdems[i]->wMdem.show(); } } hMdemScroll->addItem(mdemSpacer); } void update(bool isChanged) { if (pagination->currentPage > -1) { switchPage(pagination->currentPage); } if (currentMdem.length() > 0) { updateMdemInfo(getFilename(currentMdem), isChanged); } } void switchPage(int pageIdx) { pagination->currentPage = pageIdx; // Hide all pagination buttons for (auto& button : pagination->paginationButtons) { button->hide(); } int l = 0; char buffer[50]; snprintf(buffer, sizeof(buffer), "Page: %d", pageIdx + 1); pagination->paginationLabel.setText(buffer); // Adjust mdem amount, hide widgets in mdems for (auto& mdem : mdems) { if (mdem->wBack.isVisible()) { mdem->wBack.hide(); mdem->toggleVisibility.setText("Show"); } } // Update pagination buttons for (int k = -DISTANCE; k <= DISTANCE; ++k) { if (pageIdx + k >= 0 && pageIdx + k < pagination->pages.size()) { auto button = pagination->paginationButtons[l]; snprintf(buffer, sizeof(buffer), "%d", pageIdx + k + 1); button->setText(buffer); if (pageIdx + k != pageIdx) { button->show(); } else { button->hide(); } ++l; } } // Handle first and last buttons if (pageIdx > 0 && pagination->pages.size() > 1) { pagination->firstButton.show(); } else { pagination->firstButton.hide(); } if (pageIdx < pagination->pages.size() - 1 && pagination->pages.size() > 1) { pagination->lastButton.show(); } else { pagination->lastButton.hide(); } // Handle next and previous buttons if (!pagination->pages.empty() && pagination->currentPage < pagination->pages.size() - 1) { pagination->nextButton.show(); } else { pagination->nextButton.hide(); } if (!pagination->pages.empty() && pagination->currentPage >= 1) { pagination->prevButton.show(); } else { pagination->prevButton.hide(); } Page page; if (pagination->pages.size() <= 0) { page = Page(); } else if (pageIdx < pagination->pages.size()){ page = pagination->pages[pageIdx]; } else { if (pageIdx - 1 < pagination->pages.size()) { page = pagination->pages[pageIdx -1]; } else { page = pagination->pages[0]; } } std::vector pageSlice( currentMdemBuffer->questions.begin() + page.start, currentMdemBuffer->questions.begin() + page.end ); CreateMdems(pageSlice); } void reloadMdem(std::string path) { if (path == "") { return; } auto toRemove = std::vector(); for (auto it = buffers.begin(); it != buffers.end(); ++it) { auto pair = *it; if (currentMdem.compare(path) != 0 && (!pair.second->isModified || pair.second->error)) { toRemove.push_back(pair.first); } } for (auto key: toRemove) { buffers.erase(key); } MdemBuffer *buffer; auto filename = getFilename(path); if (currentMdem.compare(path) == 0) { 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; } start = std::chrono::high_resolution_clock::now(); auto file = std::ifstream(path); std::string content; // Reset the mdem list. for (auto mdem: mdems) { mdem->wMdem.hide(); } for (auto question: currentMdemBuffer->questions) { delete question; } currentMdemBuffer->questions.clear(); if (file) { std::stringstream buffer; buffer << file.rdbuf(); content = buffer.str(); end = std::chrono::high_resolution_clock::now(); showTimes = settings->value(SETTING_SHOW_TIMES).toBool(); if (showTimes) { std::cout << showTime("I/O time") << std::endl; } debug = settings->value(SETTING_DEBUG).toBool(); auto res = transpile(content); currentMdemBuffer->error = res.error.length() > 0; if (res.error == "") { if (res.value.lastTrainedAt == 0) { currentMdemBuffer->trainedAt = 0; } else { auto timezoneOffset = settings->value("timezone").toInt(); currentMdemBuffer->trainedAt = res.value.lastTrainedAt - 3600 * timezoneOffset; } if (settings->value(SETTING_DEBUG).toBool()) { std::cout << std::format("Last trained at: {}", currentMdemBuffer->trainedAt) << std::endl; } currentMdemBuffer->questions = res.value.questions; errorView->box.hide(); } else { currentMdemBuffer->trainedAt = 0; std::cout << std::format("Compilation error: {}", res.error) << std::endl; for (auto question: res.value.questions) { delete question; } // Show errors. hMdemScroll->removeItem(mdemSpacer); errorView->label.setText( QString::fromStdString( std::format( "Error while transpiling {}: {} ({}:{})", filename, res.error, res.row, res.column ) ) ); errorView->box.show(); hMdemScroll->addWidget(&errorView->box); hMdemScroll->addItem(mdemSpacer); } makePages(); switchPage(0); updateMdemInfo(filename, false); } else { std::cout << std::format("Could not open the file: {}", currentPath.toStdString()) << std::endl; } for (auto mdem: mdems) { mdem->wBack.hide(); mdem->toggleVisibility.setText("Show"); } hideQuestionElements(); } void pickDirectory(QString directory) { auto path = std::filesystem::path(directory.toStdString()); if ( !std::filesystem::exists(directory.toStdString()) || !std::filesystem::is_directory(directory.toStdString()) ) { QMessageBox::information( nullptr, "Error", "The directory that is specified as the default memorybase does not exist." ); return; } currentPath = directory; // Update tree view. if (directory.length() <= 0) { membaseLabel->setText(directory); return; } mdemList->setRootIndex(model->setRootPath(directory)); std::smatch matches; // Update label. membaseLabel->setText(QString::fromStdString( std::format( "memorybase: {}", getFilename(currentPath.toStdString()) ) )); reloadMdem(""); } void setupEditorSave() { debug = settings->value(SETTING_DEBUG).toBool(); showTimes = settings->value(SETTING_SHOW_TIMES).toBool(); auto res = transpile(editor->text().toStdString()); if (res.error.length() > 0) { currentMdemBuffer->trainedAt = 0; for (auto question: res.value.questions) { delete question; } QMessageBox::information( nullptr, "Editing error", QString::fromStdString(res.error) ); } else { if (editMdem) { if (res.value.questions.size() <= 0) { QMessageBox::information( nullptr, "Editing error", "There are no questions in your input." ); } else if (res.value.questions.size() == 1) { auto oldQuestion = editMdem->question; editMdem->question = res.value.questions[0]; for (size_t i = 0; i < currentMdemBuffer->questions.size(); ++i) { if (currentMdemBuffer->questions[i] == oldQuestion) { currentMdemBuffer->questions.erase(currentMdemBuffer->questions.begin() + i); delete oldQuestion; currentMdemBuffer->questions.insert(currentMdemBuffer->questions.begin() + i, res.value.questions[0]); break; } } setupMdem(editMdem, res.value.questions[0]); showBacklabels(editMdem); editorWindow->hide(); updateMdemInfo(getFilename(currentMdem), true); } else { QMessageBox::information( nullptr, "Editing error", "There are more than question in your input. Please enter one." ); } } else { if (res.value.questions.size() <= 0) { QMessageBox::information( nullptr, "Editing error", "There are no questions in your input." ); } else { currentMdemBuffer->questions.insert( currentMdemBuffer->questions.begin(), res.value.questions.begin(), res.value.questions.end() ); makePages(); switchPage(0); editorWindow->hide(); updateMdemInfo(getFilename(currentMdem), true); } } } }; void saveMdem() { start = std::chrono::high_resolution_clock::now(); auto filename = getFilename(currentMdem); std::ofstream out(currentMdem); out << outputMdem(currentMdemBuffer->questions, currentMdemBuffer->trainedAt); updateMdemInfo(getFilename(currentMdem), false); end = std::chrono::high_resolution_clock::now(); showTimes = settings->value(SETTING_SHOW_TIMES).toBool(); if (showTimes) { std::cout << showTime("Saving time") << std::endl; } } QMainWindow *initMdemListWindow() { QMainWindow* window = new QMainWindow; pagination = new Pagination; auto *toolbar = new Toolbar(); // Setup related windows auto *settingsWindow = initSettings(); trainWindow = initTrainWindow(); { // Menu bar. QMenuBar *menuBar = new QMenuBar; QFileDialog *fileDialog = new QFileDialog; QMenu *menu = new QMenu("File"); QAction *actionOpen = menu->addAction("Open memorybase"); QObject::connect(actionOpen, &QAction::triggered, [fileDialog]() { fileDialog->setDirectory(QDir::homePath()); fileDialog->setFileMode(QFileDialog::FileMode::Directory); fileDialog->open(); QObject::disconnect(fileDialog, 0, 0, 0); fileDialog->connect( fileDialog, &QFileDialog::fileSelected, pickDirectory ); }); QAction *openSettings = menu->addAction("Settings"); QObject::connect( openSettings, &QAction::triggered, [settingsWindow]() { settingsWindow->show(); }); menuBar->addMenu(menu); window->setMenuBar(menuBar); } { // Editor. editorWindow = new QMainWindow; editorWindow->setWindowTitle("QScintilla Simple Editor"); editorWindow->resize(800, 600); QWidget *wEditor = new QWidget; QVBoxLayout *vlEditor = new QVBoxLayout; wEditor->setLayout(vlEditor); QsciLexerCPP *lexer = new QsciLexerCPP; editor = new QsciScintilla; editor->setLexer(lexer); editor->setUtf8(true); editor->setMarginWidth(0, 15); editor->zoomIn(2); QHBoxLayout *buttonLayout = new QHBoxLayout; QWidget *editorButtons = new QWidget; auto btnSaveEditor = new QPushButton; editorButtons->setLayout(buttonLayout); btnSaveEditor->setText(QString::fromStdString("Save")); QObject::connect( btnSaveEditor, &QToolButton::clicked, setupEditorSave ); buttonLayout->addWidget(btnSaveEditor); vlEditor->addWidget(editor); vlEditor->addWidget(editorButtons); QShortcut* shortcutSave = new QShortcut(QKeySequence("Ctrl+S"), editorWindow); QObject::connect(shortcutSave, &QShortcut::activated, []() { if (editor->isVisible()) { setupEditorSave(); } }); editorWindow->setCentralWidget(wEditor); } auto *wTop = new QWidget(); { // Top. // Main top layout. QHBoxLayout *hlTop = new QHBoxLayout(); wTop->setLayout(hlTop); // Labels auto *wLabels = new QWidget(); auto *vlLeftTop = new QVBoxLayout(); wLabels->setLayout(vlLeftTop); wLabels->setMinimumSize(0, 40); QString labelStyle = "font-size: 17px; font-weight: 400;"; // Memorybase label. membaseLabel = new QLabel(); membaseLabel->setStyleSheet(labelStyle); vlLeftTop->addWidget(membaseLabel); // Memorybase label. mdemLabel = new QLabel(); mdemLabel->setStyleSheet(labelStyle); vlLeftTop->addWidget(mdemLabel); lastPracticeLabel = new QLabel(); lastPracticeLabel->setStyleSheet(labelStyle); vlLeftTop->addWidget(lastPracticeLabel); // Button layout. 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); // Define buttons. QObject::connect(&toolbar->btnAdd, &QToolButton::clicked, []() { editMdem = nullptr; editorWindow->show(); editor->setText(""); }); QObject::connect(&toolbar->btnLoad, &QToolButton::clicked, []() { reloadMdem(currentMdem); }); QObject::connect(&toolbar->btnSave, &QToolButton::clicked, []() { saveMdem(); }); QObject::connect( &toolbar->btnPractice, &QToolButton::clicked, [toolbar]() { if (currentMdemBuffer) { trainWindow->show(); trainWindow->resize(600, 300); initiatePractice( currentMdemBuffer, static_cast( toolbar->cbAlgorithm.currentData().toInt() ) ); } } ); // Button content. toolbar->btnAdd.setText("Add"); toolbar->btnSave.setText("Save"); toolbar->btnLoad.setText("Load"); toolbar->btnPractice.setText("Practice"); toolbar->cbAlgorithm.addItem("Spaced", SPACED); toolbar->cbAlgorithm.addItem("Random", RANDOM); toolbar->cbAlgorithm.addItem("Primary", PRIMARY); // Add buttons. hlButtonsTop->addWidget(&toolbar->btnAdd); hlButtonsTop->addWidget(&toolbar->btnSave); hlButtonsTop->addWidget(&toolbar->btnLoad); hlButtonsBottom->addWidget(&toolbar->cbAlgorithm); hlButtonsBottom->addWidget(&toolbar->btnPractice); vlButtons->addWidget(buttonsTop); vlButtons->addWidget(buttonsBottom); // Style buttons hlButtonsTop->setContentsMargins(0, 0, 0, 0); hlButtonsBottom->setContentsMargins(0, 5, 0, 0); // Add top components. hlTop->addWidget(wLabels); hlTop->addStretch(1); hlTop->addWidget(buttons); } QWidget *leftWidget = new QWidget(); { // Left side. QVBoxLayout *leftLayout = new QVBoxLayout(); leftWidget->setLayout(leftLayout); model = new QFileSystemModel(); mdemList = new QTreeView(); mdemList->setModel(model); currentPath = settings->value(SETTING_MEMORYBASE).toString(); if (currentPath.size() > 0) { pickDirectory(currentPath); } /*leftLayout->addWidget(leftTop);*/ QObject::connect( mdemList, &QTreeView::doubleClicked, [](const QModelIndex &index) { trainWindow->close(); auto fileInfo = model->fileInfo(index); reloadMdem(fileInfo.filePath().toStdString()); } ); for (int col = 1; col < model->columnCount(); ++col) { mdemList->hideColumn(col); } model->setHeaderData(0, Qt::Horizontal, QObject::tr("Custom Name")); leftLayout->addWidget(mdemList); } auto wMain = new QWidget; auto *rightWidget = new QWidget(); auto *rightLayout = new QVBoxLayout(); { // Main layout. auto vlMain = new QVBoxLayout; wMain->setLayout(vlMain); rightWidget->setLayout(rightLayout); auto *hSplitter = new QSplitter(); hSplitter->addWidget(leftWidget); hSplitter->addWidget(rightWidget); hSplitter->setStretchFactor(1, 3); hSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); vlMain->addWidget(wTop); vlMain->addWidget(hSplitter); } { // 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 QScrollArea *mdemScroll = new QScrollArea(); QWidget *mdemContainer = new QWidget(); hMdemScroll = new QVBoxLayout(); mdemScroll->setWidget(mdemContainer); mdemScroll->setWidgetResizable(true); mdemContainer->setLayout(hMdemScroll); rightLayout->addWidget(mdemScroll); mdemSpacer = new QSpacerItem( 50, 50, QSizePolicy::Minimum, QSizePolicy::Expanding ); /*hMdemScroll->addItem(mdemSpacer);*/ } { // Pagination auto wPagination = new QWidget(); auto hPagination = new QHBoxLayout(); wPagination->setLayout(hPagination); pagination->firstButton.setText(QString::fromStdString("<<")); hPagination->addWidget(&pagination->firstButton); pagination->firstButton.hide(); QObject::connect(&pagination->firstButton, &QToolButton::clicked, []() { if (pagination->pages.size() > 0) { switchPage(0); } }); pagination->prevButton.setText(QString::fromStdString("<")); hPagination->addWidget(&pagination->prevButton); pagination->prevButton.hide(); QObject::connect(&pagination->prevButton, &QToolButton::clicked, []() { if (pagination->pages.size() > 0) { switchPage(pagination->currentPage - 1); } }); perPage = settings->value(SETTING_PER_PAGE).toInt(); for (size_t i = 0; i < perPage; i++) { auto elButton = new QToolButton(); elButton->setText(QString("%1").arg(i+1)); hPagination->addWidget(elButton); elButton->hide(); QObject::connect(elButton, &QToolButton::clicked, [elButton]() { auto pageNum = std::stoi(elButton->text().toStdString().c_str()); auto pageIdx = pageNum - 1; if (pageIdx < pagination->pages.size()) { switchPage(pageIdx); } }); pagination->paginationButtons.push_back(elButton); } pagination->nextButton.setText(QString::fromStdString(">")); hPagination->addWidget(&pagination->nextButton); pagination->nextButton.hide(); QObject::connect(&pagination->nextButton, &QToolButton::clicked, []() { if (pagination->pages.size() > 0) { switchPage(pagination->currentPage + 1); } }); pagination->lastButton.setText(QString::fromStdString(">>")); hPagination->addWidget(&pagination->lastButton); pagination->lastButton.hide(); QObject::connect(&pagination->lastButton, &QToolButton::clicked, []() { if (pagination->pages.size() > 0) { switchPage(pagination->pages.size() - 1); } }); hPagination->addStretch(1); hPagination->addWidget(&pagination->paginationLabel); rightLayout->addWidget(wPagination); } { // Setup shortcuts. auto addShortcut = [window](QString key, std::function func) { QShortcut* shortcut = new QShortcut(QKeySequence(key), window); QObject::connect(shortcut, &QShortcut::activated, [func]() { func(); }); }; addShortcut("Ctrl+,", [settingsWindow]() { settingsWindow->show(); }); addShortcut("Ctrl+S", [toolbar]() { toolbar->btnSave.click(); }); addShortcut("Ctrl+P", [toolbar]() { toolbar->btnPractice.click(); }); addShortcut("Ctrl+A", [toolbar]() { toolbar->btnAdd.click(); }); addShortcut("Ctrl+L", [toolbar]() { toolbar->btnLoad.click(); }); } window->setCentralWidget(wMain); window->show(); return window; }