From a107c901c524bfa28a272a147088a22cc2e7a8d3 Mon Sep 17 00:00:00 2001 From: asattely Date: Mon, 15 May 2023 22:45:34 -0400 Subject: [PATCH 1/3] allow melisma to be deleted on chordrest duration change --- src/engraving/layout/v0/lyricslayout.cpp | 8 +++++--- src/engraving/libmscore/lyrics.cpp | 12 ++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/engraving/layout/v0/lyricslayout.cpp b/src/engraving/layout/v0/lyricslayout.cpp index b69d588621a47..82d54e87b9b3a 100644 --- a/src/engraving/layout/v0/lyricslayout.cpp +++ b/src/engraving/layout/v0/lyricslayout.cpp @@ -279,13 +279,15 @@ void LyricsLayout::layout(LyricsLine* item, LayoutContext& ctx) ps = ps->prev1(SegmentType::ChordRest); } if (!ps || ps == lyricsSegment) { - // no valid previous CR, so try to lengthen melisma instead + // either there is no valid previous CR, or the previous CR is the one the lyric starts on + // we don't want to make the melisma longer arbitrarily, but there is a possibility that the next + // CR won't extend the melisma, so let's check it ps = ns; s = ps->nextCR(lyricsTrack, true); EngravingItem* e = s ? s->element(lyricsTrack) : nullptr; // check to make sure we have a chord - if (!e || e->type() != ElementType::CHORD) { - // nothing to do but set ticks to 0 + if (!e || e->type() != ElementType::CHORD || ps->tick() > item->tick() + item->ticks()) { + // nope, nothing to do but set ticks to 0 // this will result in melisma being deleted later item->lyrics()->undoChangeProperty(Pid::LYRIC_TICKS, Fraction::fromTicks(0)); item->setTicks(Fraction(0, 1)); diff --git a/src/engraving/libmscore/lyrics.cpp b/src/engraving/libmscore/lyrics.cpp index 5dd0af8903b7a..a22a286fd7b8b 100644 --- a/src/engraving/libmscore/lyrics.cpp +++ b/src/engraving/libmscore/lyrics.cpp @@ -373,6 +373,9 @@ PropertyValue Lyrics::getProperty(Pid propertyId) const bool Lyrics::setProperty(Pid propertyId, const PropertyValue& v) { + ChordRest* scr = nullptr; + ChordRest* ecr = nullptr; + switch (propertyId) { case Pid::PLACEMENT: setPlacement(v.value()); @@ -389,14 +392,14 @@ bool Lyrics::setProperty(Pid propertyId, const PropertyValue& v) // endTick info is wrong. // Somehow we need to fix this. // See https://musescore.org/en/node/285304 and https://musescore.org/en/node/311289 - ChordRest* ecr = score()->findCR(endTick(), track()); + ecr = score()->findCR(endTick(), track()); if (ecr) { ecr->setMelismaEnd(false); } } - + scr = score()->findCR(tick(), track()); _ticks = v.value(); - if (_ticks <= Fraction(0, 1)) { + if (scr && _ticks <= scr->ticks()) { // if no ticks, we have to relayout in order to remove invalid melisma segments setRemoveInvalidSegments(); layout()->layoutItem(this); @@ -518,8 +521,9 @@ void Lyrics::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps void Lyrics::removeInvalidSegments() { _removeInvalidSegments = false; - if (_separator && isMelisma() && _ticks < _separator->startCR()->ticks()) { + if (_separator && isMelisma() && _ticks <= _separator->startCR()->ticks()) { setTicks(Fraction(0, 1)); + _separator->setTicks(Fraction(0, 1)); _separator->removeUnmanaged(); delete _separator; _separator = nullptr; From d8bdda7f55f201403b15e7cb476a8e557db1b444 Mon Sep 17 00:00:00 2001 From: asattely Date: Tue, 16 May 2023 14:50:57 -0400 Subject: [PATCH 2/3] allow lyric entry to interrupt melisma --- src/engraving/layout/v0/lyricslayout.cpp | 3 ++- src/engraving/layout/v0/tlayout.cpp | 8 +++++++- src/engraving/libmscore/line.cpp | 2 +- src/engraving/libmscore/lyrics.cpp | 3 +-- src/engraving/libmscore/lyrics.h | 2 +- src/engraving/libmscore/textedit.cpp | 20 ++++++++++++++++++++ 6 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/engraving/layout/v0/lyricslayout.cpp b/src/engraving/layout/v0/lyricslayout.cpp index 82d54e87b9b3a..803be75da5e17 100644 --- a/src/engraving/layout/v0/lyricslayout.cpp +++ b/src/engraving/layout/v0/lyricslayout.cpp @@ -135,7 +135,8 @@ void LyricsLayout::layout(Lyrics* item, LayoutContext& ctx) ChordRest* cr = item->chordRest(); if (item->_removeInvalidSegments) { item->removeInvalidSegments(); - } else if (item->_ticks > Fraction(0, 1) || item->_syllabic == LyricsSyllabic::BEGIN || item->_syllabic == LyricsSyllabic::MIDDLE) { + } + if (item->_ticks > Fraction(0, 1) || item->_syllabic == LyricsSyllabic::BEGIN || item->_syllabic == LyricsSyllabic::MIDDLE) { if (!item->_separator) { item->_separator = new LyricsLine(ctx.mutDom().dummyParent()); item->_separator->setTick(cr->tick()); diff --git a/src/engraving/layout/v0/tlayout.cpp b/src/engraving/layout/v0/tlayout.cpp index e26f58c907a55..bdc5379e97298 100644 --- a/src/engraving/layout/v0/tlayout.cpp +++ b/src/engraving/layout/v0/tlayout.cpp @@ -5327,6 +5327,9 @@ SpannerSegment* TLayout::layoutSystemSLine(SLine* line, System* system, LayoutCo SpannerSegment* TLayout::layoutSystem(LyricsLine* line, System* system, LayoutContext& ctx) { + if (!line->lyrics()) { + return nullptr; // a lyrics line with no lyrics shouldn't exist + } Fraction stick = system->firstMeasure()->tick(); Fraction etick = system->lastMeasure()->endTick(); @@ -5402,7 +5405,10 @@ SpannerSegment* TLayout::layoutSystem(LyricsLine* line, System* system, LayoutCo } TLayout::layout(lineSegm, ctx); - + if (!line->lyrics()) { + // this line could have been removed in the process of laying out surrounding lyrics + return nullptr; + } // if temp melisma extend the first line segment to be // after the lyrics syllable (otherwise the melisma segment // will be too short). diff --git a/src/engraving/libmscore/line.cpp b/src/engraving/libmscore/line.cpp index 880c2414ba247..733f64237364e 100644 --- a/src/engraving/libmscore/line.cpp +++ b/src/engraving/libmscore/line.cpp @@ -886,7 +886,7 @@ PointF SLine::linePos(Grip grip, System** sys) const } } } - } else if (isLyricsLine() && toLyrics(explicitParent())->ticks() > Fraction(0, 1)) { + } else if (isLyricsLine() && explicitParent() && toLyrics(explicitParent())->ticks() > Fraction(0, 1)) { // melisma line // it is possible CR won't be in correct track // prefer element in current track if available diff --git a/src/engraving/libmscore/lyrics.cpp b/src/engraving/libmscore/lyrics.cpp index a22a286fd7b8b..ae42ffead0012 100644 --- a/src/engraving/libmscore/lyrics.cpp +++ b/src/engraving/libmscore/lyrics.cpp @@ -521,11 +521,10 @@ void Lyrics::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlags ps void Lyrics::removeInvalidSegments() { _removeInvalidSegments = false; - if (_separator && isMelisma() && _ticks <= _separator->startCR()->ticks()) { + if (_separator && isMelisma() && _ticks < _separator->startCR()->ticks()) { setTicks(Fraction(0, 1)); _separator->setTicks(Fraction(0, 1)); _separator->removeUnmanaged(); - delete _separator; _separator = nullptr; setAlign(propertyDefault(Pid::ALIGN).value()); if (_syllabic == LyricsSyllabic::BEGIN || _syllabic == LyricsSyllabic::SINGLE) { diff --git a/src/engraving/libmscore/lyrics.h b/src/engraving/libmscore/lyrics.h index 993e388f83452..71f54d2434eca 100644 --- a/src/engraving/libmscore/lyrics.h +++ b/src/engraving/libmscore/lyrics.h @@ -141,7 +141,7 @@ class LyricsLine final : public SLine Lyrics* lyrics() const { return toLyrics(explicitParent()); } Lyrics* nextLyrics() const { return _nextLyrics; } - bool isEndMelisma() const { return lyrics()->ticks().isNotZero(); } + bool isEndMelisma() const { return lyrics() && lyrics()->ticks().isNotZero(); } bool isDash() const { return !isEndMelisma(); } bool setProperty(Pid propertyId, const PropertyValue& v) override; diff --git a/src/engraving/libmscore/textedit.cpp b/src/engraving/libmscore/textedit.cpp index f4352354e6f3f..fb8935bb4f379 100644 --- a/src/engraving/libmscore/textedit.cpp +++ b/src/engraving/libmscore/textedit.cpp @@ -222,6 +222,26 @@ void TextBase::endEdit(EditData& ed) } else { triggerLayout(); } + if (isLyrics()) { + Lyrics* prev = prevLyrics(toLyrics(this)); + if (prev) { + if (prev->tick() + prev->ticks() >= tick()) { + // the previous lyric has a spanner attached that goes through this one + // we need to shorten it + Segment* s = score()->tick2segment(tick()); + if (s) { + s = s->prev1(SegmentType::ChordRest); + if (s->tick() > prev->tick()) { + prev->undoChangeProperty(Pid::LYRIC_TICKS, s->tick() - prev->tick()); + } else { + prev->undoChangeProperty(Pid::LYRIC_TICKS, Fraction::fromTicks(1)); + } + } + } + prev->setRemoveInvalidSegments(); + prev->triggerLayout(); + } + } static const double w = 2.0; score()->addRefresh(canvasBoundingRect().adjusted(-w, -w, w, w)); From c7c90e04d1c7d97d0fa717ac0c780f6219ab121b Mon Sep 17 00:00:00 2001 From: asattely Date: Wed, 5 Jul 2023 17:47:41 -0400 Subject: [PATCH 3/3] code review changes --- src/engraving/libmscore/lyrics.cpp | 24 ++++++++++++++++++++++++ src/engraving/libmscore/lyrics.h | 1 + src/engraving/libmscore/textedit.cpp | 21 +++------------------ 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/engraving/libmscore/lyrics.cpp b/src/engraving/libmscore/lyrics.cpp index ae42ffead0012..78a9305e9c06d 100644 --- a/src/engraving/libmscore/lyrics.cpp +++ b/src/engraving/libmscore/lyrics.cpp @@ -313,6 +313,29 @@ bool Lyrics::isEditAllowed(EditData& ed) const return TextBase::isEditAllowed(ed); } +void Lyrics::adjustPrevious() +{ + Lyrics* prev = prevLyrics(toLyrics(this)); + if (prev) { + // search for lyric spanners to split at this point if necessary + if (prev->tick() + prev->ticks() >= tick()) { + // the previous lyric has a spanner attached that goes through this one + // we need to shorten it + Segment* s = score()->tick2segment(tick()); + if (s) { + s = s->prev1(SegmentType::ChordRest); + if (s->tick() > prev->tick()) { + prev->undoChangeProperty(Pid::LYRIC_TICKS, s->tick() - prev->tick()); + } else { + prev->undoChangeProperty(Pid::LYRIC_TICKS, Fraction::fromTicks(1)); + } + } + } + prev->setRemoveInvalidSegments(); + prev->triggerLayout(); + } +} + //--------------------------------------------------------- // endEdit //--------------------------------------------------------- @@ -320,6 +343,7 @@ bool Lyrics::isEditAllowed(EditData& ed) const void Lyrics::endEdit(EditData& ed) { TextBase::endEdit(ed); + triggerLayoutAll(); } diff --git a/src/engraving/libmscore/lyrics.h b/src/engraving/libmscore/lyrics.h index 71f54d2434eca..ca86a36090eef 100644 --- a/src/engraving/libmscore/lyrics.h +++ b/src/engraving/libmscore/lyrics.h @@ -88,6 +88,7 @@ class Lyrics final : public TextBase Fraction endTick() const; void removeFromScore(); void setRemoveInvalidSegments() { _removeInvalidSegments = true; } + void adjustPrevious(); using EngravingObject::undoChangeProperty; void paste(EditData& ed, const String& txt) override; diff --git a/src/engraving/libmscore/textedit.cpp b/src/engraving/libmscore/textedit.cpp index fb8935bb4f379..a95f3561288ea 100644 --- a/src/engraving/libmscore/textedit.cpp +++ b/src/engraving/libmscore/textedit.cpp @@ -223,24 +223,9 @@ void TextBase::endEdit(EditData& ed) triggerLayout(); } if (isLyrics()) { - Lyrics* prev = prevLyrics(toLyrics(this)); - if (prev) { - if (prev->tick() + prev->ticks() >= tick()) { - // the previous lyric has a spanner attached that goes through this one - // we need to shorten it - Segment* s = score()->tick2segment(tick()); - if (s) { - s = s->prev1(SegmentType::ChordRest); - if (s->tick() > prev->tick()) { - prev->undoChangeProperty(Pid::LYRIC_TICKS, s->tick() - prev->tick()); - } else { - prev->undoChangeProperty(Pid::LYRIC_TICKS, Fraction::fromTicks(1)); - } - } - } - prev->setRemoveInvalidSegments(); - prev->triggerLayout(); - } + // we must adjust previous lyrics before the call to commitText(), in order to make the adjustments + // part of the same undo command. there is logic above that will skip this call if the text is empty + toLyrics(this)->adjustPrevious(); } static const double w = 2.0;