diff --git a/src/libs/core/score/tnoteitem.cpp b/src/libs/core/score/tnoteitem.cpp index 7a212f903b360972e0fffe0d5b9548532bdbbbf3..0480555ad90bff67f006d461d60ff2900de6f545 100644 --- a/src/libs/core/score/tnoteitem.cpp +++ b/src/libs/core/score/tnoteitem.cpp @@ -234,16 +234,7 @@ void TnoteItem::setNote(const Tnote& n) { m_notePosY = staff()->upperLine() + (m_note->rhythm() == Trhythm::Whole ? 2.0 : 4.0); else { if (m_note->isValid()) { - m_notePosY = staff()->score()->clefOffset().total() + staff()->upperLine() - (n.octave() * 7 + (n.note() - 1)); - if (staff()->isPianoStaff()) { - if (m_note->onUpperStaff()) { - if (m_notePosY > staff()->upperLine() + 13.0) - m_notePosY += 10.0; - } else { - if (m_notePosY > staff()->upperLine() + 3.0) - m_notePosY += 10.0; - } - } + m_notePosY = getHeadY(n); } else { if (staff()->score()->singleNote()) { m_notePosY = 0.0; @@ -506,6 +497,21 @@ void TnoteItem::markNoteHead(const QColor& outlineColor) { } +qreal TnoteItem::getHeadY(const Tnote& n) { + qreal yPos = staff()->score()->clefOffset().total() + staff()->upperLine() - (n.octave() * 7 + (n.note() - 1)); + if (staff()->isPianoStaff()) { + if (n.onUpperStaff()) { + if (yPos > staff()->upperLine() + 13.0) + yPos += 10.0; + } else { + if (yPos > staff()->upperLine() + 3.0) + yPos += 10.0; + } + } + return yPos; +} + + // #define LINE_WIDTH (0.2) // void TnoteItem::paint(QPainter* painter) { // if (m_note->isValid() && m_notePosY > 0.0) { diff --git a/src/libs/core/score/tnoteitem.h b/src/libs/core/score/tnoteitem.h index 12a916428824bd9e85b4ecf63060cae242889513..2f4d1e1dcbc87561f7820ba175481bf07edb6352 100644 --- a/src/libs/core/score/tnoteitem.h +++ b/src/libs/core/score/tnoteitem.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2017-2018 by Tomasz Bojczuk * + * Copyright (C) 2017-2019 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -146,6 +146,13 @@ public: */ void markNoteHead(const QColor& outlineColor); + /** + * Returns Y coordinate of the note head for given note @p n. + * But it doesn't take care of Scorek font overlay, + * so to get proper text item position @p 15.0 has to be subtracted + */ + qreal getHeadY(const Tnote& n); + signals: void noteChanged(); void notePosYchanged(); diff --git a/src/libs/core/score/tscoreobject.cpp b/src/libs/core/score/tscoreobject.cpp index 4baeb438d1e80cf5a5c77176c09f05486b0a2dc0..64c44e04ba61d67116bcdb49c4a91c76c2f447c0 100644 --- a/src/libs/core/score/tscoreobject.cpp +++ b/src/libs/core/score/tscoreobject.cpp @@ -414,6 +414,11 @@ TnoteItem* TscoreObject::note(int noteId) { } +QQuickItem* TscoreObject::noteHead(int noteId) { + return noteId > -1 && noteId < notesCount() ? m_segments[noteId]->item()->head() : nullptr; +} + + Tnote TscoreObject::noteOfItem(TnoteItem* item) const { return item ? *item->note() : Tnote(); } diff --git a/src/libs/core/score/tscoreobject.h b/src/libs/core/score/tscoreobject.h index 2d5f8d7fad5a622b7710d5fd5ae4acfa8f19c66f..e412f66052c58e38edac08ee8f60e65fc0128007 100644 --- a/src/libs/core/score/tscoreobject.h +++ b/src/libs/core/score/tscoreobject.h @@ -54,6 +54,7 @@ class Tmeter; class Tmelody; class Taction; class TbeamObject; +class QQuickItem; /** @@ -160,6 +161,8 @@ public: */ Q_INVOKABLE TnoteItem* note(int noteId); + QQuickItem* noteHead(int noteId); + /** * Returns note of given @p item or invalid (empty) one if item is null */ @@ -419,6 +422,8 @@ public: QQmlComponent* component() { return m_qmlComponent; } QQmlEngine* qmlEngine() { return m_qmlEngine; } + TclefOffset clefOffset() const { return m_clefOffset; } + signals: void meterChanged(); @@ -488,8 +493,6 @@ signals: protected: void addStaff(TstaffItem* st); - TclefOffset clefOffset() const { return m_clefOffset; } - /** * Shifts @p count measures starting from @p measureNr by setting first and last ids * of the next measure to given @p sourceStaff diff --git a/src/main/texamexecutor.cpp b/src/main/texamexecutor.cpp index cc3c721e1d6d82a8dddca1b42a3b48aea4e30cbd..27ed84aeefa653d41bebc1fac982eb8069fb7ae5 100755 --- a/src/main/texamexecutor.cpp +++ b/src/main/texamexecutor.cpp @@ -789,29 +789,28 @@ void TexamExecutor::correctAnswer() { m_askingTimer->stop(); m_tipHandler->clearCanvas(); auto curQ = m_exam->answList()->last(); - QColor markColor = m_supp->answerColor(curQ); +// QColor markColor = m_supp->answerColor(curQ); // if (curQ->melody() && (curQ->answerAsNote() || curQ->questionAsNote())) { // SCORE->setReadOnlyReacting(true); // It is undone whenever unLockScore() is called // } if (curQ->answerOnScore()) { if (curQ->melody()) { - + if (m_level.manualKey && curQ->key.value() != MAIN_SCORE->keySignatureValue()) + MAIN_SCORE->correctKeySignature(curQ->key); } else { Tnote goodNote = curQ->questionOnScore() ? curQ->qa_2.note : curQ->qa.note; - char key = MAIN_SCORE->keySignatureValue(); - bool correctAccid = curQ->wrongAccid() || curQ->wrongOctave(); -// if (curQ->wrongAccid() || curQ->wrongOctave()) // it corrects wrong octave as well -// SCORE->correctAccidental(goodNote); -// else if (curQ->wrongNote()) {/* - if (m_level.manualKey /*&& curQ->key.value() != SCORE->keySignature().value()*/) - key = curQ->key.value(); -// SCORE->correctKeySignature(curQ->key); +// bool correctAccid = curQ->wrongAccid() || curQ->wrongOctave(); + if (curQ->wrongAccid() || curQ->wrongOctave()) // it corrects wrong octave as well + MAIN_SCORE->correctNote(goodNote, true); + else if (curQ->wrongNote()) { +// if (m_level.manualKey && curQ->key.value() != MAIN_SCORE->keySignatureValue()) +// MAIN_SCORE->correctKeySignature(curQ->key); m_exercise->setCorrectedNoteId(0); -// SCORE->correctNote(goodNote, markColor); -// } -// if (curQ->wrongKey()) -// SCORE->correctKeySignature(curQ->key);*/ - MAIN_SCORE->correctNote(goodNote, key, correctAccid); + MAIN_SCORE->correctNote(goodNote, false); + } + if (curQ->wrongKey()) + MAIN_SCORE->correctKeySignature(curQ->key); +// MAIN_SCORE->correctNote(goodNote, correctAccid); correctAnimObject = MAIN_SCORE; } } else if (curQ->answerOnInstr()) { diff --git a/src/main/tmainscoreobject.cpp b/src/main/tmainscoreobject.cpp index 75b6d35ca743209ef1e850a42e255e36a3d56dbd..a5f09448e963326c116d8f7ca980db6039c27ff3 100644 --- a/src/main/tmainscoreobject.cpp +++ b/src/main/tmainscoreobject.cpp @@ -55,6 +55,8 @@ TmainScoreObject::TmainScoreObject(QObject* parent) : } m_instance = this; + m_goodNote = new Tnote(); + m_showNamesAct = new Taction(tr("Show note names"), QString(), this); m_showNamesAct->setCheckable(true); m_showNamesAct->setChecked(GLOB->namesOnScore()); @@ -105,6 +107,7 @@ TmainScoreObject::TmainScoreObject(QObject* parent) : TmainScoreObject::~TmainScoreObject() { + delete m_goodNote; m_instance = nullptr; } @@ -409,17 +412,35 @@ int TmainScoreObject::markNoteHead(const QColor& outColor, int noteNr) { } -void TmainScoreObject::correctNote(const Tnote& goodNote, char keySign, bool corrAccid) { +void TmainScoreObject::correctNote(const Tnote& goodNote, bool corrAccid) { if (m_scoreObj->singleNote()) { - m_scoreObj->setNote(0, goodNote); - if (keySign != keySignatureValue()) - m_scoreObj->setKeySignature(keySign); - if (corrAccid) { - // TODO - } - markNoteHead(GLOB->correctColor(), 0); + if (corrAccid) { + // FIXME probably it is not necessary - animation is universal for any kind of score mistake + } + auto noteItem = m_scoreObj->note(0); + if (!m_animationObj) { + QQmlComponent comp(m_scoreObj->qmlEngine(), QUrl(QStringLiteral("qrc:/exam/CorrectNoteAnim.qml"))); + m_animationObj = qobject_cast<QObject*>(comp.create()); + m_animationObj->setParent(this); + connect(m_animationObj, SIGNAL(finished()), this, SLOT(correctionFinishedSlot())); + connect(m_animationObj, SIGNAL(applyCorrect()), this, SLOT(applyCorrectSlot())); + } + m_animationObj->setProperty("noteHead", QVariant::fromValue(m_scoreObj->noteHead(0))); + m_animationObj->setProperty("endY", noteItem->getHeadY(goodNote) - 15.0); + m_animationObj->setProperty("running", true); + *m_goodNote = goodNote; + } else + QTimer::singleShot(1500, [=]{ emit correctionFinished(); }); // TODO Fake so far +} + + +void TmainScoreObject::correctKeySignature(const TkeySignature& keySign) { + if (m_questionKey) { + m_questionKey->setProperty("text", keySign.getName() + QLatin1String("<br>!")); + m_questionKey->setProperty("color", GLOB->EanswerColor); } - QTimer::singleShot(1500, [=]{ emit correctionFinished(); }); // Fake so far + if (keySign != keySignatureValue()) + m_scoreObj->setKeySignature(keySign.value()); } @@ -503,6 +524,17 @@ void TmainScoreObject::paletteSlot() { } +void TmainScoreObject::applyCorrectSlot() { + markNoteHead(GLOB->correctColor(), 0); +} + + +void TmainScoreObject::correctionFinishedSlot() { + m_scoreObj->setNote(0, *m_goodNote); + emit correctionFinished(); +} + + QColor TmainScoreObject::scoreBackgroundColor(const QColor& c, int alpha) { return Tcolor::merge(NOO->alpha(c, alpha), qApp->palette().base().color()); } diff --git a/src/main/tmainscoreobject.h b/src/main/tmainscoreobject.h index f06a404b3e1ec2bf52fb43bc15f6b82dced1a22c..0d9900b4f5193ca4ba0046e7d7502b8238178c60 100644 --- a/src/main/tmainscoreobject.h +++ b/src/main/tmainscoreobject.h @@ -142,7 +142,9 @@ public: * Returns how many notes was marked */ int markNoteHead(const QColor& outColor, int noteNr); - void correctNote(const Tnote& goodNote, char keySign, bool corrAccid = false); + + void correctNote(const Tnote& goodNote, bool corrAccid = false); + void correctKeySignature(const TkeySignature& keySign); Q_INVOKABLE void saveMusicXml(const QString& fileName, const QString& title = QString(), const QString& composer = QString()); @@ -172,6 +174,10 @@ protected: void checkExtraStaves(); + protected slots: + void applyCorrectSlot(); + void correctionFinishedSlot(); + private: TscoreObject *m_scoreObj = nullptr; Taction *m_playAct, *m_recModeAct; @@ -191,6 +197,8 @@ private: QQuickItem *m_mainScoreItem = nullptr; QList<TstaffLines*> m_emptyStaves; + Tnote *m_goodNote; + QObject *m_animationObj = nullptr; }; diff --git a/src/nootka.qrc b/src/nootka.qrc index d6ec691ed626fec113460692824c06fc86fcf18d..92e90155d21a1527bffee2ec9c0782cf92324365 100644 --- a/src/nootka.qrc +++ b/src/nootka.qrc @@ -134,6 +134,7 @@ <file alias="exam/ExamSettingsDialog.qml">qml/exam/ExamSettingsDialog.qml</file> <file alias="exam/CorrectNameAnim.qml">qml/exam/CorrectNameAnim.qml</file> <file alias="exam/CorrectInstrAnim.qml">qml/exam/CorrectInstrAnim.qml</file> + <file alias="exam/CorrectNoteAnim.qml">qml/exam/CorrectNoteAnim.qml</file> <file alias="exam/Certificate.qml">qml/exam/Certificate.qml</file> <file alias="charts/AnalyzeDialog.qml">charts/qml/AnalyzeDialog.qml</file> diff --git a/src/qml/exam/CorrectNoteAnim.qml b/src/qml/exam/CorrectNoteAnim.qml new file mode 100644 index 0000000000000000000000000000000000000000..c0ac07c84608113bd7e25ff7c4337a8c8785a98f --- /dev/null +++ b/src/qml/exam/CorrectNoteAnim.qml @@ -0,0 +1,47 @@ +/** This file is part of Nootka (http://nootka.sf.net) * + * Copyright (C) 2019 by Tomasz Bojczuk (seelook@gmail.com) * + * on the terms of GNU GPLv3 license (http://www.gnu.org/licenses) */ + +import QtQuick 2.9 + + +SequentialAnimation { + property Item noteHead: null + property real endY: 0 + + signal applyCorrect() + + // private + property real halfY: 0 + + onRunningChanged: halfY = noteHead.y + (noteHead.y - endY) / 2 + + SequentialAnimation { // cross blinking when no note was selected + loops: noteHead && noteHead.parent.notePosY === 0 ? 2 : 0 + NumberAnimation { + property: "disp"; from: 0; to: 200; duration: 200 + target: Rectangle { + id: cross + parent: noteHead ? noteHead.parent : null; visible: disp > 100; rotation: 45; z: 10 + property int disp: 0 + width: parent ? parent.width * 2 : 0; height: width / 15; color: GLOB.wrongColor; anchors.centerIn: parent; radius: width / 4 + Rectangle { height: parent.width; width: parent.height; color: GLOB.wrongColor; anchors.centerIn: parent; radius: height / 4 } + } + } + NumberAnimation { target: cross; property: "disp"; from: 200; to: 0; duration: 200 } + } + + ScriptAction { script: noteHead.visible = true } + + ParallelAnimation { + NumberAnimation { target: noteHead; property: "y"; to: halfY; duration: 300 } + NumberAnimation { target: noteHead; property: "scale"; to: 3; duration: 300 } + } + + ScriptAction { script: applyCorrect() } + + ParallelAnimation { + NumberAnimation { target: noteHead; property: "y"; to: endY; duration: 300 } + NumberAnimation { target: noteHead; property: "scale"; to: 1; duration: 300 } + } +}