From 03652c4718e9d5c6bf8d6fb713ed10fc6a2a3061 Mon Sep 17 00:00:00 2001 From: jose-rZM <100773386+jose-rZM@users.noreply.github.com> Date: Sat, 16 May 2026 13:40:35 +0200 Subject: [PATCH 1/6] feat(ui): redesign --- resources.qrc | 1 - resources/styles/app.qss | 256 +++++++++++++++---- src/gui/lltabledialog.cpp | 14 +- src/gui/lltabledialog.h | 2 + src/gui/lltutorwindow.cpp | 78 ++++-- src/gui/lltutorwindow.ui | 57 ++++- src/gui/mainwindow.cpp | 296 ++++++++++++--------- src/gui/mainwindow.ui | 509 ++++++++++++++++++++----------------- src/gui/slrtabledialog.cpp | 19 +- src/gui/slrtabledialog.h | 2 + src/gui/slrtutorwindow.cpp | 14 +- src/gui/slrtutorwindow.ui | 57 ++++- 12 files changed, 841 insertions(+), 464 deletions(-) diff --git a/resources.qrc b/resources.qrc index 47e5d964..641551d3 100644 --- a/resources.qrc +++ b/resources.qrc @@ -5,7 +5,6 @@ resources/NotoSans-Bold.ttf resources/NotoSans-Italic.ttf resources/NotoSans-Regular.ttf - resources/trophy.svg resources/styles/app.qss translations/st_en.qm translations/st_es.qm diff --git a/resources/styles/app.qss b/resources/styles/app.qss index 138c84a1..90ec64d1 100644 --- a/resources/styles/app.qss +++ b/resources/styles/app.qss @@ -9,6 +9,38 @@ QWidget#centralwidget { color: #E0E0E0; } +QWidget#homeContent { + background: transparent; +} + +QLabel#homeEyebrow { + color: #15B9C2; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +QLabel#homeTitle { + color: #F4F7F8; + font-size: 30px; + font-weight: 700; +} + +QLabel#homeSubtitle { + color: #9AA5A8; + font-size: 15px; + line-height: 1.5; +} + +QLabel#difficultyTitle { + color: #C9D0D2; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + QMenu#menuAcercaDe::item { padding: 6px 24px; } @@ -23,56 +55,84 @@ QMenu#menuAcercaDe::icon { } QLabel#labelScore { - font-weight: bold; + font-weight: 700; font-size: 15px; - color: white; + color: #F3F5F6; +} + +QLabel#badgeNivel { + min-width: 34px; + min-height: 24px; + padding: 0 10px; + border-radius: 12px; + background-color: rgba(17, 179, 188, 0.18); + color: #BDECEF; + border: none; + qproperty-alignment: 'AlignCenter'; +} + +QProgressBar#progressBarNivel { + background-color: #2A2E30; + border: none; + border-radius: 4px; +} + +QProgressBar#progressBarNivel::chunk { + background-color: #11B3BC; + border-radius: 4px; } QPushButton#pushButton, QPushButton#pushButton_2 { - background-color: #00ADB5; + background-color: #262B2D; color: #FFFFFF; - border: none; - padding: 12px 24px; + border: 1px solid #353A3D; + padding: 0 18px; border-radius: 10px; font-size: 16px; - font-weight: bold; + font-weight: 700; + text-align: left; } QPushButton#pushButton:hover, QPushButton#pushButton_2:hover { - background-color: #00CED1; + background-color: #2D3436; + border: 1px solid #4A5255; } QPushButton#pushButton:pressed, QPushButton#pushButton_2:pressed { - background-color: #007F86; + background-color: #222729; } QPushButton#tutorial { - background-color: #2F2F2F; - color: #CCCCCC; - border: 1px solid #444444; - padding: 8px 16px; - border-radius: 4px; + background-color: rgba(255, 255, 255, 0.03); + color: #CDD5D7; + border: 1px solid rgba(255, 255, 255, 0.08); + padding: 0 14px; + border-radius: 8px; font-size: 14px; + font-weight: 600; + text-align: left; } QPushButton#tutorial:hover { - background-color: #3A3A3A; + background-color: rgba(255, 255, 255, 0.06); color: #FFFFFF; + border: 1px solid rgba(255, 255, 255, 0.14); } QPushButton#tutorial:pressed { - background-color: #262626; + background-color: rgba(255, 255, 255, 0.09); } QRadioButton#lv1Button, QRadioButton#lv2Button, QRadioButton#lv3Button { - color: #DDDDDD; - font-size: 16px; - spacing: 6px; + color: #D9E0E2; + font-size: 15px; + font-weight: 600; + spacing: 8px; } QRadioButton#lv1Button::indicator, @@ -100,26 +160,73 @@ QRadioButton#lv3Button::indicator:checked + QLabel { QPushButton#idiom { background: transparent; - color: #CCCCCC; - border: 1px solid #00ADB5; - border-radius: 4px; - padding: 6px 14px; + color: #AEB7BA; + border: 1px solid #3A4245; + border-radius: 8px; + padding: 6px 12px; font-size: 12px; } QPushButton#idiom:hover { - background-color: rgba(255, 255, 255, 0.05); + background-color: rgba(255, 255, 255, 0.04); color: #FFFFFF; + border: 1px solid #4A5457; } QPushButton#idiom:pressed { background-color: rgba(255, 255, 255, 0.10); } +QWidget#LLTutorWindow, +QWidget#SLRTutorWindow { + background-color: #1F1F1F; +} + +QWidget#LLTutorWindow QPushButton#backButton, +QWidget#SLRTutorWindow QPushButton#backButton { + background: transparent; + color: #C5CED0; + border: 1px solid #353D40; + border-radius: 8px; + padding: 6px 12px; + font-size: 13px; + font-weight: 600; +} + +QWidget#LLTutorWindow QPushButton#backButton:hover, +QWidget#SLRTutorWindow QPushButton#backButton:hover { + background-color: rgba(255, 255, 255, 0.04); + color: #FFFFFF; + border: 1px solid #475155; +} + +QWidget#LLTutorWindow QPushButton#backButton:pressed, +QWidget#SLRTutorWindow QPushButton#backButton:pressed { + background-color: rgba(255, 255, 255, 0.08); +} + +QWidget#LLTutorWindow QLabel#tick, +QWidget#SLRTutorWindow QLabel#tick { + color: #42C78B; +} + +QWidget#LLTutorWindow QLabel#cross, +QWidget#SLRTutorWindow QLabel#cross { + color: #E46B6B; +} + +QWidget#LLTutorWindow QLabel#cntRight, +QWidget#LLTutorWindow QLabel#cntWrong, +QWidget#SLRTutorWindow QLabel#cntRight, +QWidget#SLRTutorWindow QLabel#cntWrong { + color: #F1F4F5; + font-weight: 600; +} + QListWidget#listWidget { - border: 1px solid #3A3A3A; - border-radius: 6px; - background-color: transparent; + border: 1px solid #303638; + border-radius: 12px; + background-color: #212526; } QListWidget#listWidget::viewport { @@ -155,10 +262,10 @@ QListWidget#listWidget QScrollBar::sub-page:vertical { } QAbstractScrollArea#gr { - background-color: #232936; - border: 1px solid #3A3A3A; - padding: 12px; - border-radius: 6px; + background-color: #20252B; + border: 1px solid #30363A; + padding: 14px; + border-radius: 12px; } QAbstractScrollArea#gr QWidget { @@ -166,19 +273,25 @@ QAbstractScrollArea#gr QWidget { } QFrame#grammarView { - background-color: #232936; + background-color: #20252B; } QTextEdit#textEdit { - background-color: #1E1E1E; + background-color: #212526; color: #FFFFFF; - border: 1px solid #3A3A3A; - border-radius: 6px; - padding: 8px; + border: 1px solid #303638; + border-radius: 12px; + padding: 12px; selection-background-color: #44475A; selection-color: #FFFFFF; } +QTextEdit#textEdit QWidget, +QTextEdit#textEdit QAbstractScrollArea, +QTextEdit#textEdit QAbstractScrollArea::viewport { + background-color: #212526; +} + QTextEdit#textEdit QScrollBar:vertical { background: #1E1E1E; width: 10px; @@ -208,42 +321,43 @@ QTextEdit#textEdit QScrollBar::sub-page:vertical { } QTextEdit#userResponse { - background-color: #2E2E2E; - color: #E0E0E0; - border: 1px solid #444444; - border-radius: 6px; - padding: 6px 10px; - font-size: 16px; + background-color: #252A2C; + color: #E7ECEE; + border: 1px solid #383F42; + border-radius: 12px; + padding: 10px 14px; + font-size: 15px; } QTextEdit#userResponse:focus { - border-bottom: 2px solid #00ADB5; + border: 1px solid #11B3BC; + background-color: #282E30; } QTextEdit#userResponse:disabled { - background-color: #3A3A3A; + background-color: #313638; color: #777777; - border: 1px solid #555555; + border: 1px solid #444A4D; } QPushButton#confirmButton { - background-color: rgba(0, 173, 181, 0.85); + background-color: #11B3BC; border: none; - border-radius: 20px; - min-width: 40px; - min-height: 40px; - max-width: 40px; - max-height: 40px; + border-radius: 16px; + min-width: 48px; + min-height: 48px; + max-width: 48px; + max-height: 48px; padding: 0px; - icon-size: 24px 24px; + icon-size: 22px 22px; } QPushButton#confirmButton:hover { - background-color: #00C8D6; + background-color: #1AC4CD; } QPushButton#confirmButton:pressed { - background-color: #009AA3; + background-color: #0A9098; } QPushButton#confirmButton:disabled { @@ -363,6 +477,42 @@ QMessageBox QPushButton[role="danger"]:pressed { background-color: #C12E2A; } +QDialog#infoDialog { + background-color: #1F1F1F; + color: #E8ECEE; +} + +QLabel#infoDialogEyebrow { + color: #11B3BC; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; +} + +QLabel#infoDialogTitle { + color: #F5F7F8; + font-size: 24px; + font-weight: 700; +} + +QLabel#infoDialogSubtitle { + color: #9AA5A8; + font-size: 14px; +} + +QTextBrowser#infoDialogContent { + background-color: #212526; + color: #E8ECEE; + border: 1px solid #303638; + border-radius: 12px; + padding: 14px 16px; + selection-background-color: #35515A; +} + +QTextBrowser#infoDialogContent a { + color: #36C5CC; +} + QWizard[wizardTheme="slr"] * { background-color: #2B2B2B; color: #E0E0E0; diff --git a/src/gui/lltabledialog.cpp b/src/gui/lltabledialog.cpp index c6fad3df..5f7b128c 100644 --- a/src/gui/lltabledialog.cpp +++ b/src/gui/lltabledialog.cpp @@ -17,6 +17,7 @@ */ #include "lltabledialog.h" +#include #include #include @@ -96,7 +97,18 @@ LLTableDialog::LLTableDialog(const QStringList& rowHeaders, resize(width, height); connect(submitButton, &QPushButton::clicked, this, - [this]() { emit submitted(getTableData()); }); + [this]() { + commitPendingEdit(); + emit submitted(getTableData()); + }); +} + +void LLTableDialog::commitPendingEdit() { + if (QWidget* editor = QApplication::focusWidget(); + editor != nullptr && table->isAncestorOf(editor)) { + submitButton->setFocus(Qt::OtherFocusReason); + QApplication::processEvents(); + } } QVector> LLTableDialog::getTableData() const { diff --git a/src/gui/lltabledialog.h b/src/gui/lltabledialog.h index 73be372f..8efd7e60 100644 --- a/src/gui/lltabledialog.h +++ b/src/gui/lltabledialog.h @@ -83,6 +83,8 @@ class LLTableDialog : public QDialog { void submitted(const QVector>& data); private: + void commitPendingEdit(); + QTableWidget* table; ///< The widget representing the LL(1) parsing table. QPushButton* submitButton; ///< Button to submit the completed table. }; diff --git a/src/gui/lltutorwindow.cpp b/src/gui/lltutorwindow.cpp index ecf1a31e..e228dce1 100644 --- a/src/gui/lltutorwindow.cpp +++ b/src/gui/lltutorwindow.cpp @@ -43,6 +43,44 @@ QString NormalizeProductionCell(const QString& cell) { return normalized; } +QStringList ParseProductionCell(Grammar& grammar, const QString& cell) { + const QString trimmed = cell.trimmed(); + if (trimmed.isEmpty()) { + return {}; + } + + const QStringList spacedTokens = + trimmed.split(kCellWhitespace, Qt::SkipEmptyParts); + if (!spacedTokens.isEmpty()) { + bool allKnown = true; + for (const QString& token : spacedTokens) { + if (!grammar.st_.In(token.toStdString())) { + allKnown = false; + break; + } + } + + if (allKnown) { + return spacedTokens; + } + } + + const QString normalized = NormalizeProductionCell(trimmed); + if (normalized.isEmpty()) { + return {}; + } + + QStringList production; + for (const std::string& symbol : grammar.Split(normalized.toStdString())) { + production.append(QString::fromStdString(symbol)); + } + if (!production.isEmpty()) { + return production; + } + + return {normalized}; +} + ParsedSymbols ParseSymbolList(const QString& input) { ParsedSymbols parsed; const QString text = input.trimmed(); @@ -78,7 +116,7 @@ LLTutorWindow::LLTutorWindow(const Grammar& grammar, TutorialManager* tm, // ====== UI Setup ========================================== ui->setupUi(this); - ui->backButton->setText(tr("Back")); + ui->backButton->setText(tr("Atras")); // -- Confirm Button Icon & Shadow ui->confirmButton->setIcon(QIcon(":/resources/send.svg")); @@ -146,10 +184,10 @@ void LLTutorWindow::requestExit(bool applyResults) { bool LLTutorWindow::confirmExitToHome() { QMessageBox msg(this); - msg.setWindowTitle(tr("Leave LL(1) exercise")); + msg.setWindowTitle(tr("Salir del ejercicio LL(1)")); msg.setTextFormat(Qt::RichText); - msg.setText(tr("Do you want to go back to the home page? This will discard " - "your current progress.")); + msg.setText(tr("Quieres volver al menu principal? Se perdera el progreso " + "actual.")); msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msg.setDefaultButton(QMessageBox::No); @@ -157,7 +195,7 @@ bool LLTutorWindow::confirmExitToHome() { QAbstractButton* noBtn = msg.button(QMessageBox::No); if (yesBtn) { - yesBtn->setText(tr("Yes")); + yesBtn->setText(tr("Si")); yesBtn->setCursor(Qt::PointingHandCursor); yesBtn->setIcon(QIcon()); yesBtn->setProperty("role", "primary"); @@ -192,7 +230,6 @@ void LLTutorWindow::exportConversationToPdf(const QString& filePath) { html += R"(