File indexing completed on 2024-04-14 03:49:11

0001 /*
0002     SPDX-FileCopyrightText: 2009 Frederik Gladhorn <gladhorn@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "multiplechoicemodewidget.h"
0007 #include "guifrontend.h"
0008 #include "latexrenderer.h"
0009 #include "multiplechoicedata.h"
0010 
0011 #include "ui_practice_widget_multiplechoice.h"
0012 
0013 #include <KColorScheme>
0014 #include <QAction>
0015 #include <QDebug>
0016 #include <QKeyEvent>
0017 #include <QPushButton>
0018 #include <QTimer>
0019 #include <QVBoxLayout>
0020 
0021 using namespace Practice;
0022 
0023 MultiplechoiceModeWidget::MultiplechoiceModeWidget(GuiFrontend *frontend, QWidget *parent)
0024     : AbstractModeWidget(frontend, parent)
0025     , m_latexRenderer(nullptr)
0026 {
0027     m_ui = new Ui::MultiplechoicePracticeWidget();
0028     m_ui->setupUi(this);
0029 
0030     for (int i = 0; i < 5; i++) {
0031         QAction *action = new QAction(this);
0032         action->setShortcut(Qt::Key_1 + i);
0033         addAction(action);
0034         m_actions.append(action);
0035     }
0036     connect(frontend, &AbstractFrontend::continueAction, this, &AbstractModeWidget::stopAudio);
0037     connect(frontend, &AbstractFrontend::skipAction, this, &AbstractModeWidget::stopAudio);
0038 }
0039 
0040 void MultiplechoiceModeWidget::setQuestionFont(const QFont &font)
0041 {
0042     m_ui->questionLabel->setFont(font);
0043 }
0044 
0045 void MultiplechoiceModeWidget::setSolutionFont(const QFont &font)
0046 {
0047     m_solutionFont = font;
0048     resetButtonStyleSheet();
0049 }
0050 
0051 void MultiplechoiceModeWidget::setQuestion(const QVariant &question)
0052 {
0053     if (!question.canConvert<MultipleChoiceData>()) {
0054         qWarning() << "expected MultipleChoiceData";
0055         return;
0056     }
0057     MultipleChoiceData data = question.value<MultipleChoiceData>();
0058 
0059     m_ui->questionLabel->setMinimumSize(QSize(0, 0));
0060     if (LatexRenderer::isLatex(data.question)) {
0061         if (!m_latexRenderer) {
0062             m_latexRenderer = new LatexRenderer(this);
0063             m_latexRenderer->setResultLabel(m_ui->questionLabel);
0064         }
0065         m_latexRenderer->renderLatex(data.question);
0066     } else {
0067         m_ui->questionLabel->setText(data.question);
0068     }
0069 
0070     if (m_choiceButtons.size() != data.choices.size()) {
0071         qDeleteAll(m_choiceButtons);
0072         m_choiceButtons.clear();
0073         setNumberOfPushButtons(data.choices.size());
0074     }
0075 
0076     int j = 0;
0077     for (QPushButton *pushButton : qAsConst(m_choiceButtons)) {
0078         pushButton->setText(data.choices[j]);
0079         pushButton->setToolTip(data.choices[j]);
0080         pushButton->setFont(m_solutionFont);
0081         j++;
0082     }
0083 }
0084 
0085 void MultiplechoiceModeWidget::showQuestion()
0086 {
0087     m_ui->questionPronunciationLabel->setVisible(m_ui->questionPronunciationLabel->isEnabled());
0088     m_ui->questionSoundButton->setVisible(m_ui->questionSoundButton->isEnabled());
0089     m_ui->solutionPronunciationLabel->setVisible(false);
0090     m_ui->solutionSoundButton->setVisible(false);
0091     m_ui->feedbackLabel->clear();
0092 
0093     resetButtonStyleSheet();
0094 
0095     if (!m_choiceButtons.isEmpty()) {
0096         for (QPushButton *pushButton : qAsConst(m_choiceButtons)) {
0097             pushButton->setChecked(false);
0098             pushButton->setEnabled(true);
0099         }
0100 
0101         QTimer::singleShot(0, m_choiceButtons[0], SLOT(setFocus()));
0102     }
0103 }
0104 
0105 void MultiplechoiceModeWidget::setNumberOfPushButtons(const int numberOfChoices)
0106 {
0107     QVBoxLayout *verticalLayout = new QVBoxLayout();
0108     m_ui->gridLayout->addLayout(verticalLayout, 2, 0);
0109 
0110     for (int i = 0; i < numberOfChoices; i++) {
0111         QHBoxLayout *horizontalLayout = new QHBoxLayout();
0112         verticalLayout->addLayout(horizontalLayout);
0113 
0114         // Display number of entry
0115         QLabel *label = new QLabel(QString::number(i + 1) + QStringLiteral(":"), this);
0116         horizontalLayout->addWidget(label);
0117 
0118         // Button displaying choice
0119         QPushButton *pushButton = new QPushButton(this);
0120         pushButton->setCheckable(true);
0121         pushButton->setFlat(true);
0122         pushButton->sizePolicy().setHorizontalPolicy(QSizePolicy::Maximum);
0123         m_choiceButtons.append(pushButton);
0124         if (i < 5) {
0125             connect(m_actions.at(i), &QAction::triggered, pushButton, &QAbstractButton::click);
0126         }
0127         connect(pushButton, &QPushButton::clicked, this, &MultiplechoiceModeWidget::continueAction);
0128         pushButton->installEventFilter(this);
0129         horizontalLayout->addWidget(pushButton);
0130 
0131         // Spacer to align button to the left
0132         horizontalLayout->addStretch(1);
0133     }
0134 }
0135 
0136 void MultiplechoiceModeWidget::setSynonym(const QString & /*entry*/)
0137 {
0138     // TODO Do something here to show synonyms
0139 }
0140 
0141 void MultiplechoiceModeWidget::showSynonym()
0142 {
0143     // TODO Do something here to show synonyms
0144 }
0145 
0146 bool MultiplechoiceModeWidget::eventFilter(QObject *obj, QEvent *event)
0147 {
0148     // make it possible to use the enter key to select multiple choice options
0149     if (event->type() == QEvent::KeyPress) {
0150         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
0151         if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) {
0152             QPushButton *pushButton = qobject_cast<QPushButton *>(obj);
0153             if (pushButton) {
0154                 pushButton->click();
0155                 return true;
0156             }
0157         }
0158     }
0159 
0160     return QObject::eventFilter(obj, event);
0161 }
0162 
0163 void MultiplechoiceModeWidget::setSolution(const QVariant &solution)
0164 {
0165     m_solution = solution.toInt();
0166 }
0167 
0168 void MultiplechoiceModeWidget::setHint(const QVariant &hint)
0169 {
0170     m_choiceButtons.at(hint.toInt())->setEnabled(false);
0171 }
0172 
0173 void MultiplechoiceModeWidget::setFeedback(const QVariant &feedback)
0174 {
0175     m_ui->feedbackLabel->setText(feedback.toString());
0176 }
0177 
0178 void MultiplechoiceModeWidget::showSolution()
0179 {
0180     int input = -1;
0181     if (userInput().isValid()) {
0182         input = userInput().toInt();
0183     }
0184 
0185     const QColor textColor = palette().color(QPalette::WindowText);
0186     // Set border to text color with light transparency
0187     const QString borderColor = QStringLiteral("#90") + palette().color(QPalette::WindowText).name().remove(0, 1);
0188     // Set background to correct color, but with transparency
0189     const QString correctBackground = QStringLiteral("#7D") + m_correctPalette.color(QPalette::Text).name().remove(0, 1);
0190 
0191     m_choiceButtons[m_solution]->setStyleSheet(
0192         m_choiceButtons[m_solution]->styleSheet() +
0193         " QPushButton:checked { "
0194                 "color: " + textColor.name() + "; "
0195                 "border-color: " + borderColor + "; "
0196                 "background-color: " + correctBackground +
0197                             " }"
0198     );
0199     // Always check correct button to highlight correct answer
0200     m_choiceButtons[m_solution]->setChecked(true);
0201 
0202     if (input != -1 && input != m_solution) {
0203         const QString wrongBackground = QStringLiteral("#7D") + m_wrongPalette.color(QPalette::Text).name().remove(0, 1);
0204 
0205         m_choiceButtons[input]->setStyleSheet(
0206             m_choiceButtons[input]->styleSheet() +
0207             " QPushButton:checked { "
0208                     "color: " + textColor.name() + "; "
0209                     "border-color: " + borderColor + "; "
0210                     "background-color: " + wrongBackground +
0211                                 " }"
0212         );
0213     }
0214     for (QPushButton *pushButton : qAsConst(m_choiceButtons)) {
0215         pushButton->setEnabled(false);
0216     }
0217     m_ui->solutionPronunciationLabel->setVisible(m_ui->solutionPronunciationLabel->isEnabled());
0218     m_ui->solutionSoundButton->setVisible(m_ui->solutionSoundButton->isEnabled());
0219 }
0220 
0221 QVariant MultiplechoiceModeWidget::userInput()
0222 {
0223     int i = 0;
0224     for (QPushButton *pushButton : qAsConst(m_choiceButtons)) {
0225         if (pushButton->isChecked())
0226             return i;
0227         i++;
0228     }
0229 
0230     return QVariant();
0231 }
0232 
0233 void MultiplechoiceModeWidget::setQuestionSound(const QUrl &soundUrl)
0234 {
0235     m_ui->questionSoundButton->setSoundFile(soundUrl);
0236 }
0237 
0238 void MultiplechoiceModeWidget::setSolutionSound(const QUrl &soundUrl)
0239 {
0240     m_ui->solutionSoundButton->setSoundFile(soundUrl);
0241 }
0242 
0243 void MultiplechoiceModeWidget::setSolutionPronunciation(const QString &pronunciationText)
0244 {
0245     m_ui->solutionPronunciationLabel->setText('[' + pronunciationText + ']');
0246     m_ui->solutionPronunciationLabel->setEnabled(!pronunciationText.isNull());
0247 }
0248 
0249 void MultiplechoiceModeWidget::setQuestionPronunciation(const QString &pronunciationText)
0250 {
0251     m_ui->questionPronunciationLabel->setText('[' + pronunciationText + ']');
0252     m_ui->questionPronunciationLabel->setEnabled(!pronunciationText.isNull());
0253 }
0254 
0255 void MultiplechoiceModeWidget::resetButtonStyleSheet()
0256 {
0257     // Define default QPushButton StyleSheet
0258     const QColor textColor = palette().color(QPalette::WindowText);
0259     // Set border to text color with light transparency
0260     const QString borderColor = QStringLiteral("#90") + palette().color(QPalette::WindowText).name().remove(0, 1);
0261     const QString defaultStyleSheet =
0262         "QPushButton { text-align: left; "
0263                       "color: " + textColor.name() + "; "
0264                       "padding: 5px; "
0265                       "border-color: #00FFFFFF; " // Make border transparent
0266                       "border-style: solid; "
0267                       "border-width: 1px; "
0268                       "border-radius: 4px; "
0269                       "font-style: " +
0270                         m_solutionFont.styleName() + "; " +
0271                       "font-weight: " +
0272                         QString::number(m_solutionFont.weight()) + "; " +
0273                       "font-size: " +
0274                         QString::number(m_solutionFont.pointSize()) + "pt } "+
0275         "QPushButton:hover { "
0276                       "border-color: " + borderColor + " } "
0277         "QPushButton:focus { "
0278                       "color: " + textColor.name() + " } "
0279         "QPushButton:focus:!hover { "
0280                       "border-style: dashed; "
0281                       "border-color: " + borderColor + " }";
0282 
0283     if (!m_choiceButtons.isEmpty()) {
0284         for (QPushButton *pushButton : qAsConst(m_choiceButtons)) {
0285             pushButton->setStyleSheet(defaultStyleSheet);
0286         }
0287     }
0288 }
0289 
0290 #include "moc_multiplechoicemodewidget.cpp"