From b94b634c4239a864f712ec4181f1eb856bfe79a9 Mon Sep 17 00:00:00 2001 From: SeeLook <945374+SeeLook@users.noreply.github.com> Date: Fri, 24 Feb 2017 15:36:17 +0100 Subject: [PATCH] Implemented shifting measure to the next staff if there is not enough space in the current one. Obtaining lowest and highest notes in the staff to place it well, updating flickable area height when staves are added or moved. --- src/libs/core/score/tmeasureobject.cpp | 2 +- src/libs/core/score/tnoteobject.cpp | 8 ++++ src/libs/core/score/tnoteobject.h | 1 + src/libs/core/score/tscoreobject.cpp | 54 +++++++++++++++--------- src/libs/core/score/tscoreobject.h | 15 +++++++ src/libs/core/score/tstaffobject.cpp | 58 +++++++++++++++++--------- src/libs/core/score/tstaffobject.h | 9 +++- src/qml/score/Score.qml | 3 +- 8 files changed, 107 insertions(+), 43 deletions(-) diff --git a/src/libs/core/score/tmeasureobject.cpp b/src/libs/core/score/tmeasureobject.cpp index 91f38a359..fe54db346 100644 --- a/src/libs/core/score/tmeasureobject.cpp +++ b/src/libs/core/score/tmeasureobject.cpp @@ -56,7 +56,7 @@ void TmeasureObject::setStaff(TstaffObject* st) { void TmeasureObject::appendNewNotes(int segmentId, int count) { // so far we are sure there is enough space for whole note list in this measure -// qDebug() << debug() << "append" << count << "note(s)"; +// qDebug() << debug() << "append" << count << "note(s) from" << segmentId; for (int n = segmentId; n < segmentId + count; ++n) m_notes.append(m_score->noteSegment(n)); updateRhythmicGroups(); diff --git a/src/libs/core/score/tnoteobject.cpp b/src/libs/core/score/tnoteobject.cpp index 982956d4e..e18691a42 100644 --- a/src/libs/core/score/tnoteobject.cpp +++ b/src/libs/core/score/tnoteobject.cpp @@ -113,6 +113,14 @@ TnoteObject::~TnoteObject() { } +void TnoteObject::setStaff(TstaffObject* staffObj) { + if (staffObj != m_staff) { + m_staff = staffObj; + setParentItem(m_staff->staffItem()); + } +} + + void TnoteObject::setMeasure(TmeasureObject* m) { m_measure = m; } diff --git a/src/libs/core/score/tnoteobject.h b/src/libs/core/score/tnoteobject.h index 0fbf281b3..9871abd80 100644 --- a/src/libs/core/score/tnoteobject.h +++ b/src/libs/core/score/tnoteobject.h @@ -44,6 +44,7 @@ public: ~TnoteObject(); TstaffObject* staff() const { return m_staff; } + void setStaff(TstaffObject* staffObj); TmeasureObject* measure() { return m_measure; } void setMeasure(TmeasureObject* m); diff --git a/src/libs/core/score/tscoreobject.cpp b/src/libs/core/score/tscoreobject.cpp index ce37e57d1..4e573b72d 100644 --- a/src/libs/core/score/tscoreobject.cpp +++ b/src/libs/core/score/tscoreobject.cpp @@ -27,7 +27,7 @@ #include <QtCore/qdebug.h> #include "checktime.h" -#define MIN_STAFF_FACTOR (1.2) // if TstaffObject::gapFactor is less than this value - new staff is necessary +// #define MIN_STAFF_FACTOR (1.2) // if TstaffObject::gapFactor is less than this value - new staff is necessary TscoreObject::TscoreObject(QObject* parent) : @@ -78,21 +78,15 @@ CHECKTIME ( auto lastMeasure = m_measures.last(); if (lastMeasure->free() == 0) { // new measure is needed lastMeasure = new TmeasureObject(m_measures.count(), this); - auto lastSt = lastStaff(); - if (lastSt->gapFactor() < MIN_STAFF_FACTOR) { // and new staff is needed as well - emit staffCreate(); - lastSt = lastStaff(); - } - lastStaff()->appendMeasure(lastMeasure); m_measures << lastMeasure; + lastStaff()->appendMeasure(lastMeasure); } int noteDur = n.duration(); if (noteDur > lastMeasure->free()) { // split note that is adding - int leftDuration = noteDur - lastMeasure->free(); - QList<Tnote> notesToCurrent; - QList<Tnote> notesToNext; + int leftDuration = noteDur - lastMeasure->free(); int lastNoteId = m_segments.count(); + QList<Tnote> notesToCurrent; solveList(n, lastMeasure->free(), notesToCurrent); // solve free duration in current measure if (notesToCurrent.isEmpty()) qDebug() << "[TscoreObject] can't resolve duration of" << lastMeasure->free(); @@ -101,6 +95,7 @@ CHECKTIME ( lastMeasure->appendNewNotes(lastNoteId, notesToCurrent.count()); } + QList<Tnote> notesToNext; solveList(n, leftDuration, notesToNext); // solve remaining duration for the next measure lastNoteId = m_segments.count(); // update id of the last note segment if (notesToNext.isEmpty()) @@ -108,20 +103,14 @@ CHECKTIME ( else { appendNoteList(notesToNext); auto newLastMeasure = new TmeasureObject(m_measures.count(), this); // add a new measure - auto lastSt = lastStaff(); - if (lastSt->gapFactor() < 1.2) { - emit staffCreate(); - lastSt = lastStaff(); - } - lastSt->appendMeasure(newLastMeasure); m_measures << newLastMeasure; + lastStaff()->appendMeasure(newLastMeasure); newLastMeasure->appendNewNotes(lastNoteId, notesToNext.count()); } - } else { // just add new note to the measure + } else { // just add new note to the last measure m_notes << n; int lastNoteId = m_segments.count(); - auto newSeg = new TnotePair(lastNoteId, &m_notes.last()); - m_segments << newSeg; + m_segments << new TnotePair(lastNoteId, &m_notes.last()); lastMeasure->appendNewNotes(lastNoteId, 1); } @@ -182,7 +171,10 @@ int TscoreObject::meterToInt() { return static_cast<int>(m_meter->meter()); } qreal TscoreObject::stavesHeight() { - return m_staves.isEmpty() ? 0.0 : lastStaff()->staffItem()->y() + lastStaff()->staffItem()->height() * lastStaff()->scale(); + if (m_staves.isEmpty()) + return 0.0; + auto last = lastStaff(); + return last->staffItem()->y() + last->staffItem()->height() * last->scale(); } @@ -194,6 +186,7 @@ void TscoreObject::addStaff(TstaffObject* st) { m_staves.append(st); if (m_staves.count() == 1) // initialize first measure of first staff st->appendMeasure(m_measures.first()); + // next staves position ca be set only when staffItem is set, see TstaffObject::setStaffItem() then connect(st, &TstaffObject::hiNotePosChanged, [=](int staffNr, qreal offset){ for (int i = staffNr; i < m_staves.size(); ++i) // move every staff about offset m_staves[i]->staffItem()->setY(m_staves[i]->staffItem()->y() + offset); @@ -209,6 +202,27 @@ void TscoreObject::addStaff(TstaffObject* st) { } +void TscoreObject::shiftMeasures(int firstId, int count) { + qDebug() << "[TscoreObject] shifting" << count << "measure(s) from" << firstId; + auto sourceStaff = m_measures[firstId]->staff(); + TstaffObject* targetStaff = nullptr; + if (sourceStaff == lastStaff()) { // create new staff to shift measure(s) there + emit staffCreate(); + targetStaff = lastStaff(); + } else + targetStaff = m_staves[sourceStaff->number()]; + + for (int m = firstId; m < firstId + count; ++m) { + auto meas = m_measures[m]; + for (int n = 0; n < meas->noteCount(); ++n) + meas->note(n)->object()->setStaff(targetStaff); + + sourceStaff->takeMeasure(meas->number() - sourceStaff->firstMeasureNr()); + targetStaff->appendMeasure(meas); + } + targetStaff->refresh(); +} + //################################################################################################# //################### PRIVATE ############################################ //################################################################################################# diff --git a/src/libs/core/score/tscoreobject.h b/src/libs/core/score/tscoreobject.h index bfa482eea..9ef6a1096 100644 --- a/src/libs/core/score/tscoreobject.h +++ b/src/libs/core/score/tscoreobject.h @@ -116,7 +116,15 @@ public: signals: void meterChanged(); + + /** + * Asks Score.qml about create new staff + */ void staffCreate(); + + /** + * Informs Score.qml that content widget height has to be adjusted to all staves height + */ void stavesHeightChanged(); protected: @@ -124,6 +132,13 @@ protected: TclefOffset clefOffset() const { return m_clefOffset; } + /** + * Shifts @p count measures starting from @p firstId among all score measures. + * It detects the staff the measure belongs to and finds the next one or creates a new staff if not exists. + * Also it manages ownership of initial (source) staff shifted notes and sets parent of every such note to target staff. + */ + void shiftMeasures(int firstId, int count = 1); + private: /** * Appends notes to @p m_notes list, creates corresponding @p TnotePair diff --git a/src/libs/core/score/tstaffobject.cpp b/src/libs/core/score/tstaffobject.cpp index c6c4b0cc2..404aabc12 100644 --- a/src/libs/core/score/tstaffobject.cpp +++ b/src/libs/core/score/tstaffobject.cpp @@ -31,20 +31,18 @@ TstaffObject::TstaffObject(QObject* parent) : m_score(nullptr), m_upperLine(16.0), m_staffItem(nullptr), - m_loNotePos(28.0), m_hiNotePos(12.0) + m_loNotePos(28.0), m_hiNotePos(8.0) { } -TstaffObject::~TstaffObject() { - qDebug() << debug() << "is going delete"; +TstaffObject::~TstaffObject() { + qDebug() << "[TstaffObject] is going delete"; } void TstaffObject::setScore(TscoreObject* s) { m_score = s; - setParent(s); - qDebug() << debug() << this << "got a score parent" << s; m_score->addStaff(this); } @@ -63,16 +61,15 @@ void TstaffObject::setUpperLine(qreal upLine) { void TstaffObject::setStaffItem(QQuickItem* si) { m_staffItem = si; - if (m_score->stavesCount() > 1) { + if (m_score->stavesCount() > 1) { // initial staff position, depends on lowest note in the previous staff auto prevStaff = m_score->staff(m_score->stavesCount() - 2); - m_staffItem->setY(prevStaff->staffItem()->y() + prevStaff->minHeight() * prevStaff->scale()); // scale of this staff is not set yet + m_staffItem->setY(prevStaff->staffItem()->y() + (prevStaff->loNotePos() - hiNotePos()) * prevStaff->scale()); // scale of this staff is not set yet } - qDebug() << debug() << "set staff item to" << m_staffItem; } int TstaffObject::firstMeasureNr() { - return m_measures.first()->number(); + return m_measures.empty() ? 0 : m_measures.first()->number(); } @@ -111,28 +108,38 @@ void TstaffObject::fit() { } qreal factor = 2.5; - qreal m_gapsSum = 0.0; - qreal m_allNotesWidth = 0.0; + qreal gapsSum = 0.0; + m_allNotesWidth = 0.0; for (int m = 0; m < m_measures.size(); ++m) { auto measure = m_measures[m]; for (int n = 0; n < measure->noteCount(); ++n) { auto note = measure->note(n)->object(); - m_gapsSum += note->rhythmFactor(); + gapsSum += note->rhythmFactor(); m_allNotesWidth += note->width(); - if (n > 1) { - factor = (m_staffItem->width() - m_notesIndent - m_allNotesWidth - 1.0) / m_gapsSum; - // if (factor < 1.0) { // shift current measure and the next ones - // needShift = true; - // break; // rest of the notes goes to the next staff - // } + factor = (m_staffItem->width() - m_notesIndent - m_allNotesWidth - 1.0) / gapsSum; + if (factor < 0.8) { // shift current measure and the next ones + if (m == 0) + qDebug() << debug() << "!!!!!! Split this measure among staves !!!!!"; + else { + for (int nn = n; nn >= 0; --nn) { // revert gapsSum and m_allNotesWidth to state at the end of the previous measure + auto revertNote = measure->note(nn)->object(); + gapsSum -= revertNote->rhythmFactor(); + m_allNotesWidth -= revertNote->width(); + } + m_gapFactor = (m_staffItem->width() - m_notesIndent - m_allNotesWidth - 1.0) / gapsSum; // allow factor bigger than 2.5 + m_score->shiftMeasures(measure->number(), m_measures.count() - m); + updateNotesPos(); + return; + } + break; // rest of the notes goes to the next staff } } } m_gapFactor = qBound(0.5, factor, 2.5); // notes in this staff are ready to positioning updateNotesPos(); - qDebug() << debug() << "gap factor" << m_gapFactor << m_allNotesWidth << m_gapsSum; +// qDebug() << debug() << "gap factor" << m_gapFactor << "notes count" << lastMeasure()->last()->index() + 1; } @@ -141,7 +148,8 @@ void TstaffObject::updateNotesPos(int startMeasure) { if (firstMeas->isEmpty()) return; - qDebug() << debug() << "updating notes positions from" << startMeasure << "measure"; + qDebug() << debug() << "updating notes positions from" << startMeasure << "measure" + << "gap factor" << m_gapFactor << "notes count" << lastMeasure()->last()->index() + 1; TnoteObject* prevNote = nullptr; if (startMeasure == 0) firstMeas->first()->object()->setX(m_notesIndent); @@ -178,9 +186,19 @@ void TstaffObject::checkNotesRange(bool doEmit) { void TstaffObject::appendMeasure(TmeasureObject* m) { m->setStaff(this); m_measures << m; + if (m_measures.count() == 1) + emit firstMeasureNrChanged(); } +void TstaffObject::takeMeasure(int id) { + m_measures.removeAt(id); + if (id == 0) + emit firstMeasureNrChanged(); +} + + + //################################################################################################# //################### PRIVATE ############################################ //################################################################################################# diff --git a/src/libs/core/score/tstaffobject.h b/src/libs/core/score/tstaffobject.h index 473b97370..b8ac45e22 100644 --- a/src/libs/core/score/tstaffobject.h +++ b/src/libs/core/score/tstaffobject.h @@ -33,7 +33,7 @@ class Tnote; /** - * + * @class TstaffObject is C++ logic of Staff.qml */ class NOOTKACORE_EXPORT TstaffObject : public QObject { @@ -106,6 +106,11 @@ public: */ qreal minHeight() const { return m_loNotePos - (number() == 0 ? 0.0 : m_hiNotePos); } + /** + * Width of all notes on the staff + */ + qreal allNotesWidth() { return m_allNotesWidth; } + /** * Scaling factor of the staff */ @@ -133,6 +138,7 @@ protected: void checkNotesRange(bool doEmit = true); void appendMeasure(TmeasureObject* m); + void takeMeasure(int id); private: void findLowestNote(); /**< Checks all Y positions of staff notes to find lowest one */ @@ -146,6 +152,7 @@ private: qreal m_notesIndent; qreal m_gapFactor; qreal m_loNotePos, m_hiNotePos; + qreal m_allNotesWidth = 0.0; }; #endif // TSTAFFOBJECT_H diff --git a/src/qml/score/Score.qml b/src/qml/score/Score.qml index c1678564f..dcf57b8c1 100644 --- a/src/qml/score/Score.qml +++ b/src/qml/score/Score.qml @@ -37,6 +37,7 @@ Flickable { score.contentY = score.contentHeight - score.height lastStaff.keySignature.onKeySignatureChanged.connect(setKeySignature) } + onStavesHeightChanged: score.contentHeight = Math.max(stavesHeight, score.height) } property alias scale: staff0.scale @@ -61,7 +62,7 @@ Flickable { width: parent.width contentWidth: score.width - contentHeight: scoreObj.stavesHeight +// contentHeight: Math.max(scoreObj.stavesHeight, score.height) Rectangle { id: bgRect -- GitLab