From e2111edc6f906776fb107838c7d19c8ae7ad1005 Mon Sep 17 00:00:00 2001 From: SeeLook <SeeLook@localhost> Date: Mon, 1 Jan 2018 19:56:36 +0100 Subject: [PATCH] Checking answer (partially), displaying results so added ResultTip.qml and all signals. Restore tresulttexts and textrans --- src/libs/core/CMakeLists.txt | 6 +- src/libs/core/core.pro | 30 ++-- src/libs/core/exam/textrans.h | 70 ++++++-- src/libs/core/exam/tresulttext.cpp | 14 +- src/libs/core/exam/tresulttext.h | 16 +- src/main/texamexecutor.cpp | 247 ++++++++++++++--------------- src/main/tmainscoreobject.cpp | 10 ++ src/main/tmainscoreobject.h | 2 + src/main/ttiphandler.cpp | 122 +++++++------- src/main/ttiphandler.h | 41 +++-- src/nootka.qrc | 1 + src/qml/exam/ExamExecutor.qml | 4 + src/qml/exam/ResultTip.qml | 55 +++++++ 13 files changed, 368 insertions(+), 250 deletions(-) create mode 100644 src/qml/exam/ResultTip.qml diff --git a/src/libs/core/CMakeLists.txt b/src/libs/core/CMakeLists.txt index e9343c910..06b5b4850 100644 --- a/src/libs/core/CMakeLists.txt +++ b/src/libs/core/CMakeLists.txt @@ -48,16 +48,14 @@ set(LIB_NOOTKACORE_SRC instruments/tbandoneonbg.cpp instruments/tsaxbg.cpp -# Android/tmobilemenu.cpp - exam/tqatype.cpp exam/tqaunit.cpp exam/tqagroup.cpp exam/tattempt.cpp exam/tlevel.cpp exam/texam.cpp -# exam/textrans.h -# exam/tresulttext.cpp + exam/textrans.h + exam/tresulttext.cpp ) diff --git a/src/libs/core/core.pro b/src/libs/core/core.pro index 9b1c06f9d..5a6806dc3 100644 --- a/src/libs/core/core.pro +++ b/src/libs/core/core.pro @@ -15,13 +15,13 @@ SOURCES += tcolor.cpp\ tnootkaqml.cpp\ taction.cpp\ \ - exam/tattempt.cpp\ - exam/texam.cpp\ - exam/tlevel.cpp\ - exam/tqagroup.cpp\ - exam/tqatype.cpp\ - exam/tqaunit.cpp\ -# exam/tresulttext.cpp\ + exam/tattempt.cpp\ + exam/texam.cpp\ + exam/tlevel.cpp\ + exam/tqagroup.cpp\ + exam/tqatype.cpp\ + exam/tqaunit.cpp\ + exam/tresulttext.cpp\ \ music/tchunk.cpp\ music/tclef.cpp\ @@ -71,14 +71,14 @@ HEADERS += nootkaconfig.h\ tnootkaqml.h\ taction.h\ \ - exam/tattempt.h\ - exam/texam.h\ - exam/textrans.h\ - exam/tlevel.h\ - exam/tqagroup.h\ - exam/tqatype.h\ - exam/tqaunit.h\ -# exam/tresulttext.h\ + exam/tattempt.h\ + exam/texam.h\ + exam/textrans.h\ + exam/tlevel.h\ + exam/tqagroup.h\ + exam/tqatype.h\ + exam/tqaunit.h\ + exam/tresulttext.h\ \ music/tchunk.h\ music/tclef.h\ diff --git a/src/libs/core/exam/textrans.h b/src/libs/core/exam/textrans.h index 7d3f4b3a6..920860757 100644 --- a/src/libs/core/exam/textrans.h +++ b/src/libs/core/exam/textrans.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2014 by Tomasz Bojczuk * + * Copyright (C) 2014-2018 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -35,60 +35,96 @@ public: TexTrans() {}; + /** + * Average time taken to answer + */ static const QString averAnsverTimeTxt() { return QApplication::translate("TexamView", "Average time taken to answer"); - } /** Average time taken to answer */ + } static const QString inSecondsTxt() { return QApplication::translate("TexamView", "[in seconds]"); } // [in seconds] static const QString totalTimetxt() { return QApplication::translate("TexamView", "Total time"); } // Total time static const QString effectTxt() { return QApplication::translate("TexamView", "Effectiveness"); } // Effectiveness + /** + * Number of correct answers + */ static const QString corrAnswersNrTxt() { return QApplication::translate("TexamView", "Number of correct answers"); - } /** Number of correct answers */ + } static const QString reactTimeTxt() { return QApplication::translate("TexamView", "Time for an answer"); } // Time for an answer static const QString mistakesNrTxt() { return QApplication::translate("TexamView", "Number of mistakes"); } // Number of mistakes + /** + * Number of not bad answers + */ static const QString halfMistakenTxt() { return QApplication::translate("TexamView", "'Not bad' answers"); - } /** Number of not bad answers */ + } + /** + * (counted as half of a mistake) + */ static const QString halfMistakenAddTxt() { return QApplication::translate("TexamView", "(counted as half of a mistake)"); - } /** (counted as half of a mistake) */ + } static const QString examFilterTxt() { return QApplication::translate("TstartExamDlg", "Exam results") + " (*.noo)" ; } + /** + * Load an exam file + */ static const QString loadExamFileTxt() { return QApplication::translate("TstartExamDlg", "Load an exam file"); - } /** Load an exam file */ + } + /** + * Get more levels <a>from Nootka home page</a> + */ static QString moreLevelLinkTxt() { return QApplication::translate( "levelSettings" ,"Get more levels <a href=\"%1\">from Nootka home page</a>") .arg("http://www.nootka.sf.net/index.php?C=down#levels"); - } /** Get more levels <a>from Nootka home page</a> */ - -static QString playMelodyTxt() { return QApplication::translate("Texam", "play melody"); } /** play melody */ - -static QString writeMelodyTxt() { return QApplication::translate("Texam", "write melody"); } /** write melody */ - -static QString attemptTxt() { return QApplication::translate("Texam", "attempt"); } /** attempt */ - + } + + /** + * play melody + */ +static QString playMelodyTxt() { return QApplication::translate("Texam", "play melody"); } + + /** + * write melody + */ +static QString writeMelodyTxt() { return QApplication::translate("Texam", "write melody"); } + + /** + * attempt + */ +static QString attemptTxt() { return QApplication::translate("Texam", "attempt"); } + + /** + * 7 attempts (or other number given as a parameter) + */ static QString attemptsTxt(int aCount) { return QApplication::translate("Texam", "%n attempt(s)", "like: '1 attempt' or '121 attempts'", aCount); - } /** 7 attempts (or other number given as a parameter) */ + } + /** + * Play a melody written in a score + */ static QString playDescTxt() { return QApplication::translate("Texam", "Play a melody written in a score"); - } /** Play a melody written in a score */ + } + /** + * Listen to a melody and write it on a score + */ static QString writeDescTxt() { return QApplication::translate("Texam", "Listen to a melody and write it on a score"); - } /** Listen to a melody and write it on a score */ + } }; diff --git a/src/libs/core/exam/tresulttext.cpp b/src/libs/core/exam/tresulttext.cpp index cbf208b11..8359ced60 100644 --- a/src/libs/core/exam/tresulttext.cpp +++ b/src/libs/core/exam/tresulttext.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2015-2016 by Tomasz Bojczuk * + * Copyright (C) 2015-2018 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -23,7 +23,9 @@ #include <QtGui/qcolor.h> - /** Adds comma and space ', ' (',<br>' on Android) to not empty string or returns the same. */ + /** + * Adds comma and space ', ' (',<br>' on Android) to not empty string or returns the same. + */ void addSpaceToNotEmpty(QString& txt) { if (!txt.isEmpty()) { #if defined (Q_OS_ANDROID) @@ -45,12 +47,8 @@ void newLineText(QString& txt, const QString& newText) { } -QString wasAnswerOKtext(TQAunit* answer, const QColor& textColor, int fontSize, int attempt) { +QString wasAnswerOKtext(TQAunit* answer, int attempt) { QString txt; - if (fontSize != -1) - txt = QString("<span style=\"color: %1; font-size: %2px;\">").arg(textColor.name()).arg(fontSize); - else - txt = QString("<span style=\"color: %1;\">").arg(textColor.name()); TQAunit curQ; if (answer->melody() && attempt > 0 && attempt <= answer->attemptsCount()) curQ.setMistake(answer->attempt(attempt - 1)->summary()); @@ -94,7 +92,7 @@ QString wasAnswerOKtext(TQAunit* answer, const QColor& textColor, int fontSize, } txt += misMes; } - txt += QLatin1String("</span><br>"); +// txt += QLatin1String("</span><br>"); return txt; } diff --git a/src/libs/core/exam/tresulttext.h b/src/libs/core/exam/tresulttext.h index 6955c72f6..4c7b6ee0d 100644 --- a/src/libs/core/exam/tresulttext.h +++ b/src/libs/core/exam/tresulttext.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2015 by Tomasz Bojczuk * + * Copyright (C) 2015-2018 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -21,15 +21,17 @@ #define TRESULTTEXT_H #include <nootkacoreglobal.h> -#include <QString> +#include <QtCore/qstring.h> class TQAunit; class QColor; - /** Returns HTML formatted text with answer details. - * If @p fontSize remains default - default @p fontSize is taken. - * When @p attempt is bigger than 0 (and answer was a melody of course) - * The summary of that attempt is prepared. */ -NOOTKACORE_EXPORT QString wasAnswerOKtext(TQAunit* answer, const QColor& textColor, int fontSize = -1, int attempt = 0); + /** + * Returns HTML formatted text with answer details. + * If @p fontSize remains default - default @p fontSize is taken. + * When @p attempt is bigger than 0 (and answer was a melody of course) + * The summary of that attempt is prepared. + */ +NOOTKACORE_EXPORT QString wasAnswerOKtext(TQAunit* answer, int attempt = 0); #endif // TRESULTTEXT_H diff --git a/src/main/texamexecutor.cpp b/src/main/texamexecutor.cpp index a4974b989..41f788609 100755 --- a/src/main/texamexecutor.cpp +++ b/src/main/texamexecutor.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2011-2017 by Tomasz Bojczuk * + * Copyright (C) 2011-2018 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -33,7 +33,6 @@ // #include <exam/textrans.h> #include <exam/tattempt.h> #include "texamhelp.h" -// #include <help/texpertanswerhelp.h> #include <taudioparams.h> #include <texamparams.h> #include <tscoreparams.h> @@ -580,143 +579,143 @@ void TexamExecutor::checkAnswer(bool showResults) { if (m_exam->melodies() && SOUND->melodyIsPlaying()) SOUND->stopPlaying(); -// if (!GLOB->E->autoNextQuest || m_exercise) -// TOOLBAR->startExamAct->setDisabled(false); + if (!GLOB->E->autoNextQuest || m_exercise) + m_stopExamAct->setEnabled(true); m_isAnswered = true; // Let's check -// Tnote questNote, answNote, userNote; // example note & returned note -// // At first we determine what has to be checked -// if (!curQ->melody()) { -// questNote = curQ->qa.note; -// if (curQ->answerAsNote()) { -// if (m_level.manualKey) { -// if (SCORE->keySignature().value() != curQ->key.value()) -// curQ->setMistake(TQAunit::e_wrongKey); -// } -// if (curQ->questionAsNote()) -// questNote = curQ->qa_2.note; -// answNote = SCORE->getNote(0); -// } -// if (curQ->answerAsName()) { -// if (curQ->questionAsName()) -// questNote = curQ->qa_2.note; -// m_prevNoteIfName = NOTENAME->getNoteName(); // store note to restore it if will be repeated -// answNote = NOTENAME->getNoteName(); -// } -// userNote = answNote; -// if (curQ->answerAsSound()) { -// answNote = SOUND->note(); + Tnote questNote, answNote, userNote; // example note & returned note +// At first we determine what has to be checked + if (!curQ->melody()) { + questNote = curQ->qa.note; + if (curQ->answerAsNote()) { + if (m_level.manualKey) { + if (MAIN_SCORE->keySignatureValue() != curQ->key.value()) + curQ->setMistake(TQAunit::e_wrongKey); + } + if (curQ->questionAsNote()) + questNote = curQ->qa_2.note; + answNote = MAIN_SCORE->getNote(0); + } + if (curQ->answerAsName()) { + if (curQ->questionAsName()) + questNote = curQ->qa_2.note; + m_prevNoteIfName = NOTENAME->note(); // store note to restore it if will be repeated + answNote = NOTENAME->note(); + } + userNote = answNote; + if (curQ->answerAsSound()) { + answNote = SOUND->note(); // if ((TintonationView::Eaccuracy)m_level.intonation != TintonationView::e_noCheck) { // if (TnoteStruct(Tnote(), SOUND->pitch()).inTune(TintonationView::getThreshold(m_level.intonation))) // curQ->setMistake(TQAunit::e_wrongIntonation); // } -// } -// } -// // Now we can check -// if (curQ->answerAsFret()) { // 1. Comparing positions -// TfingerPos answPos, questPos; + } + } +// Now we can check + if (curQ->answerAsFret()) { // 1. Comparing positions + TfingerPos answPos, questPos; // answPos = INSTRUMENT->getfingerPos(); -// if (curQ->questionAsFret()) { -// if (answPos == curQ->qa.pos) { // check has not user got answer the same as question position -// curQ->setMistake(TQAunit::e_wrongPos); -// qDebug("Cheater!"); -// } else -// questPos = curQ->qa_2.pos; -// } else -// questPos = curQ->qa.pos; -// if (questPos != answPos && curQ->isCorrect()) { // if no cheater give him a chance -// QList <TfingerPos> tmpPosList; // Maybe hi gave correct note but on incorrect string only -// m_supp->getTheSamePosNoOrder(answPos, tmpPosList); // get other positions -// bool otherPosFound = false; -// for (int i = 0; i < tmpPosList.size(); i++) { -// if (tmpPosList[i] == questPos) { // and compare it with expected -// otherPosFound = true; -// break; -// } -// } -// if (otherPosFound) { -// if (m_level.showStrNr || answPos.fret() < m_level.loFret || answPos.fret() > m_level.hiFret) -// curQ->setMistake(TQAunit::e_wrongString); // check the level settings and mark as wrong string when deserve -// } else -// curQ->setMistake(TQAunit::e_wrongPos); -// } -// } else { -// if (curQ->melody()) { // 2. or checking melodies -// curQ->setMistake(TQAunit::e_correct); // reset an answer -// if (curQ->answerAsNote()) { // dictation -// Tmelody answMelody; + if (curQ->questionAsFret()) { + if (answPos == curQ->qa.pos) { // check has not user got answer the same as question position + curQ->setMistake(TQAunit::e_wrongPos); + qDebug("Cheater!"); + } else + questPos = curQ->qa_2.pos; + } else + questPos = curQ->qa.pos; + if (questPos != answPos && curQ->isCorrect()) { // if no cheater give him a chance + QList <TfingerPos> tmpPosList; // Maybe hi gave correct note but on incorrect string only + m_supp->getTheSamePosNoOrder(answPos, tmpPosList); // get other positions + bool otherPosFound = false; + for (int i = 0; i < tmpPosList.size(); i++) { + if (tmpPosList[i] == questPos) { // and compare it with expected + otherPosFound = true; + break; + } + } + if (otherPosFound) { + if (m_level.showStrNr || answPos.fret() < m_level.loFret || answPos.fret() > m_level.hiFret) + curQ->setMistake(TQAunit::e_wrongString); // check the level settings and mark as wrong string when deserve + } else + curQ->setMistake(TQAunit::e_wrongPos); + } + } else { + if (curQ->melody()) { // 2. or checking melodies + curQ->setMistake(TQAunit::e_correct); // reset an answer + if (curQ->answerAsNote()) { // dictation + Tmelody answMelody; // SCORE->getMelody(&answMelody); -// m_supp->compareMelodies(curQ->melody(), &answMelody, curQ->lastAttempt()); -// } else { // playing a score -// m_supp->compareMelodies(curQ->melody(), m_melody->listened(), curQ->lastAttempt()); -// } -// int goodAllready = 0, notBadAlready = 0, wrongAlready = 0; -// for (int i = 0; i < curQ->lastAttempt()->mistakes.size(); ++i) { // setting mistake type in TQAunit -// if (curQ->lastAttempt()->mistakes[i] == TQAunit::e_correct) { -// goodAllready++; -// continue; // it was correct - skip -// } -// if (curQ->lastAttempt()->mistakes[i] & TQAunit::e_wrongNote) { -// wrongAlready++; -// } else // or 'not bad' -// notBadAlready++; -// } -// if (goodAllready == curQ->melody()->length()) { // all required notes are correct -// curQ->setMistake(TQAunit::e_correct); // even if user put them more and effect. is poor -// // qDebug() << "Melody is correct"; -// } else if (goodAllready + notBadAlready == curQ->melody()->length()) { // add committed mistakes of last attempt -// curQ->setMistake(curQ->lastAttempt()->summary()); // or 'not bad' -// // qDebug() << "Melody is not bad" << curQ->mistake(); -// } else if (goodAllready + notBadAlready >= curQ->melody()->length() * 0.7) { // at least 70% notes answered properly -// // qDebug() << "Melody has little notes"; -// if (curQ->lastAttempt()->effectiveness() >= 50.0) { // and effectiveness is sufficient -// curQ->setMistake(curQ->lastAttempt()->summary()); -// curQ->setMistake(TQAunit::e_littleNotes); // but less notes than required -// // qDebug() << "... and sufficient effectiveness"; -// } else { // or effectiveness is too poor -// curQ->setMistake(TQAunit::e_veryPoor); -// // qDebug() << "... but very poor effectiveness" << curQ->lastAttempt()->effectiveness(); -// } -// } else { -// curQ->setMistake(TQAunit::e_wrongNote); -// // qDebug() << "Simply wrong answer"; -// } -// if (m_level.manualKey && !curQ->isWrong()) { -// if (SCORE->keySignature().value() != curQ->key.value()) -// curQ->setMistake(TQAunit::e_wrongKey); -// } -// // Another case is poor or very poor effectiveness but it is obtained after effect. update in sumarizeAnswer() -// } else { // 3. or checking are the notes the same -// m_supp->checkNotes(curQ, questNote, answNote, m_answRequire.octave, m_answRequire.accid); -// if (!m_answRequire.accid && curQ->isCorrect() && (curQ->answerAsNote() || curQ->answerAsName())) { -// // Save user given note when it is correct and accidental was not forced to respect kind of accidental -// if (curQ->questionAs == curQ->answerAs) -// curQ->qa_2.note = userNote; -// else -// curQ->qa.note = userNote; -// } -// } -// } -// m_penalty->checkAnswer(); -// -// disableWidgets(); -// bool autoNext = GLOB->E->autoNextQuest; -// if (GLOB->E->afterMistake == TexamParams::e_stop && !curQ->isCorrect()) -// autoNext = false; // when mistake and e_stop - the same like autoNext = false; -// -// if (showResults) { -// m_tipHandler->resultTip(curQ); // tip duration is calculated by itself (inside resultTip() method) + m_supp->compareMelodies(curQ->melody(), &answMelody, curQ->lastAttempt()); + } else { // playing a score + m_supp->compareMelodies(curQ->melody(), m_melody->listened(), curQ->lastAttempt()); + } + int goodAllready = 0, notBadAlready = 0, wrongAlready = 0; + for (int i = 0; i < curQ->lastAttempt()->mistakes.size(); ++i) { // setting mistake type in TQAunit + if (curQ->lastAttempt()->mistakes[i] == TQAunit::e_correct) { + goodAllready++; + continue; // it was correct - skip + } + if (curQ->lastAttempt()->mistakes[i] & TQAunit::e_wrongNote) { + wrongAlready++; + } else // or 'not bad' + notBadAlready++; + } + if (goodAllready == curQ->melody()->length()) { // all required notes are correct + curQ->setMistake(TQAunit::e_correct); // even if user put them more and effect. is poor +// qDebug() << "Melody is correct"; + } else if (goodAllready + notBadAlready == curQ->melody()->length()) { // add committed mistakes of last attempt + curQ->setMistake(curQ->lastAttempt()->summary()); // or 'not bad' +// qDebug() << "Melody is not bad" << curQ->mistake(); + } else if (goodAllready + notBadAlready >= curQ->melody()->length() * 0.7) { // at least 70% notes answered properly +// qDebug() << "Melody has little notes"; + if (curQ->lastAttempt()->effectiveness() >= 50.0) { // and effectiveness is sufficient + curQ->setMistake(curQ->lastAttempt()->summary()); + curQ->setMistake(TQAunit::e_littleNotes); // but less notes than required +// qDebug() << "... and sufficient effectiveness"; + } else { // or effectiveness is too poor + curQ->setMistake(TQAunit::e_veryPoor); +// qDebug() << "... but very poor effectiveness" << curQ->lastAttempt()->effectiveness(); + } + } else { + curQ->setMistake(TQAunit::e_wrongNote); +// qDebug() << "Simply wrong answer"; + } + if (m_level.manualKey && !curQ->isWrong()) { + if (MAIN_SCORE->keySignatureValue() != curQ->key.value()) + curQ->setMistake(TQAunit::e_wrongKey); + } + // Another case is poor or very poor effectiveness but it is obtained after effect. update in sumarizeAnswer() + } else { // 3. or checking are the notes the same + m_supp->checkNotes(curQ, questNote, answNote, m_answRequire.octave, m_answRequire.accid); + if (!m_answRequire.accid && curQ->isCorrect() && (curQ->answerAsNote() || curQ->answerAsName())) { + // Save user given note when it is correct and accidental was not forced to respect kind of accidental + if (curQ->questionAs == curQ->answerAs) + curQ->qa_2.note = userNote; + else + curQ->qa.note = userNote; + } + } + } + m_penalty->checkAnswer(); + + disableWidgets(); + bool autoNext = GLOB->E->autoNextQuest; + if (GLOB->E->afterMistake == TexamParams::e_stop && !curQ->isCorrect()) + autoNext = false; // when mistake and e_stop - the same like autoNext = false; + + if (showResults) { + m_tipHandler->resultTip(curQ); // tip duration is calculated by itself (inside resultTip() method) // if ((!m_exercise || (m_exercise && curQ->isCorrect())) && !autoNext) // m_tipHandler->whatNextTip(curQ->isCorrect()); -// if (!autoNext) { + if (!autoNext) { // if (!curQ->isCorrect() && !m_exercise && !curQ->melody()) // TOOLBAR->addAction(TOOLBAR->prevQuestAct); // if (!curQ->isCorrect() && curQ->melody()) // TOOLBAR->addAction(TOOLBAR->attemptAct); // TOOLBAR->addAction(TOOLBAR->nextQuestAct); -// } -// } -// + } + } + // markAnswer(curQ); // int waitTime = GLOB->E->questionDelay; // if (m_melody) // increase minimal delay before next question for melodies to 500ms diff --git a/src/main/tmainscoreobject.cpp b/src/main/tmainscoreobject.cpp index 8db6753bc..45ad035ed 100644 --- a/src/main/tmainscoreobject.cpp +++ b/src/main/tmainscoreobject.cpp @@ -130,8 +130,18 @@ void TmainScoreObject::clearScore() { m_scoreObj->setBgColor(qApp->palette().base().color()); } + void TmainScoreObject::setKeySignature(const TkeySignature& key) { m_scoreObj->setKeySignature(static_cast<int>(key.value())); } +char TmainScoreObject::keySignatureValue() { + return static_cast<char>(m_scoreObj->keySignature()); +} + + +Tnote TmainScoreObject::getNote(int id) { + return m_scoreObj->noteAt(id); +} + void TmainScoreObject::askQuestion(Tmelody* mel) { m_scoreObj->setBgColor(scoreBackgroundColor(GLOB->EquestionColor, 20)); diff --git a/src/main/tmainscoreobject.h b/src/main/tmainscoreobject.h index f8a05d394..0b0a4ab1a 100644 --- a/src/main/tmainscoreobject.h +++ b/src/main/tmainscoreobject.h @@ -89,6 +89,8 @@ public: void setReadOnly(bool ro); void clearScore(); void setKeySignature(const TkeySignature& key); + char keySignatureValue(); + Tnote getNote(int id); // exam/exercise related void askQuestion(const Tnote& note, char realStr = 0); diff --git a/src/main/ttiphandler.cpp b/src/main/ttiphandler.cpp index 2a27cc278..5dc5e025d 100644 --- a/src/main/ttiphandler.cpp +++ b/src/main/ttiphandler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2012-2017 by Tomasz Bojczuk * + * Copyright (C) 2012-2018 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -34,6 +34,7 @@ #include "tnameitem.h" #include <tsound.h> #include <tpath.h> +#include <texamparams.h> #if defined (Q_OS_ANDROID) #include <tmtr.h> #else @@ -137,7 +138,7 @@ void TtipHandler::setTipPos(const QPointF& p) { } -//void Tcanvas::setStatusMessage(const QString& text, int duration) { +//void TtipHandler::setStatusMessage(const QString& text, int duration) { //#if defined (Q_OS_ANDROID) // tMessage->setMessage(text, duration); //#else @@ -159,49 +160,41 @@ int TtipHandler::bigFont() { } -//void Tcanvas::resultTip(TQAunit* answer, int time) { -// clearConfirmTip(); -// clearResultTip(); -// clearTryAgainTip(); - -// bool autoNext = Tcore::gl()->E->autoNextQuest; -// if (Tcore::gl()->E->afterMistake == TexamParams::e_stop && !answer->isCorrect()) -// autoNext = false; // when mistake and e_stop - the same like autoNext = false; -// if (autoNext) { // determine time of displaying -// if (answer->isCorrect() || Tcore::gl()->E->afterMistake == TexamParams::e_continue) -// time = 2500; // hard-coded -// else -// time = Tcore::gl()->E->mistakePreview; // user defined wait time -// } - -//#if defined (Q_OS_ANDROID) -// int bf = qMin(Tmtr::shortScreenSide() / 10, qRound(Tmtr::fingerPixels() * 2.0)); -//#else -// int bf = bigFont(); -//#endif -// m_resultTip = new TgraphicsTextTip(wasAnswerOKtext(answer, TexecutorSupply::answerColor(answer->mistake()), bf)); -// m_scene->addItem(m_resultTip); -// m_resultTip->setZValue(100); - +void TtipHandler::resultTip(TQAunit* answer, int time) { + m_timerToConfirm->stop(); + clearCanvas(); + + bool autoNext = GLOB->E->autoNextQuest; + if (GLOB->E->afterMistake == TexamParams::e_stop && !answer->isCorrect()) + autoNext = false; // when mistake and e_stop - the same like autoNext = false; + if (autoNext) { // determine time of displaying + if (answer->isCorrect() || GLOB->E->afterMistake == TexamParams::e_continue) + time = 2500; // hard-coded + else + time = GLOB->E->mistakePreview; // user defined wait time + } + + emit showResultTip(wasAnswerOKtext(answer), TexecutorSupply::answerColor(answer->mistake())); + // if (answer->isNotSoBad()) // m_resultTip->setScale(m_scale); // else // m_resultTip->setScale(m_scale * 1.2); // setResultPos(); -// if (Tcore::gl()->E->showWrongPlayed && Tcore::gl()->E->showWrongPlayed && !answer->melody() && +// if (GLOB->E->showWrongPlayed && GLOB->E->showWrongPlayed && !answer->melody() && // answer->answerAsSound() && !answer->isCorrect() && SOUND->note().note) // detectedNoteTip(SOUND->note()); // In exercise mode display detected note when it was incorrect -// if (time) -// QTimer::singleShot(time, this, SLOT(clearResultTip())); -//} + if (time) + QTimer::singleShot(time, [=]{ emit destroyResultTip(); }); +} -//QString Tcanvas::detectedText(const QString& txt) { -// return QString("<span style=\"color: %1;\"><big>").arg(Tcore::gl()->EquestionColor.name()) + txt + QLatin1String("</big></span>"); +//QString TtipHandler::detectedText(const QString& txt) { +// return QString("<span style=\"color: %1;\"><big>").arg(GLOB->EquestionColor.name()) + txt + QLatin1String("</big></span>"); //} -//void Tcanvas::detectedNoteTip(const Tnote& note) { +//void TtipHandler::detectedNoteTip(const Tnote& note) { // Tnote n = note; // if (n.isValid()) // setStatusMessage(QLatin1String("<table valign=\"middle\" align=\"center\"><tr><td> ") + @@ -210,9 +203,9 @@ int TtipHandler::bigFont() { //} -//void Tcanvas::tryAgainTip(int time) { +//void TtipHandler::tryAgainTip(int time) { // m_tryAgainTip = new TgraphicsTextTip(QString("<span style=\"color: %1; font-size: %2px;\">") -// .arg(Tcore::gl()->EquestionColor.name()).arg(bigFont()) + tr("Try again!") + "</span>"); +// .arg(GLOB->EquestionColor.name()).arg(bigFont()) + tr("Try again!") + "</span>"); // m_scene->addItem(m_tryAgainTip); // m_tryAgainTip->setZValue(100); // m_tryAgainTip->setScale(m_scale); @@ -245,7 +238,7 @@ void TtipHandler::startTip() { } -//void Tcanvas::certificateTip() { +//void TtipHandler::certificateTip() { // if (m_certifyTip) // return; @@ -259,7 +252,7 @@ void TtipHandler::startTip() { //} -//void Tcanvas::whatNextTip(bool isCorrect, bool toCorrection) { +//void TtipHandler::whatNextTip(bool isCorrect, bool toCorrection) { // delete m_questionTip; // clearWhatNextTip(); //#if defined (Q_OS_ANDROID) @@ -288,7 +281,7 @@ void TtipHandler::startTip() { // }); // } // if (toCorrection) { -// m_correctTip = new ThackedTouchTip(getTipText("correct", "Correct"), Tcore::gl()->EanswerColor); +// m_correctTip = new ThackedTouchTip(getTipText("correct", "Correct"), GLOB->EanswerColor); // m_scene->addItem(m_correctTip); // m_correctTip->setFont(smalTipFont(m_view)); // connect(m_correctTip, &ThackedTouchTip::clicked, [=] { @@ -336,11 +329,14 @@ void TtipHandler::startTip() { void TtipHandler::confirmTip(int time) { + if (!m_confirmTipOn) { #if defined (Q_OS_ANDROID) - showConfirmTip(); + showConfirmTip(); #else - m_timerToConfirm->start(time + 1); // add 1 to show it immediately when time = 0 + m_timerToConfirm->start(time + 1); // add 1 to show it immediately when time = 0 #endif + m_confirmTipOn = true; + } } @@ -357,7 +353,7 @@ void TtipHandler::showConfirmTip() { } -//void Tcanvas::playMelodyAgainMessage() { +//void TtipHandler::playMelodyAgainMessage() { //#if defined (Q_OS_ANDROID) // tMessage->setMessage(detectedText(tr("Select any note to play it again.")), 3000); //#else @@ -367,6 +363,7 @@ void TtipHandler::showConfirmTip() { void TtipHandler::questionTip() { + emit destroyResultTip(); QString br = QStringLiteral("<br>"); QString sp = QStringLiteral(" "); QString questText; @@ -496,7 +493,7 @@ void TtipHandler::questionTip() { } -//void Tcanvas::outOfTuneTip(float pitchDiff) { +//void TtipHandler::outOfTuneTip(float pitchDiff) { // if (m_outTuneTip) // return; // QString tuneText; @@ -508,7 +505,7 @@ void TtipHandler::questionTip() { // tooLow = false; // } // m_outTuneTip = new TgraphicsTextTip(QString("<span style=\"color: %1; font-size: %2px;\">") -// .arg(Tcore::gl()->EanswerColor.name()).arg(bigFont()) + tuneText + "</span>"); +// .arg(GLOB->EanswerColor.name()).arg(bigFont()) + tuneText + "</span>"); // m_scene->addItem(m_outTuneTip); // m_outTuneTip->setZValue(100); // m_outTuneTip->setScale(m_scale); @@ -517,12 +514,12 @@ void TtipHandler::questionTip() { //} -//void Tcanvas::melodyCorrectMessage() { +//void TtipHandler::melodyCorrectMessage() { // if (m_melodyCorrectMessage) // return; // m_melodyCorrectMessage = true; -// QString message = QString("<span style=\"color: %1;\"><big>").arg(Tcore::gl()->EanswerColor.name()) + +// QString message = QString("<span style=\"color: %1;\"><big>").arg(GLOB->EanswerColor.name()) + // tr("Click incorrect notes to see<br>and to listen to them corrected.") + QLatin1String("</big></span>"); //#if defined (Q_OS_ANDROID) // tMessage->setMessage(message, 10000); // temporary message on a tip @@ -534,13 +531,13 @@ void TtipHandler::questionTip() { ///** @p prevTime param is to call clearing method after this time. */ -//void Tcanvas::correctToGuitar(TQAtype::Etype &question, int prevTime, TfingerPos& goodPos) { +//void TtipHandler::correctToGuitar(TQAtype::Etype &question, int prevTime, TfingerPos& goodPos) { // if (m_correctAnim) // return; // m_goodPos = goodPos; // m_flyEllipse = new QGraphicsEllipseItem; // m_flyEllipse->setPen(Qt::NoPen); -// m_flyEllipse->setBrush(QBrush(QColor(Tcore::gl()->EquestionColor.name()))); +// m_flyEllipse->setBrush(QBrush(QColor(GLOB->EquestionColor.name()))); // m_scene->addItem(m_flyEllipse); // if (question == TQAtype::e_asNote) { // m_flyEllipse->setRect(SCORE->noteRect(1)); // 1 - answer note segment @@ -558,7 +555,7 @@ void TtipHandler::questionTip() { // m_correctAnim->setDuration(600); // connect(m_correctAnim, SIGNAL(finished()), this, SLOT(correctAnimFinished())); // QPointF destP = m_view->mapToScene(GUITAR->mapToParent(GUITAR->mapFromScene(GUITAR->fretToPos(goodPos)))); -// if (!Tcore::gl()->GisRightHanded) { // fix destination position point for left-handed guitars +// if (!GLOB->GisRightHanded) { // fix destination position point for left-handed guitars // if (goodPos.fret()) // destP.setX(destP.x() - GUITAR->fingerRect().width()); // else @@ -570,7 +567,7 @@ void TtipHandler::questionTip() { // m_correctAnim->setScaling(GUITAR->fingerRect().width() / m_flyEllipse->rect().width(), 2.0); // m_correctAnim->scaling()->setEasingCurveType(QEasingCurve::OutQuint); // } -// m_correctAnim->setColoring(QColor(Tcore::gl()->EanswerColor.name())); +// m_correctAnim->setColoring(QColor(GLOB->EanswerColor.name())); // if (goodPos.fret() == 0) { // QPointF p1(m_view->mapToScene(GUITAR->mapToParent( // GUITAR->mapFromScene(GUITAR->stringLine(goodPos.str()).p1())))); @@ -583,7 +580,7 @@ void TtipHandler::questionTip() { //} -//void Tcanvas::levelStatusMessage() { +//void TtipHandler::levelStatusMessage() { //#if !defined (Q_OS_ANDROID) // QString message; // if (m_exam->isExercise()) @@ -598,6 +595,7 @@ void TtipHandler::questionTip() { void TtipHandler::clearCanvas() { + m_confirmTipOn = false; emit destroyTips(); // clearConfirmTip(); // clearResultTip(); @@ -619,7 +617,7 @@ void TtipHandler::clearCanvas() { -//void Tcanvas::clearCertificate() { +//void TtipHandler::clearCertificate() { // if (m_certifyTip) { // m_certifyTip->deleteLater(); // m_certifyTip = nullptr; @@ -627,7 +625,7 @@ void TtipHandler::clearCanvas() { //} -//void Tcanvas::clearCorrection() { +//void TtipHandler::clearCorrection() { // if (m_correctAnim) { // m_correctAnim->deleteLater(); // m_correctAnim = 0; @@ -641,7 +639,7 @@ void TtipHandler::clearCanvas() { -//void Tcanvas::clearMelodyCorrectMessage() { +//void TtipHandler::clearMelodyCorrectMessage() { // if (m_melodyCorrectMessage) { // m_melodyCorrectMessage = false; // levelStatusMessage(); @@ -653,16 +651,16 @@ void TtipHandler::clearCanvas() { ////################### PROTECTED ############################################ ////################################################################################################# -//void Tcanvas::correctAnimFinished() { +//void TtipHandler::correctAnimFinished() { //// clearCorrection(); // m_flyEllipse->hide(); // GUITAR->setFinger(m_goodPos); -// GUITAR->markAnswer(QColor(Tcore::gl()->EanswerColor.name())); +// GUITAR->markAnswer(QColor(GLOB->EanswerColor.name())); // m_view->update(); //} -//bool Tcanvas::eventFilter(QObject* obj, QEvent* event) { +//bool TtipHandler::eventFilter(QObject* obj, QEvent* event) { //#if defined (Q_OS_ANDROID) //// if (event->type() == QEvent::KeyPress) { //// auto ke = static_cast<QKeyEvent*>(event); @@ -701,7 +699,7 @@ QPointF TtipHandler::getTipPosition(TtipHandler::EtipPos tp) { } -//void Tcanvas::setTryAgainPos() { +//void TtipHandler::setTryAgainPos() { // QPointF tl(m_scene->width() * 0.6, m_scene->height() * 0.10); // top left of tip area // if (m_resultTip) // place it below result tip // tl.setY(m_resultTip->pos().y() + m_resultTip->realH()); @@ -709,7 +707,7 @@ QPointF TtipHandler::getTipPosition(TtipHandler::EtipPos tp) { //} -//void Tcanvas::setWhatNextPos() { +//void TtipHandler::setWhatNextPos() { //#if defined (Q_OS_ANDROID) // qreal sc = (m_view->height() / 8.0) / m_nextTip->realH(); // if (sc > 1.0) @@ -746,7 +744,7 @@ QPointF TtipHandler::getTipPosition(TtipHandler::EtipPos tp) { //} -//void Tcanvas::setConfirmPos() { // right top corner +//void TtipHandler::setConfirmPos() { // right top corner //#if defined (Q_OS_ANDROID) // qreal sc = (m_view->height() / 8.0) / m_confirmTip->realH(); // if (sc > 1.0) @@ -758,7 +756,7 @@ QPointF TtipHandler::getTipPosition(TtipHandler::EtipPos tp) { //} -//void Tcanvas::createQuestionTip() { +//void TtipHandler::createQuestionTip() { // delete m_questionTip; // qreal scaleFactor = 1.2; //#if defined (Q_OS_ANDROID) // HACK: to keep question big enough on tablet big screens @@ -772,7 +770,7 @@ QPointF TtipHandler::getTipPosition(TtipHandler::EtipPos tp) { //} -//void Tcanvas::setOutTunePos() { +//void TtipHandler::setOutTunePos() { // int startX = SOUND->pitchView()->geometry().x(); // if (m_outTuneTip->realW() > SOUND->pitchView()->geometry().width() / 2) // m_outTuneTip->setScale(m_outTuneTip->realW() / (SOUND->pitchView()->geometry().width() / 2)); @@ -783,7 +781,7 @@ QPointF TtipHandler::getTipPosition(TtipHandler::EtipPos tp) { //} -//void Tcanvas::tipStateChanged() { +//void TtipHandler::tipStateChanged() { // if (sender() == m_questionTip) // m_minimizedQuestion = m_questionTip->isMinimized(); //} diff --git a/src/main/ttiphandler.h b/src/main/ttiphandler.h index 0f6fc4a78..d77d0a43b 100644 --- a/src/main/ttiphandler.h +++ b/src/main/ttiphandler.h @@ -76,19 +76,31 @@ public: // */ // void setStatusMessage(const QString& text, int duration = 0); - void changeExam(Texam* newExam); /**< Replaces exam pointer given in constructor to the new one. */ - -// void resultTip(TQAunit *answer, int time = 0); /**< show was question correct text, hides after given time */ - void startTip(); /**< Text with help on an exam start */ - -// /** -// * Text with what to click after an answer. -// * @p isCorrect - was the question correct -// * @p toCorrection - text how to see corrected answer will be shown. -// */ -// void whatNextTip(bool isCorrect, bool toCorrection = false); - - /** Text with question context */ + /** + * Replaces exam pointer given in constructor to the new one. + */ + void changeExam(Texam* newExam); + + /** + * show was question correct text, hides after given time + */ + void resultTip(TQAunit *answer, int time = 0); + + /** + * Text with help on an exam start + */ + void startTip(); + + /** + * Text with what to click after an answer. + * @p isCorrect - was the question correct + * @p toCorrection - text how to see corrected answer will be shown. + */ + void whatNextTip(bool isCorrect, bool toCorrection = false); + + /** + * Text with question context + */ void questionTip(); // void tryAgainTip(int time); /**< "Try again" text" */ void confirmTip(int time = 0); /**< tip about confirm an answer appears after given time */ @@ -135,6 +147,8 @@ signals: void showStartTip(const QString text, const QColor& color, const QPointF& pos); void showQuestionTip(const QString text, const QPointF& pos); void destroyTips(); + void showResultTip(const QString text, const QColor& color); + void destroyResultTip(); // void buttonClicked(const QString&); /**< This signal is emitted when user click image button (a link) on any tip.*/ // void certificateMagicKeys(); /**< When translator wants to see a certificate preview */ // void correctingFinished(); /**< Emitted when correction animation finish */ @@ -165,6 +179,7 @@ private: EtipPos m_questTipPosType; /**< Kind of question tip position */ int m_iconSize; /**< Icon image size on tips calculated from actual font metrics. */ QPointF m_lastTipPos; + bool m_confirmTipOn = false; private: QPointF getTipPosition(EtipPos tp); diff --git a/src/nootka.qrc b/src/nootka.qrc index ca1452273..816c6c6a4 100644 --- a/src/nootka.qrc +++ b/src/nootka.qrc @@ -105,6 +105,7 @@ <file alias="exam/ResultLabel.qml">qml/exam/ResultLabel.qml</file> <file alias="exam/ExamTip.qml">qml/exam/ExamTip.qml</file> <file alias="exam/QuestionTip.qml">qml/exam/QuestionTip.qml</file> + <file alias="exam/ResultTip.qml">qml/exam/ResultTip.qml</file> <file alias="qtquickcontrols2.conf">qml/qtquickcontrols2.conf</file> <!-- <file alias="+android/qtquickcontrols2.conf">qml/+android/qtquickcontrols2.conf</file> --> diff --git a/src/qml/exam/ExamExecutor.qml b/src/qml/exam/ExamExecutor.qml index 26aa1f125..f9d7265ad 100644 --- a/src/qml/exam/ExamExecutor.qml +++ b/src/qml/exam/ExamExecutor.qml @@ -28,6 +28,10 @@ Texecutor { var s = Qt.createComponent("qrc:/exam/QuestionTip.qml") s.createObject(executor, { "text": text, "offX": pos.x, "offY": pos.y } ) } + onShowResultTip: { + var r = Qt.createComponent("qrc:/exam/ResultTip.qml") + r.createObject(executor, { "text": text, "color": color } ) + } } } diff --git a/src/qml/exam/ResultTip.qml b/src/qml/exam/ResultTip.qml new file mode 100644 index 000000000..67d79f948 --- /dev/null +++ b/src/qml/exam/ResultTip.qml @@ -0,0 +1,55 @@ +/** This file is part of Nootka (http://nootka.sf.net) * + * Copyright (C) 2018 by Tomasz Bojczuk (seelook@gmail.com) * + * on the terms of GNU GPLv3 license (http://www.gnu.org/licenses) */ + +import QtQuick 2.9 +import QtGraphicalEffects 1.0 + + +Item { + id: resultTip + + property alias color: txt.color + property alias text: txt.text + + y: GLOB.useAnimations ? -2 * height : parent.height / 50 + anchors.horizontalCenter: parent.horizontalCenter + + width: txt.width; height: txt.height + + Text { + id: txt + font { pixelSize: (executor.height / 15) * (1 - 0.2 * (lineCount - 1)); family: "Sans"; bold: true } + visible: false + horizontalAlignment: Text.AlignHCenter; textFormat: Text.StyledText + } + + DropShadow { + anchors.fill: txt + horizontalOffset: executor.height / 200 + verticalOffset: executor.height / 200 + radius: executor.height / 100 + samples: 17 + color: activPal.shadow + source: txt + } + + transformOrigin: Item.Top + + Connections { + target: executor.tipHandler + onDestroyResultTip: resultTip.destroy() + } + + Component.onCompleted: { + if (GLOB.useAnimations) + anim.running = true + } + + SequentialAnimation { + id: anim + NumberAnimation { target: resultTip; property: "y"; to: executor.height / 50; duration: 200 } + NumberAnimation { target: resultTip; property: "scale"; to: 2; duration: 200 } + NumberAnimation { target: resultTip; property: "scale"; to: 1; duration: 300 } + } +} -- GitLab