From f5771d01ebaaa5bad0ab08a15fd7ce072daa4e71 Mon Sep 17 00:00:00 2001 From: SeeLook <945374+SeeLook@users.noreply.github.com> Date: Sun, 4 Dec 2016 18:10:07 +0100 Subject: [PATCH] Fixed positioning of tips with question content depending on question-answer type. Tcanvas::determineTipPos is clear and easy readable instead of missed up free-something variables in TquestionTip. Added html file with table describing all possibilities. --- src/plugins/exam/tcanvas.cpp | 192 ++++++++++++++++------------ src/plugins/exam/tcanvas.h | 41 ++++-- src/plugins/exam/tip positions.ods | Bin 0 -> 11982 bytes src/plugins/exam/tip_positions.html | 126 ++++++++++++++++++ src/plugins/exam/tquestiontip.cpp | 24 +--- src/plugins/exam/tquestiontip.h | 89 +++++++------ 6 files changed, 310 insertions(+), 162 deletions(-) create mode 100644 src/plugins/exam/tip positions.ods create mode 100644 src/plugins/exam/tip_positions.html diff --git a/src/plugins/exam/tcanvas.cpp b/src/plugins/exam/tcanvas.cpp index 5535ec470..301507250 100644 --- a/src/plugins/exam/tcanvas.cpp +++ b/src/plugins/exam/tcanvas.cpp @@ -67,7 +67,7 @@ QFont smalTipFont(QWidget* w) { /** Multiplexer of question tip scale factor to make it big enough on hi dpi tablet screens */ inline qreal multiScale() { - return qreal(Tmtr::shortScreenSide() / Tmtr::fingerPixels()) / 5.0; + return (static_cast<qreal>(Tmtr::shortScreenSide()) / Tmtr::fingerPixels()) / 5.0; } #endif @@ -82,7 +82,8 @@ Tcanvas::Tcanvas(QGraphicsView* view, Texam* exam) : m_exam(exam), m_timerToConfirm(new QTimer(this)), m_flyEllipse(nullptr), - m_minimizedQuestion(false), m_melodyCorrectMessage(false) + m_minimizedQuestion(false), m_melodyCorrectMessage(false), + m_tipPos(e_bottomRight) { m_scene = m_view->scene(); m_newSize = m_scene->sceneRect().size().toSize(); @@ -305,13 +306,11 @@ void Tcanvas::whatNextTip(bool isCorrect, bool toCorrection) { } whatNextText += br + TexamHelp::toStopExamTxt(QLatin1String("<a href=\"stopExam\">") + pixToHtml(Tpath::img("stopExam"), m_iconSize) + a); m_whatTip = new TgraphicsTextTip(whatNextText, m_view->palette().highlight().color()); -// if (m_guitarFree) // tip is wide there, otherwise text is word-wrapped and is narrowest but higher -// m_whatTip->setTextWidth(m_maxTipWidth); m_scene->addItem(m_whatTip); m_whatTip->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); m_whatTip->setTipMovable(true); connect(m_whatTip, SIGNAL(linkActivated(QString)), this, SLOT(linkActivatedSlot(QString))); - connect(m_whatTip, SIGNAL(moved()), this, SLOT(tipMoved())); + connect(m_whatTip, &TgraphicsTextTip::moved, this, &Tcanvas::tipMoved); #endif setWhatNextPos(); } @@ -371,21 +370,10 @@ void Tcanvas::questionTip() { delete m_outTuneTip; clearWhatNextTip(); clearMelodyCorrectMessage(); - createQuestionTip(); - m_guitarFree = m_questionTip->freeGuitar() && GUITAR->isVisible(); - m_nameFree = m_questionTip->freeName() && SCORE->insertMode() == TmultiScore::e_single; - m_scoreFree = m_questionTip->freeScore(); - if (!m_guitarFree && !m_nameFree && !m_scoreFree) // workaround when only score is visible - m_scoreFree = true; - m_tipPos = e_scoreOver; // score is visible always - if (m_nameFree && SCORE->insertMode() == TmultiScore::e_single) - m_tipPos = e_nameOver; - else if (m_scoreFree) - m_tipPos = e_scoreOver; - else if (m_guitarFree && GUITAR->isVisible()) - m_tipPos = e_guitarOver; - m_questionTip->setMinimized(m_minimizedQuestion); - setQuestionPos(); + createQuestionTip(); + m_tipPos = determineTipPos(); + m_questionTip->setMinimized(m_minimizedQuestion); + setQuestionPos(); } @@ -569,10 +557,6 @@ void Tcanvas::clearMelodyCorrectMessage() { } -void Tcanvas::markAnswer(TQAtype::Etype qType, TQAtype::Etype aType) { -} - - const QRect& Tcanvas::getRect(TQAtype::Etype kindOf) { switch (kindOf) { case TQAtype::e_asNote: @@ -592,12 +576,12 @@ const QRect& Tcanvas::getRect(TQAtype::Etype kindOf) { void Tcanvas::sizeChangedDelayed(const QRectF& newRect) { QSizeF factor(newRect.width() / m_prevSize.width(), newRect.height() / m_prevSize.height()); - for (int i = 0; i < 3; ++i) { - if (!m_posOfQuestTips[i].isNull()) - m_posOfQuestTips[i] = QPointF(m_posOfQuestTips[i].x() * factor.width(), m_posOfQuestTips[i].y() * factor.height()); - if (!m_posOfWhatTips[i].isNull()) - m_posOfWhatTips[i] = QPointF(m_posOfWhatTips[i].x() * factor.width(), m_posOfWhatTips[i].y() * factor.height()); - } + for (int i = 0; i < TIP_POS_NUM; ++i) { + if (!m_posOfQuestTips[i].isNull()) + m_posOfQuestTips[i] = QPointF(m_posOfQuestTips[i].x() * factor.width(), m_posOfQuestTips[i].y() * factor.height()); + if (!m_posOfWhatTips[i].isNull()) + m_posOfWhatTips[i] = QPointF(m_posOfWhatTips[i].x() * factor.width(), m_posOfWhatTips[i].y() * factor.height()); + } if (!m_posOfConfirm.isNull()) m_posOfConfirm = QPointF(m_posOfConfirm.x() * factor.width(), m_posOfConfirm.y() * factor.height()); m_prevSize = newRect.size(); @@ -641,8 +625,6 @@ void Tcanvas::sizeChanged() { if (m_confirmTip) { clearConfirmTip(); // To re-create confirm tip works better than re-scaling showConfirmTip(); -// m_confirmTip->setScale(m_scale * 1.2); -// setConfirmPos(); } if (m_certifyTip) { clearCertificate(); @@ -706,30 +688,27 @@ bool Tcanvas::eventFilter(QObject* obj, QEvent* event) { //################################################################################################## int Tcanvas::getMaxTipHeight() { - if (m_nameFree || m_scoreFree) - return SCORE->height() * 0.6; - else - return GUITAR->height() * 1.1; + if (m_tipPos == e_nameOver || m_tipPos == e_scoreOver) + return SCORE->height() * 0.6; + else + return GUITAR->height() * 1.1; } void Tcanvas::setPosOfTip(TgraphicsTextTip* tip) { - QRect geoRect; - if (m_nameFree) { // middle of the noteName - geoRect = NOTENAME->geometry(); - } else if (m_scoreFree) {// on the score at its center - geoRect = SCORE->geometry(); - fixWidthOverScore(tip); - if (SCORE->insertMode() != TmultiScore::e_single && !GUITAR->isVisible()) // only score - place it bottom right - geoRect = QRect(m_view->width() - tip->realW(), m_view->height() - tip->realH(), tip->realW(), tip->realH()); - } else { // middle of the guitar - geoRect = GUITAR->geometry(); - if (m_exam && !m_exam->melodies()) // in single mode put a tip on the right guitar side, below note name - geoRect.setRect(NOTENAME->x(), GUITAR->y(), NOTENAME->width(), GUITAR->height()); - } - tip->setPos(qMin(geoRect.x() + (geoRect.width() - tip->realW()) / 2, m_view->width() - tip->realW() - 5.0), - qMin(geoRect.y() + (geoRect.height() - tip->realH()) / 2, m_view->height() - tip->realH() - 5.0)); - // qMin guards a tip position in scene boundaries + QRect geoRect; + if (m_tipPos == e_nameOver) { // middle of the noteName + geoRect.setRect(SCORE->x() + SCORE->width(), SCORE->y() + NOTENAME->y(), NOTENAME->width(), NOTENAME->height()); + } else if (m_tipPos == e_scoreOver) {// on the score at its center + geoRect = SCORE->geometry(); + fixWidthOverScore(tip); + } else if (m_tipPos == e_guitarOver) // middle of the guitar + geoRect = GUITAR->geometry(); + else // bottom-right corner + geoRect = QRect(m_view->width() - tip->realW(), m_view->height() - tip->realH(), tip->realW(), tip->realH()); + tip->setPos(qMin(geoRect.x() + (geoRect.width() - tip->realW()) / 2, m_view->width() - tip->realW() - 5.0), + qMin(geoRect.y() + (geoRect.height() - tip->realH()) / 2, m_view->height() - tip->realH() - 5.0)); + // qMin guards a tip position in scene boundaries } @@ -778,19 +757,20 @@ void Tcanvas::setWhatNextPos() { } #else int maxTipHeight = getMaxTipHeight(); - if (!m_nameFree && m_whatTip->realH() != maxTipHeight) - m_whatTip->setScale((qreal)maxTipHeight / m_whatTip->realH()); - if (m_tipPos == e_nameOver) { - if (m_whatTip->realW() != m_view->width() * 0.45) - m_whatTip->setScale((m_view->width() * 0.45) / m_whatTip->realW()); - if (m_whatTip->realH() > SCORE->height()) - m_whatTip->setScale((qreal)(SCORE->height()) / m_whatTip->realH()); - } else - fixWidthOverScore(m_whatTip); - if (m_posOfWhatTips[(int)m_tipPos].isNull()) // calculate tip position only when user doesn't change it - setPosOfTip(m_whatTip); - else - m_whatTip->setFixPos(m_posOfWhatTips[(int)m_tipPos]); + if (m_tipPos != e_nameOver && m_whatTip->realH() != maxTipHeight) + m_whatTip->setScale(maxTipHeight / m_whatTip->realH()); + + if (m_tipPos == e_nameOver) { + if (m_whatTip->realW() != m_view->width() * 0.45) + m_whatTip->setScale((m_view->width() * 0.45) / m_whatTip->realW()); + if (m_whatTip->realH() > SCORE->height()) + m_whatTip->setScale(SCORE->height() / m_whatTip->realH()); + } else + fixWidthOverScore(m_whatTip); + if (m_posOfWhatTips[static_cast<int>(m_tipPos)].isNull()) // calculate tip position only when user doesn't change it + setPosOfTip(m_whatTip); + else + m_whatTip->setFixPos(m_posOfWhatTips[static_cast<int>(m_tipPos)]); #endif } @@ -828,8 +808,8 @@ void Tcanvas::createQuestionTip() { void Tcanvas::setQuestionPos() { - int maxTipHeight = getMaxTipHeight() * 1.1; - qreal fineScale; + int maxTipHeight = qRound(getMaxTipHeight() * 1.1); + qreal fineScale; if (m_questionTip->boundingRect().height() > maxTipHeight) { // check is scaling needed fineScale = (qreal)maxTipHeight / m_questionTip->boundingRect().height(); #if defined (Q_OS_ANDROID) @@ -845,13 +825,13 @@ void Tcanvas::setQuestionPos() { scaleStep += 0.1; } } - if (m_posOfQuestTips[(int)m_tipPos].isNull()) // calculate tip position only when user doesn't change it - setPosOfTip(m_questionTip); - else { - fixWidthOverScore(m_questionTip); - m_questionTip->setFixPos(m_posOfQuestTips[(int)m_tipPos]); - } - m_questionTip->show(); + if (m_posOfQuestTips[static_cast<int>(m_tipPos)].isNull()) // calculate tip position only when user doesn't change it + setPosOfTip(m_questionTip); + else { + fixWidthOverScore(m_questionTip); + m_questionTip->setFixPos(m_posOfQuestTips[static_cast<int>(m_tipPos)]); + } + m_questionTip->show(); } @@ -873,19 +853,19 @@ void Tcanvas::updateRelatedPoint() { void Tcanvas::fixWidthOverScore ( TgraphicsTextTip* tip ) { - if (m_tipPos == e_scoreOver && tip->realW() > SCORE->width()) - tip->setScale((qMax((qreal)SCORE->width() * 0.9, m_view->width() / 3.0) / (tip->boundingRect().width()))); + if (m_tipPos == e_scoreOver && tip->realW() > SCORE->width()) + tip->setScale((qMax(SCORE->width() * 0.9, m_view->width() / 3.0) / (tip->boundingRect().width()))); } void Tcanvas::tipMoved() { - if (sender() == m_questionTip) - m_posOfQuestTips[(int)m_tipPos] = m_questionTip->pos(); - else if (sender() == m_whatTip) - m_posOfWhatTips[(int)m_tipPos] = m_whatTip->pos(); - else if (sender() == m_confirmTip) - m_posOfConfirm = m_confirmTip->pos(); + if (sender() == m_questionTip) + m_posOfQuestTips[static_cast<int>(m_tipPos)] = m_questionTip->pos(); + else if (sender() == m_whatTip) + m_posOfWhatTips[static_cast<int>(m_tipPos)] = m_whatTip->pos(); + else if (sender() == m_confirmTip) + m_posOfConfirm = m_confirmTip->pos(); } @@ -895,6 +875,54 @@ void Tcanvas::tipStateChanged() { } +/** + * For details, see table in tip_positions.html file + */ +Tcanvas::EtipPos Tcanvas::determineTipPos() { + EtipPos tipPos; + switch (m_exam->curQ()->questionAs) { + /** Question is note on the score, so place a tip over name if not used or over guitar if visible but if not - in bottom-right corner. */ + case TQAtype::e_asNote : { + if (SCORE->insertMode() == TmainScore::e_single) { + if (m_exam->curQ()->answerAs == TQAtype::e_asName) { + tipPos = GUITAR->isVisible() ? e_guitarOver : e_bottomRight; + } else + tipPos = e_nameOver; + } else { // melody, so answer is sound (only supported case) + tipPos = GUITAR->isVisible() ? e_guitarOver : e_bottomRight; + } + break; + } + /** Question is note name, so place a tip over score if not used, or over guitar. */ + case TQAtype::e_asName : { // single note mode only + if (m_exam->curQ()->answerAs == TQAtype::e_asNote) + tipPos = GUITAR->isVisible() ? e_guitarOver : e_bottomRight; + else + tipPos = e_scoreOver; + break; + } + case TQAtype::e_asFretPos : { // single note mode only + if (m_exam->curQ()->answerAs == TQAtype::e_asNote) + tipPos = e_nameOver; + else + tipPos = e_scoreOver; + break; + } + case TQAtype::e_asSound : { + if (SCORE->insertMode() == TmainScore::e_single) { + if (m_exam->curQ()->answerAs == TQAtype::e_asNote) + tipPos = e_nameOver; + else + tipPos = e_scoreOver; + } else { + tipPos = GUITAR->isVisible() ? e_guitarOver : e_bottomRight; + } + break; + } + } + return tipPos; +} + diff --git a/src/plugins/exam/tcanvas.h b/src/plugins/exam/tcanvas.h index d4773a8f3..366dbbbcb 100644 --- a/src/plugins/exam/tcanvas.h +++ b/src/plugins/exam/tcanvas.h @@ -27,6 +27,8 @@ #include <tfingerpos.h> +#define TIP_POS_NUM (4) /**< Number of possible tip positions depends on question/answer combination */ + class Tnote; class TnoteName; class TcombinedAnim; @@ -56,16 +58,25 @@ class Tcanvas : public QObject public: - enum EtipPos { - e_guitarOver = 0, e_scoreOver = 1, e_nameOver = 2 - }; /**< Describes a kind of tip position depended on q/a type - over what widget tip is placed */ + /** + * Describes a kind of tip position depended on q/a type - over what widget tip is placed. + * Number of enumerators has to correspond with @p TIP_POS_NUM definition + */ + enum EtipPos { + e_guitarOver = 0, e_scoreOver = 1, e_nameOver = 2, e_bottomRight + }; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) + Q_ENUM(EtipPos) +#endif Tcanvas(QGraphicsView* view, Texam* exam); virtual ~Tcanvas(); - /** Cross platform status message: - * - status bar on desktops (@class TstatusLabel) - * - pop up message on screen bottom for mobile (@class TtouchMessage) */ + /** + * Cross platform status message: + * - status bar on desktops (@p TstatusLabel) + * - pop up message on screen bottom for mobile (@p TtouchMessage) + */ void setStatusMessage(const QString& text, int duration = 0); void changeExam(Texam* newExam); /**< Replaces exam pointer given in constructor to the new one. */ @@ -74,11 +85,15 @@ public: 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. + /** + * 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); - void questionTip(); /**< Text with question context */ + * @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 */ void melodyCorrectMessage(); /**< Status message about how to correct a melody notes. */ @@ -99,8 +114,6 @@ public: QFont tipFont(qreal factor = 1); QString startTipText(); - /** Paints animated exclamation mark over answering widget. */ - void markAnswer(TQAtype::Etype qType, TQAtype::Etype aType); /** Paints rectangle around given type of widget to mark where is answer. */ const QRect& getRect(TQAtype::Etype kindOf); @@ -165,7 +178,6 @@ private: QPointer<TcombinedAnim> m_correctAnim; QTimer *m_timerToConfirm; int m_maxTipWidth; - bool m_guitarFree, m_nameFree, m_scoreFree; QSizeF m_prevSize; QSize m_newSize; QGraphicsEllipseItem *m_flyEllipse; @@ -173,7 +185,7 @@ private: QColor m_correctColor; TnoteName *m_noteName; QPoint m_relPoint; - QPointF m_posOfQuestTips[3], m_posOfWhatTips[3], m_posOfConfirm; + QPointF m_posOfQuestTips[TIP_POS_NUM], m_posOfWhatTips[TIP_POS_NUM], m_posOfConfirm; bool m_minimizedQuestion, m_melodyCorrectMessage; EtipPos m_tipPos; /**< Kind of tip position */ int m_iconSize; /**< Icon image size on tips calculated from actual font metrics. */ @@ -191,6 +203,7 @@ private: void updateRelatedPoint(); void createQuestionTip(); /**< Be sure that @p m_exam has already pointed current exam */ void fixWidthOverScore(TgraphicsTextTip* tip); /**< Scales tip if its width is bigger than score widget */ + EtipPos determineTipPos(); }; diff --git a/src/plugins/exam/tip positions.ods b/src/plugins/exam/tip positions.ods new file mode 100644 index 0000000000000000000000000000000000000000..f3b5b1b521b80f98faf2a77fdc2ddcec7ae9c043 GIT binary patch literal 11982 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 literal 0 HcmV?d00001 diff --git a/src/plugins/exam/tip_positions.html b/src/plugins/exam/tip_positions.html new file mode 100644 index 000000000..7578581bf --- /dev/null +++ b/src/plugins/exam/tip_positions.html @@ -0,0 +1,126 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + + <meta http-equiv="content-type" content="text/html; charset=utf-8"/> + <title></title> + <meta name="generator" content="LibreOffice 5.2.3.3 (Linux)"/> + <meta name="created" content="2016-12-04T12:40:04.099779111"/> + <meta name="changed" content="2016-12-04T15:03:39.802531112"/> + + <style type="text/css"> + body,div,table,thead,tbody,tfoot,tr,th,td,p { font-family:"Arial"; font-size:x-small } + a.comment-indicator:hover + comment { background:#ffd; position:absolute; display:block; border:1px solid black; padding:0.5em; } + a.comment-indicator { background:red; display:inline-block; border:1px solid black; width:0.5em; height:0.5em; } + comment { display:none; } + </style> + +</head> + +<body> +<table cellspacing="0" border="0"> + <colgroup width="85"></colgroup> + <colgroup width="102"></colgroup> + <colgroup width="121"></colgroup> + <colgroup width="85"></colgroup> + <colgroup width="134"></colgroup> + <tr> + <td height="17" align="center" valign=middle>q/a</td> + <td align="center" valign=middle>score</td> + <td align="center" valign=middle>name</td> + <td align="center" valign=middle>guitar</td> + <td align="center" valign=middle>sound</td> + </tr> + <tr> + <td height="33" align="center" valign=middle>score</td> + <td align="center" valign=middle>------------</td> + <td align="center" valign=middle>------------</td> + <td align="center" valign=middle>------------</td> + <td align="center" valign=middle>over guitar<br>Or bottom-right</td> + </tr> + <tr> + <td height="33" align="center" valign=middle>score single</td> + <td align="center" valign=middle>over name</td> + <td align="center" valign=middle>over guitar <br>or bottom-right</td> + <td align="center" valign=middle>over name</td> + <td align="center" valign=middle>over name</td> + </tr> + <tr> + <td height="33" align="center" valign=middle>name single</td> + <td align="center" valign=middle>over guitar<br>Or bottom-right</td> + <td align="center" valign=middle>over score</td> + <td align="center" valign=middle>over score</td> + <td align="center" valign=middle>over score</td> + </tr> + <tr> + <td height="17" align="center" valign=middle>guitar single</td> + <td align="center" valign=middle>over name</td> + <td align="center" valign=middle>over score</td> + <td align="center" valign=middle>over score</td> + <td align="center" valign=middle>over score</td> + </tr> + <tr> + <td height="33" align="center" valign=middle>sound</td> + <td align="center" valign=middle>over guitar <br>or bottom-right</td> + <td align="center" valign=middle>------------</td> + <td align="center" valign=middle>------------</td> + <td align="center" valign=middle>------------</td> + </tr> + <tr> + <td height="17" align="center" valign=middle>sound single</td> + <td align="center" valign=middle>over name</td> + <td align="center" valign=middle>over score</td> + <td align="center" valign=middle>over score</td> + <td align="center" valign=middle>over score</td> + </tr> + <tr> + <td height="17" align="center" valign=middle><br></td> + <td align="center" valign=middle><br></td> + <td align="center" valign=middle><br></td> + <td align="center" valign=middle><br></td> + <td align="center" valign=middle><br></td> + </tr> + <tr> + <td height="17" align="center" valign=middle><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + </tr> + <tr> + <td height="17" align="center" valign=middle><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + </tr> + <tr> + <td height="17" align="center" valign=middle><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + </tr> + <tr> + <td height="17" align="center" valign=middle><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + </tr> + <tr> + <td height="17" align="center" valign=middle><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + <td align="left"><br></td> + </tr> + <tr> + <td colspan=5 rowspan=4 height="68" align="center" valign=middle>This table describes all possible positions of tip (question or what next)<br>Available for melodies and single note questions/answers</td> + </tr> +</table> +<!-- ************************************************************************** --> +</body> + +</html> diff --git a/src/plugins/exam/tquestiontip.cpp b/src/plugins/exam/tquestiontip.cpp index cdbaa679a..35a6f8e9b 100644 --- a/src/plugins/exam/tquestiontip.cpp +++ b/src/plugins/exam/tquestiontip.cpp @@ -109,9 +109,6 @@ QString TquestionTip::getNiceNoteName(Tnote& note, Tnote::EnameStyle style) { QString TquestionTip::getQuestion(TQAunit* question, int questNr, Tlevel* level, double scale) { QString br = QStringLiteral("<br>"); QString sp = QStringLiteral(" "); - m_scoreFree = true; - m_nameFree = !(bool)question->melody(); // no name widget when level uses melodies - m_guitarFree = true; m_questText.clear(); double sc = 4.0; if (scale) { @@ -126,7 +123,6 @@ QString TquestionTip::getQuestion(TQAunit* question, int questNr, Tlevel* level, QString noteStr; switch (question->questionAs) { case TQAtype::e_asNote: { - m_scoreFree = false; if (question->answerAsNote()) { if (question->qa.note.alter != question->qa_2.note.alter) m_questText += tr("Change enharmonically and show on the staff"); @@ -136,10 +132,8 @@ QString TquestionTip::getQuestion(TQAunit* question, int questNr, Tlevel* level, apendix = tr("<br><b>in %1 key.</b>", "in key signature").arg(question->key.getName()); m_questText += getTextHowAccid((Tnote::Ealter)question->qa_2.note.alter); } else if (question->answerAsName()) { - m_nameFree = false; m_questText += tr("Give name of"); } else if (question->answerAsFret()) { - m_guitarFree = false; m_questText += tr("Show on the guitar"); } else if (question->answerAsSound()) { if (question->melody()) @@ -164,27 +158,23 @@ QString TquestionTip::getQuestion(TQAunit* question, int questNr, Tlevel* level, } case TQAtype::e_asName: - m_nameFree = false; noteStr = br + getNiceNoteName(question->qa.note, question->styleOfQuestion()); if (question->answerAsNote()) { - m_scoreFree = false; m_questText += tr("Show on the staff") + noteStr; if (level->useKeySign && level->manualKey) { m_questText += tr("<br><b>in %1 key.</b>", "in key signature").arg(question->key.getName()); } } else if (question->answerAsName()) { - m_nameFree = false; noteStr = br + getNiceNoteName(question->qa.note, question->styleOfQuestion()); if (question->qa.note.alter != question->qa_2.note.alter) { m_questText += tr("Change enharmonically and give name of"); m_questText += noteStr + getTextHowAccid((Tnote::Ealter)question->qa_2.note.alter); } else m_questText += tr("Use another style to give name of") + noteStr; - } else if (question->answerAsFret()) { - m_guitarFree = false; - m_questText += tr("Show on the guitar") + noteStr; - } else if (question->answerAsSound()) { - m_questText += playOrSing(int(level->instrument)) + noteStr; + } else if (question->answerAsFret()) { + m_questText += tr("Show on the guitar") + noteStr; + } else if (question->answerAsSound()) { + m_questText += playOrSing(int(level->instrument)) + noteStr; } if (question->answerAsFret() || question->answerAsSound()) { if (level->instrument != e_noInstrument && level->showStrNr && !level->onlyLowPos) @@ -193,15 +183,12 @@ QString TquestionTip::getQuestion(TQAunit* question, int questNr, Tlevel* level, break; case TQAtype::e_asFretPos: - m_guitarFree = false; if (question->answerAsNote()) { - m_scoreFree = false; m_questText += tr("Show on the staff note played on"); if (level->useKeySign && level->manualKey) { apendix = tr("<b>in %1 key.</b>", "in key signature").arg(question->key.getName()); } } else if (question->answerAsName()) { - m_nameFree = false; m_questText += tr("Give name of"); } else if (question->answerAsFret()) { m_questText += tr("Show sound from position:", "... and string + fret numbers folowing"); @@ -220,7 +207,6 @@ QString TquestionTip::getQuestion(TQAunit* question, int questNr, Tlevel* level, case TQAtype::e_asSound: if (question->answerAsNote()) { - m_scoreFree = false; if (question->melody()) { m_questText += TexTrans::writeDescTxt(); if (level->useKeySign && level->manualKey && level->onlyCurrKey) @@ -233,12 +219,10 @@ QString TquestionTip::getQuestion(TQAunit* question, int questNr, Tlevel* level, m_questText += getTextHowAccid((Tnote::Ealter)question->qa.note.alter); } } else if (question->answerAsName()) { - m_nameFree = false; m_questText += tr("Give name of listened sound"); if (level->forceAccids) m_questText += getTextHowAccid((Tnote::Ealter)question->qa.note.alter); } else if (question->answerAsFret()) { - m_guitarFree = false; m_questText += tr("Listened sound show on the guitar"); if (level->showStrNr) m_questText += br + sp + onStringTxt(question->qa.pos.str()); diff --git a/src/plugins/exam/tquestiontip.h b/src/plugins/exam/tquestiontip.h index 8701c476a..47c032920 100644 --- a/src/plugins/exam/tquestiontip.h +++ b/src/plugins/exam/tquestiontip.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2012-2014 by Tomasz Bojczuk * + * Copyright (C) 2012-2016 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -15,19 +15,22 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ - + #ifndef TQUESTIONTIP_H #define TQUESTIONTIP_H + #include <graphics/tgraphicstexttip.h> #include <music/tnote.h> -#include <QLinearGradient> +#include <QtGui/qbrush.h> + class TfadeAnim; class Texam; class Tlevel; class TQAunit; + /** * This is graphics tip (rectangle) representing a question context * It can be minimized with mouse. @@ -35,53 +38,47 @@ class TQAunit; class TquestionTip : public TgraphicsTextTip { Q_OBJECT - + public: - /** Constructs tip with question content. */ - TquestionTip(Texam *exam, double scale = 0); - ~TquestionTip(); - - static QString getTextHowAccid(Tnote::Ealter accid); - static QString onStringTxt(quint8 strNr); /** Returns translated text on (strNr) string in Nootka font. */ - - /** Depend on @p instrument it returns text: - * 'Play or sing' (other instrument) - * 'Play' (guitars) */ - static QString playOrSing(int instr); - - static QString& text() { return m_questText; } /** Returns a reference to question HTML string. */ - - bool freeScore() { return m_scoreFree; } /** true when question is not on score */ - bool freeName() { return m_nameFree; } /** true when question is not on note name */ - bool freeGuitar() { return m_guitarFree; } /** true when question is not on guitar */ - - bool isMinimized() { return m_minimized; } /** True when tip is minimized */ - void setMinimized(bool min); /** Minimizes of maximizes a tip */ - - virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); - virtual QRectF boundingRect() const; - - virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event); - virtual void hoverMoveEvent(QGraphicsSceneHoverEvent* event); - virtual void mousePressEvent(QGraphicsSceneMouseEvent* event); - + /** Constructs tip with question content. */ + TquestionTip(Texam *exam, double scale = 0); + ~TquestionTip(); + + static QString getTextHowAccid(Tnote::Ealter accid); + static QString onStringTxt(quint8 strNr); /** Returns translated text on (strNr) string in Nootka font. */ + + /** Depend on @p instrument it returns text: + * 'Play or sing' (other instrument) + * 'Play' (guitars) */ + static QString playOrSing(int instr); + + static QString& text() { return m_questText; } /**< Returns a reference to question HTML string. */ + + bool isMinimized() { return m_minimized; } /**< True when tip is minimized */ + void setMinimized(bool min); /**< Minimizes of maximizes a tip */ + + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + virtual QRectF boundingRect() const; + + virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event); + virtual void hoverMoveEvent(QGraphicsSceneHoverEvent* event); + virtual void mousePressEvent(QGraphicsSceneMouseEvent* event); + signals: - void minimizeChanged(); /** Emitted when tips gets minimized or restored to normal state */ - + void minimizeChanged(); /**< Emitted when tips gets minimized or restored to normal state */ + protected: - /** Returns html-formatted question text. */ - QString getQuestion(TQAunit* question, int questNr, Tlevel* level, double scale = 0); - QString getNiceNoteName(Tnote& note, Tnote::EnameStyle style); - - + /** Returns html-formatted question text. */ + QString getQuestion(TQAunit* question, int questNr, Tlevel* level, double scale = 0); + QString getNiceNoteName(Tnote& note, Tnote::EnameStyle style); + private: - bool m_scoreFree, m_nameFree, m_guitarFree; /** Indicate where a tip has to be placed. */ - Tnote::Ealter m_forcedAccid; /** When different than Tnote::e_Natural text is shown */ - TfadeAnim *m_fadeInAnim; - bool m_markCorner, m_minimized; - static QString m_questText; - QLinearGradient m_staffGradient; + Tnote::Ealter m_forcedAccid; /**< When different than Tnote::e_Natural text is shown */ + TfadeAnim *m_fadeInAnim; + bool m_markCorner, m_minimized; + static QString m_questText; + QLinearGradient m_staffGradient; }; -#endif // TQUESTIONTIP_H \ No newline at end of file +#endif // TQUESTIONTIP_H -- GitLab