From 4ac9ededeaa8640d175a3d3f767d5727ed17579b Mon Sep 17 00:00:00 2001 From: jose-rZM <100773386+jose-rZM@users.noreply.github.com> Date: Sun, 31 May 2026 10:54:49 +0200 Subject: [PATCH 1/4] chore: update gitignore --- .gitignore | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.gitignore b/.gitignore index b7231b18..09782674 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.clangd build/ media/ .vscode/ @@ -8,4 +9,28 @@ Dockerfile Makefile .qmake.stash SyntaxTutor +SyntaxTutor.app .cache + +# Qt generated sources and headers +moc_*.cpp +moc_predefs.h +qrc_*.cpp +ui_*.h + +# qmake/Qt generated build helpers +target_wrapper.sh +.objects/ +.moc/ +.rcc/ +.ui/ +.bin/ + +# Test build outputs +tests/target_wrapper.sh +tests/tutor_tests +tests/.objects/ +tests/.moc/ +tests/.rcc/ +tests/.ui/ +tests/.bin/ From e5b3388d93aeb018b231aad5668dff8a9a429862 Mon Sep 17 00:00:00 2001 From: jose-rZM <100773386+jose-rZM@users.noreply.github.com> Date: Sun, 31 May 2026 11:02:55 +0200 Subject: [PATCH 2/4] chore: change build output directories --- SyntaxTutor.pro | 6 ++++++ tests/tests.pro | 9 +++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/SyntaxTutor.pro b/SyntaxTutor.pro index 5033097c..16932cda 100644 --- a/SyntaxTutor.pro +++ b/SyntaxTutor.pro @@ -6,6 +6,12 @@ TRANSLATIONS += translations/st_es.ts \ CONFIG += c++20 +OBJECTS_DIR = $$OUT_PWD/.objects +MOC_DIR = $$OUT_PWD/.moc +RCC_DIR = $$OUT_PWD/.rcc +UI_DIR = $$OUT_PWD/.ui +DESTDIR = $$OUT_PWD/.bin + # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 diff --git a/tests/tests.pro b/tests/tests.pro index 05b12f7e..af540649 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -16,10 +16,11 @@ INCLUDEPATH += \ $$PWD/helpers \ $$PWD/fixtures/grammars -OBJECTS_DIR = .objects -MOC_DIR = .moc -RCC_DIR = .rcc -UI_DIR = .ui +OBJECTS_DIR = $$OUT_PWD/.objects +MOC_DIR = $$OUT_PWD/.moc +RCC_DIR = $$OUT_PWD/.rcc +UI_DIR = $$OUT_PWD/.ui +DESTDIR = $$OUT_PWD/.bin SOURCES += \ ../src/backend/grammar.cpp \ From c48c1cc0ef220c0eab4b0b09064de52ec08ee0ac Mon Sep 17 00:00:00 2001 From: jose-rZM <100773386+jose-rZM@users.noreply.github.com> Date: Sun, 31 May 2026 11:03:54 +0200 Subject: [PATCH 3/4] fix: change wizard style --- resources/styles/app.qss | 11 +++++------ src/gui/slrwizard.h | 14 ++++++++++++++ src/gui/slrwizardpage.h | 27 ++++++++++++++++++++++----- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/resources/styles/app.qss b/resources/styles/app.qss index 0470999b..3e65e1f2 100644 --- a/resources/styles/app.qss +++ b/resources/styles/app.qss @@ -533,16 +533,15 @@ QWizard[wizardTheme="slr"] QWizard::title { font-size: 18px; } -QWizard[wizardTheme="slr"] QWizard::subTitle { - color: #CCCCCC; - font-size: 14px; - margin-bottom: 12px; -} - QWizard[wizardTheme="slr"] QLabel { color: #E0E0E0; } +QWizard[wizardTheme="slr"] QLabel#slrWizardFeedbackLabel { + color: #CCCCCC; + min-height: 20px; +} + QWizard[wizardTheme="slr"] QLineEdit { background-color: #3C3F41; color: #E0E0E0; diff --git a/src/gui/slrwizard.h b/src/gui/slrwizard.h index 91d21ebb..f1628fa3 100644 --- a/src/gui/slrwizard.h +++ b/src/gui/slrwizard.h @@ -21,6 +21,7 @@ #include "slr1_parser.hpp" #include "slrwizardpage.h" +#include #include #include #include @@ -61,6 +62,19 @@ class SLRWizard : public QWizard { const QVector>>& sortedGrammar, QWidget* parent = nullptr) : QWizard(parent) { + setWizardStyle(QWizard::ModernStyle); + setWindowFlag(Qt::WindowCloseButtonHint, true); + setButtonText(QWizard::CancelButton, tr("Salir")); + setButtonText(QWizard::NextButton, tr("Continuar")); + setButtonText(QWizard::FinishButton, tr("Finalizar")); + setButtonLayout({QWizard::Stretch, QWizard::CancelButton, + QWizard::NextButton, QWizard::FinishButton}); + + if (auto* backButton = button(QWizard::BackButton)) { + backButton->hide(); + backButton->setEnabled(false); + } + setWindowTitle(tr("Ayuda interactiva: Tabla SLR(1)")); const int nTerm = parser.gr_.st_.terminals_.size(); diff --git a/src/gui/slrwizardpage.h b/src/gui/slrwizardpage.h index 82f614c2..4526f889 100644 --- a/src/gui/slrwizardpage.h +++ b/src/gui/slrwizardpage.h @@ -56,10 +56,16 @@ class SLRWizardPage : public QWizardPage { : QWizardPage(parent), m_state(state), m_symbol(symbol), m_expected(expected) { setTitle(tr("Estado %1, símbolo '%2'").arg(state).arg(symbol)); + setSubTitle(QString()); QLabel* lbl = new QLabel(explanation, this); lbl->setWordWrap(true); + m_feedback = new QLabel(this); + m_feedback->setObjectName("slrWizardFeedbackLabel"); + m_feedback->setWordWrap(true); + m_feedback->hide(); + m_edit = new QLineEdit(this); m_edit->setObjectName("slrWizardAnswerEdit"); m_edit->setPlaceholderText( @@ -67,6 +73,7 @@ class SLRWizardPage : public QWizardPage { QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(lbl); + layout->addWidget(m_feedback); layout->addWidget(m_edit); setLayout(layout); @@ -83,15 +90,24 @@ class SLRWizardPage : public QWizardPage { * @param text The current user input. */ void onTextChanged(const QString& text) { - bool correct = (text.trimmed() == m_expected); + const QString trimmed = text.trimmed(); + bool correct = (trimmed == m_expected); setComplete(correct); - if (correct) { - setSubTitle( + + if (trimmed.isEmpty()) { + m_feedback->clear(); + m_feedback->hide(); + } else if (correct) { + m_feedback->setText( tr("✔ Respuesta correcta, pasa a la siguiente pregunta")); + m_feedback->show(); } else { - setSubTitle(tr("✘ Incorrecto, revisa el enunciado. Consulta los " - "estados que has construido.")); + m_feedback->setText(tr("✘ Incorrecto, revisa el enunciado. " + "Consulta los estados que has " + "construido.")); + m_feedback->show(); } + wizard()->button(QWizard::NextButton)->setEnabled(correct); } @@ -115,6 +131,7 @@ class SLRWizardPage : public QWizardPage { int m_state; ///< The state index of the cell. QString m_symbol; ///< The symbol (terminal or non-terminal). QString m_expected; ///< The expected user response. + QLabel* m_feedback; ///< Inline feedback label for answer validation. QLineEdit* m_edit; ///< Input field for the user's answer. bool m_isComplete = false; ///< Whether the user has entered the correct response. From 3034c13ffc115c824fcdb8266424ab8df2f049bf Mon Sep 17 00:00:00 2001 From: jose-rZM <100773386+jose-rZM@users.noreply.github.com> Date: Sun, 31 May 2026 11:04:10 +0200 Subject: [PATCH 4/4] test: add test case --- tests/tutor/slr_tutor_window_test.cpp | 69 +++++++++++++++++++++++++++ tests/tutor/tutor_window_test.h | 1 + 2 files changed, 70 insertions(+) diff --git a/tests/tutor/slr_tutor_window_test.cpp b/tests/tutor/slr_tutor_window_test.cpp index 8baebb4f..d1f4d89b 100644 --- a/tests/tutor/slr_tutor_window_test.cpp +++ b/tests/tutor/slr_tutor_window_test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -680,6 +681,74 @@ void TutorWindowTest::slrStateHIncorrectTableKeepsDialogOpen() { QVERIFY(dialog->isVisible()); } +// ----------------------------------------------------------------------------- +// Case: SLR1-TC-17A +// Summary: +// Verifies that guided mode uses the custom wizard navigation and can be +// exited without going backwards. +// +// Situation: +// SLR(1) tutor in H with the table dialog visible. +// +// Action: +// The user opens guided mode and then exits through the wizard cancel button. +// +// Expected: +// The wizard uses the configured non-native style, does not expose a back +// button, and closes back to the table dialog. +// ----------------------------------------------------------------------------- +void TutorWindowTest::slrGuidedModeWizardUsesCustomNavigationAndAllowsExit() { + const Grammar grammar = TutorGrammarFixtures::makeSlrSimpleGrammar(); + + SLRTutorWindow tutor(grammar, nullptr); + SlrTutorTestUtils::submitCorrectAnswerForCurrentState(tutor); + driveSlrTutorToH(tutor); + + SLRTableDialog* dialog = waitForSlrTableDialog(); + auto* table = dialog->findChild("slrTableWidget"); + QVERIFY(table != nullptr); + + QtModalTestUtils::requestSlrGuidedMode( + dialog, QVector>(table->rowCount(), + QVector(table->columnCount()))); + QWizard* wizard = waitForWizard(); + QVERIFY(wizard != nullptr); + QPointer wizardGuard(wizard); + auto* page = qobject_cast(wizard->currentPage()); + QVERIFY(page != nullptr); + + QCOMPARE(wizard->wizardStyle(), QWizard::ModernStyle); + QCOMPARE(page->subTitle(), QString()); + + auto* backButton = wizard->button(QWizard::BackButton); + QVERIFY(backButton != nullptr); + QVERIFY(!backButton->isVisibleTo(wizard)); + QVERIFY(!backButton->isEnabled()); + + auto* feedbackLabel = page->findChild("slrWizardFeedbackLabel"); + QVERIFY(feedbackLabel != nullptr); + QVERIFY(!feedbackLabel->isVisible()); + + auto* edit = page->findChild("slrWizardAnswerEdit"); + QVERIFY(edit != nullptr); + edit->setText(QStringLiteral("s")); + QApplication::processEvents(); + + QCOMPARE(page->subTitle(), QString()); + QVERIFY(feedbackLabel->isVisible()); + QVERIFY(!feedbackLabel->text().isEmpty()); + + auto* cancelButton = qobject_cast( + wizard->button(QWizard::CancelButton)); + QVERIFY(cancelButton != nullptr); + QVERIFY(cancelButton->isVisibleTo(wizard)); + + QTest::mouseClick(cancelButton, Qt::LeftButton); + QTRY_VERIFY(wizardGuard == nullptr || !wizardGuard->isVisible()); + QVERIFY(dialog->isVisible()); + QCOMPARE(tutor.currentStateForTest(), QString("H")); +} + // ----------------------------------------------------------------------------- // Case: SLR1-TC-18 // Summary: diff --git a/tests/tutor/tutor_window_test.h b/tests/tutor/tutor_window_test.h index 560deb63..bc5f2a2c 100644 --- a/tests/tutor/tutor_window_test.h +++ b/tests/tutor/tutor_window_test.h @@ -37,6 +37,7 @@ class TutorWindowTest : public QObject { void slrStateFConflictBranchAdvancesToFAAndThenG(); void slrStateGWrongThenCorrectReachesH(); void slrStateHIncorrectTableKeepsDialogOpen(); + void slrGuidedModeWizardUsesCustomNavigationAndAllowsExit(); void slrGuidedModeWizardCompletesAndReturnsToTable(); void slrTableDialogCancelNoReopensAndYesRequestsExit(); void slrFinalTableCorrectPathExportsAndExits();