diff --git a/src/cpp/include/api.h b/src/cpp/include/api.h index d5c75c1..a14c13e 100644 --- a/src/cpp/include/api.h +++ b/src/cpp/include/api.h @@ -1,4 +1,4 @@ #include "result.h" #include "parser.h" -Result> Transpile(std::string fileContent); +Result> Transpile(std::string fileContent, bool debug); diff --git a/src/cpp/include/parser.h b/src/cpp/include/parser.h index 022afec..610f5a6 100644 --- a/src/cpp/include/parser.h +++ b/src/cpp/include/parser.h @@ -6,37 +6,31 @@ #include "lexer.h" #include "result.h" -class Question { -public: +struct Question { virtual std::string ToString() const = 0; virtual ~Question() = default; }; -// Choice struct for Multiple Choice Questions struct Choice { std::string Answer; bool IsCorrect; }; -class SingleAnswerQuestion : public Question { -public: +struct SingleAnswerQuestion : public Question { std::string ID; std::string QuestionText; std::string Answer; std::string Section; - // Override ToString method std::string ToString() const override; }; -class MultipleChoiceQuestion : public Question { -public: +struct MultipleChoiceQuestion : public Question { std::string ID; std::string QuestionText; std::vector Choices; std::string Section; - // Override ToString method std::string ToString() const override; }; diff --git a/src/cpp/qtapp/.cache/clangd/index/main.cpp.6DE93E662B25E657.idx b/src/cpp/qtapp/.cache/clangd/index/main.cpp.6DE93E662B25E657.idx index 97f0579..1d9bd4f 100644 Binary files a/src/cpp/qtapp/.cache/clangd/index/main.cpp.6DE93E662B25E657.idx and b/src/cpp/qtapp/.cache/clangd/index/main.cpp.6DE93E662B25E657.idx differ diff --git a/src/cpp/qtapp/CMakeLists.txt b/src/cpp/qtapp/CMakeLists.txt index 2b7d0e6..0e0c272 100644 --- a/src/cpp/qtapp/CMakeLists.txt +++ b/src/cpp/qtapp/CMakeLists.txt @@ -16,4 +16,6 @@ add_executable( MdemoryApp main.cpp ) + target_link_libraries(MdemoryApp Qt5::Widgets api) +target_include_directories(MdemoryApp PRIVATE ${CMAKE_SOURCE_DIR}/include) diff --git a/src/cpp/qtapp/main.cpp b/src/cpp/qtapp/main.cpp index 6b8cc0a..b9aa857 100644 --- a/src/cpp/qtapp/main.cpp +++ b/src/cpp/qtapp/main.cpp @@ -1,3 +1,14 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -16,8 +27,10 @@ #include #include #include +#include #include "api.h" +#include "parser.h" struct Page { int start; @@ -33,7 +46,6 @@ public: QToolButton *showButton; int labelCount; - // Method to show or hide back labels void showBacklabels() { for (int i = 0; i < backLabels.size(); ++i) { if (i < labelCount) { @@ -48,7 +60,6 @@ public: } } - // Constructor to create an Mdem object Mdem() { wMdem = new QWidget(); QVBoxLayout *vMdem = new QVBoxLayout(); @@ -62,6 +73,7 @@ public: QHBoxLayout *hFront = new QHBoxLayout(); wFront->setMinimumHeight(60); wFront->setLayout(hFront); + wFront->setProperty("first", "true"); wMdem->setStyleSheet(QString( "QWidget#%1 > QWidget {" "border-right: 1px solid gray;" @@ -117,16 +129,41 @@ public: } }; -QLabel *deckListLabel; -QVBoxLayout *hMdemScroll; -QSpacerItem *mdemSpacer; -QList mdems; -QString workingPath = "/home/jorenchik/Code/mdemory/memorybase"; -void CreateMdems(QList& questions) { - if (mdemSpacer) { - hMdemScroll->removeItem(mdemSpacer); - } +QString workingPath = "/home/jorenchik/Code/mdemory/memorybase"; +QList mdems = QList(); +std::vector questions = std::vector(); +QLabel *deckListLabel; +QVBoxLayout *hMdemScroll; +QSpacerItem *mdemSpacer; + +const int PER_PAGE = 8; +int currentPage = -1; +std::vector pages; +QToolButton* prevButton; +QToolButton* firstButton; +QToolButton* lastButton; +QToolButton* nextButton; +QList paginationButtons; +QLabel* paginationLabel; + +const std::regex doubleSpaceExp( + " ", + std::regex_constants::ECMAScript | std::regex_constants::icase +); + +const std::regex tabExp( + "\t", + std::regex_constants::ECMAScript | std::regex_constants::icase +); + +const std::regex newLineExp( + "\n", + std::regex_constants::ECMAScript | std::regex_constants::icase +); + +void CreateMdems(std::vector& questions) { + hMdemScroll->removeItem(mdemSpacer); for (Mdem *mdem : mdems) { if (mdem->wMdem->isVisible()) { @@ -134,18 +171,146 @@ void CreateMdems(QList& questions) { } } - // TODO + if (questions.size() > mdems.size()) { + auto amount = questions.size() - mdems.size(); + for (size_t i = 0; i < amount; ++i) { + auto mdem = new Mdem(); + mdems.append(mdem); + hMdemScroll->addWidget(mdem->wMdem); + } + } + + auto transformAnswer = [](std::string answer) -> std::string { + answer = std::regex_replace(answer, doubleSpaceExp, " "); + answer = std::regex_replace(answer, tabExp, ""); + answer = std::regex_replace(answer, newLineExp, ""); + return answer; + }; + + for (size_t i = 0; i < questions.size(); ++i) { + if (SingleAnswerQuestion* sa = dynamic_cast(questions[i])) { + mdems[i]->wFrontText->setText(QString::fromStdString(sa->QuestionText)); + auto answer = sa->Answer; + answer = transformAnswer(answer); + answer = std::format("- {}", answer); + mdems[i]->backLabels[0]->setText(QString::fromStdString(answer)); + if (mdems[i]->wBack->isVisible()) { + mdems[i]->wBack->hide(); + } + mdems[i]->labelCount = 1; + } else if (MultipleChoiceQuestion* mw = dynamic_cast(questions[i])) { + mdems[i]->wFrontText->setText( + QString::fromStdString(mw->QuestionText) + ); + auto choices = mw->Choices; + for (size_t k = 0; k < choices.size(); ++k) { + auto answer = choices[k].Answer; + answer = transformAnswer(answer); + answer = std::format("- {}", answer); + if (k < mdems[i]->backLabels.size()) { + mdems[i]->backLabels[k]->setText(QString::fromStdString(answer)); + } else { + auto label = new QLabel(); + label->setText(QString::fromStdString(answer)); + mdems[i]->backLabels.push_back(label); + mdems[i]->wBack->layout()->addWidget(label); + } + } + mdems[i]->labelCount = choices.size(); + } + if (!mdems[i]->wMdem->isVisible()) { + mdems[i]->wMdem->show(); + } + } + + hMdemScroll->addItem(mdemSpacer); } -void SwitchPage(int pageIdx, QList& pages, QList& mdems) { - for (Mdem *mdem : mdems) { +const int DISTANCE = 2; + + +void makePages() { + pages.clear(); + auto len = 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; + } + pages.push_back(Page{startingIndex, startingIndex + amount}); + } +} + + +void SwitchPage(int pageIdx) { + currentPage = pageIdx; + + // Hide all pagination buttons + for (auto& button : paginationButtons) { + button->hide(); + } + + int l = 0; + char buffer[50]; + snprintf(buffer, sizeof(buffer), "Page: %d", pageIdx + 1); + paginationLabel->setText(buffer); + + // Hide widgets in mdems + for (auto& mdem : mdems) { if (mdem->wBack->isVisible()) { mdem->wBack->hide(); mdem->showButton->setText("Show"); } } - // TODO + // Update pagination buttons + for (int k = -DISTANCE; k <= DISTANCE; ++k) { + if (pageIdx + k >= 0 && pageIdx + k < pages.size()) { + auto button = 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 && pages.size() > 1) { + firstButton->show(); + } else { + firstButton->hide(); + } + + if (pageIdx < pages.size() - 1 && pages.size() > 1) { + lastButton->show(); + } else { + lastButton->hide(); + } + + // Handle next and previous buttons + if (!pages.empty() && currentPage < pages.size() - 1) { + nextButton->show(); + } else { + nextButton->hide(); + } + + if (!pages.empty() && currentPage >= 1) { + prevButton->show(); + } else { + prevButton->hide(); + } + + // Handle page slice + const Page& page = pages[pageIdx]; + + std::vector pageSlice(questions.begin() + page.start, questions.begin() + page.end); + CreateMdems(pageSlice); } int main(int argc, char *argv[]) { @@ -154,45 +319,74 @@ int main(int argc, char *argv[]) { QSplitter *hSplitter = new QSplitter(); + // LeftSide - QWidget *leftWidget = new QWidget(); - QVBoxLayout *leftLayout = new QVBoxLayout(); - QLabel *mdemLabel = new QLabel("Mdems"); - QFileSystemModel *model = new QFileSystemModel(); - QTreeView *mdemList = new QTreeView(); + QWidget *leftWidget = new QWidget(); + QVBoxLayout *leftLayout = new QVBoxLayout(); + QLabel *mdemLabel = new QLabel("Mdems"); + QFileSystemModel *model = new QFileSystemModel(); + QTreeView *mdemList = new QTreeView(); + mdemSpacer = new QSpacerItem(50, 50, QSizePolicy::Minimum, QSizePolicy::Expanding); leftWidget->setLayout(leftLayout); leftLayout->addWidget(mdemLabel); model->setRootPath(workingPath); mdemList->setModel(model); + QObject::connect( + mdemList, + &QTreeView::doubleClicked, + [model](const QModelIndex &index) { + auto fileInfo = model->fileInfo(index); + auto path = fileInfo.filePath().toStdString(); + auto file = std::ifstream(path); + std::string content; + if (file) { + std::stringstream buffer; + buffer << file.rdbuf(); + content = buffer.str(); + auto parseRes = Transpile(content, true); + for (auto question: questions) { + delete question; + } + questions.clear(); + questions = parseRes.value; + makePages(); + SwitchPage(0); + } else { + std::cout << std::format("Could not open the file: {}", path) << std::endl; + } + } + ); - QModelIndex rootIndex = model->index("/home/jorenchik/Code/mdemory/memorybase"); + QModelIndex rootIndex = model->index( + "/home/jorenchik/Code/mdemory/memorybase" + ); mdemList->setRootIndex(rootIndex); leftLayout->addWidget(mdemList); // DeckList - QLabel *deckLabel = new QLabel("Decks"); - QListView *deckList = new QListView(); + QLabel *deckLabel = new QLabel("Decks"); + QListView *deckList = new QListView(); leftLayout->addWidget(deckLabel); leftLayout->addWidget(deckList); // RightSide - QWidget *rightWidget = new QWidget(); + QWidget *rightWidget = new QWidget(); QVBoxLayout *rightLayout = new QVBoxLayout(); rightWidget->setLayout(rightLayout); - QWidget *top = new QWidget(); + QWidget *top = new QWidget(); QHBoxLayout *hTop = new QHBoxLayout(); - deckListLabel = new QLabel("Mdem: todo.mdem"); + deckListLabel = new QLabel("Mdem: todo.mdem"); top->setLayout(hTop); rightLayout->addWidget(top); hTop->addWidget(deckListLabel); hTop->addStretch(1); - QToolButton *refresh = new QToolButton(); + QToolButton *refresh = new QToolButton(); QToolButton *practice = new QToolButton(); - QToolButton *shuffle = new QToolButton(); + QToolButton *shuffle = new QToolButton(); hTop->addWidget(refresh); hTop->addWidget(shuffle); @@ -204,13 +398,14 @@ int main(int argc, char *argv[]) { practice->setText("Practice"); // Mdems - QScrollArea *mdemScroll = new QScrollArea(); - QWidget *mdemContainer = new QWidget(); + QScrollArea *mdemScroll = new QScrollArea(); + QWidget *mdemContainer = new QWidget(); hMdemScroll = new QVBoxLayout(); mdemScroll->setWidget(mdemContainer); mdemScroll->setWidgetResizable(true); mdemContainer->setLayout(hMdemScroll); rightLayout->addWidget(mdemScroll); + hMdemScroll->addItem(mdemSpacer); // Pagination hSplitter->addWidget(leftWidget); @@ -218,6 +413,70 @@ int main(int argc, char *argv[]) { hSplitter->setStretchFactor(0, 1); hSplitter->setStretchFactor(1, 3); + + auto pagination = new QWidget(); + auto hPagination = new QHBoxLayout(); + pagination->setLayout(hPagination); + + firstButton = new QToolButton(); + firstButton->setText(QString::fromStdString("<<")); + hPagination->addWidget(firstButton); + firstButton->hide(); + QObject::connect(firstButton, &QToolButton::clicked, [](bool checked) { + if (pages.size() > 0) { + SwitchPage(0); + } + }); + + prevButton = new QToolButton(); + prevButton->setText(QString::fromStdString("<")); + hPagination->addWidget(prevButton); + prevButton->hide(); + QObject::connect(prevButton, &QToolButton::clicked, [](bool checked) { + if (pages.size() > 0) { + SwitchPage(currentPage - 1); + } + }); + + for (int i = 0; i < PER_PAGE; i++) { + auto elButton = new QToolButton(); + elButton->setText(QString("%1").arg(i+1)); + hPagination->addWidget(elButton); + elButton->hide(); + QObject::connect(elButton, &QToolButton::clicked, [elButton](bool checked) { + auto pageNum = std::stoi(elButton->text().toStdString().c_str()); + auto pageIdx = pageNum - 1; + if (pageIdx < pages.size()) { + SwitchPage(pageIdx); + } + }); + paginationButtons.push_back(elButton); + } + + nextButton = new QToolButton(); + nextButton->setText(QString::fromStdString(">")); + hPagination->addWidget(nextButton); + nextButton->hide(); + QObject::connect(nextButton, &QToolButton::clicked, [](bool checked) { + if (pages.size() > 0) { + SwitchPage(currentPage + 1); + } + }); + + lastButton = new QToolButton(); + lastButton->setText(QString::fromStdString(">>")); + hPagination->addWidget(lastButton); + lastButton->hide(); + QObject::connect(lastButton, &QToolButton::clicked, [](bool checked) { + if (pages.size() > 0) { + SwitchPage(pages.size() - 1); + } + }); + hPagination->addStretch(1); + paginationLabel = new QLabel(); + hPagination->addWidget(paginationLabel); + rightLayout->addWidget(pagination); + window.setCentralWidget(hSplitter); window.show(); return app.exec(); diff --git a/src/cpp/transpiler/.cache/clangd/index/api.cpp.00DF3E88855FBE2C.idx b/src/cpp/transpiler/.cache/clangd/index/api.cpp.00DF3E88855FBE2C.idx index 09ba8d1..0234583 100644 Binary files a/src/cpp/transpiler/.cache/clangd/index/api.cpp.00DF3E88855FBE2C.idx and b/src/cpp/transpiler/.cache/clangd/index/api.cpp.00DF3E88855FBE2C.idx differ diff --git a/src/cpp/transpiler/.cache/clangd/index/main.cpp.3110054129CACA6D.idx b/src/cpp/transpiler/.cache/clangd/index/main.cpp.3110054129CACA6D.idx index b22bbed..edfaa5d 100644 Binary files a/src/cpp/transpiler/.cache/clangd/index/main.cpp.3110054129CACA6D.idx and b/src/cpp/transpiler/.cache/clangd/index/main.cpp.3110054129CACA6D.idx differ diff --git a/src/cpp/transpiler/.cache/clangd/index/parser.cpp.1DA6C31FD012A889.idx b/src/cpp/transpiler/.cache/clangd/index/parser.cpp.1DA6C31FD012A889.idx index 6231dc1..8e42f30 100644 Binary files a/src/cpp/transpiler/.cache/clangd/index/parser.cpp.1DA6C31FD012A889.idx and b/src/cpp/transpiler/.cache/clangd/index/parser.cpp.1DA6C31FD012A889.idx differ diff --git a/src/cpp/transpiler/CMakeLists.txt b/src/cpp/transpiler/CMakeLists.txt index c6b2cb3..ca3fafb 100644 --- a/src/cpp/transpiler/CMakeLists.txt +++ b/src/cpp/transpiler/CMakeLists.txt @@ -1,22 +1,21 @@ # Set sources -set(SOURCES +set( + SOURCES main.cpp +) + +add_library( + api lexer.cpp parser.cpp time.cpp api.cpp ) - -#include_directories(${CMAKE_SOURCE_DIR}/include) -add_library(api api.cpp) - -# Add the executable (CLI) add_executable(transpiler ${SOURCES}) -target_compile_options(transpiler PRIVATE -Wall -Wextra -Wpedantic) +target_link_libraries(transpiler api) -#add_library(api api.cpp) -#target_link_libraries(transpiler PRIVATE api) +target_compile_options(transpiler PRIVATE -Wall -Wextra -Wpedantic) target_include_directories(transpiler PRIVATE ${CMAKE_SOURCE_DIR}/include) target_include_directories(api PUBLIC ${CMAKE_SOURCE_DIR}/include) diff --git a/src/cpp/transpiler/api.cpp b/src/cpp/transpiler/api.cpp index bbcc7c1..43f7d3f 100644 --- a/src/cpp/transpiler/api.cpp +++ b/src/cpp/transpiler/api.cpp @@ -6,9 +6,17 @@ #include "lexer.h" #include "parser.h" #include "time.h" +#include "config.h" -Result> Transpile(std::string fileContent) { +bool debug; +std::chrono::high_resolution_clock::time_point start; +std::chrono::high_resolution_clock::time_point end; + +Result> Transpile(std::string fileContent, bool isDebug) { start = std::chrono::high_resolution_clock::now(); + end = std::chrono::high_resolution_clock::now(); + debug = isDebug; + auto lexRes = TokenizeMdem(fileContent); auto tokens = lexRes.value; if (lexRes.error.length() > 0) { diff --git a/src/cpp/transpiler/main.cpp b/src/cpp/transpiler/main.cpp index c5fac80..86ae4aa 100644 --- a/src/cpp/transpiler/main.cpp +++ b/src/cpp/transpiler/main.cpp @@ -5,7 +5,6 @@ #include #include "time.h" -#include "config.h" #include "api.h" std::string readFile(const std::string& filePath) { @@ -24,14 +23,11 @@ std::string readFile(const std::string& filePath) { return content; } -bool debug = false; - -std::chrono::high_resolution_clock::time_point start; -std::chrono::high_resolution_clock::time_point end; int main(int argc, char* argv[]) { std::string filePath; + bool debug = false; if (argc == 3) { auto option = std::string(argv[1]); if (option == "--debug") { @@ -49,12 +45,11 @@ int main(int argc, char* argv[]) { } try { - start = std::chrono::high_resolution_clock::now(); std::string fileContent = readFile(filePath); end = std::chrono::high_resolution_clock::now(); ShowTime("I/O time"); - auto res = Transpile(fileContent); + auto res = Transpile(fileContent, debug); auto questions = res.value; if (res.error.length() > 0) { std::cout << std::format( diff --git a/src/cpp/transpiler/parser.cpp b/src/cpp/transpiler/parser.cpp index 0ab7607..ce6f46e 100644 --- a/src/cpp/transpiler/parser.cpp +++ b/src/cpp/transpiler/parser.cpp @@ -139,6 +139,10 @@ Result ValidateGrammar(const std::vector& tokens) { Result> ParseQuestions(const std::vector& tokens) { auto questions = std::vector(); + if (tokens.size() == 0) { + return {questions, ""}; + } + auto result = ValidateGrammar(tokens); if (result.error.length() > 0) { return { @@ -152,6 +156,7 @@ Result> ParseQuestions(const std::vector& tokens) std::string section; size_t i = 0; + debug = true; if (debug) { std::cout << "SECTION: Parser output:\n"; }