Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #15407: Hide zero-length lyrics dashes #15459

Merged
merged 3 commits into from Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
93 changes: 64 additions & 29 deletions src/engraving/libmscore/lyrics.cpp
Expand Up @@ -29,6 +29,7 @@

#include "measure.h"
#include "mscoreview.h"
#include "navigate.h"
#include "score.h"
#include "segment.h"
#include "staff.h"
Expand Down Expand Up @@ -159,11 +160,11 @@ bool Lyrics::isMelisma() const

// hyphenated?
// if so, it is a melisma only if there is no lyric in same verse on next CR
if (_syllabic == LyricsSyllabic::BEGIN || _syllabic == LyricsSyllabic::MIDDLE) {
if (_separator && (_syllabic == LyricsSyllabic::BEGIN || _syllabic == LyricsSyllabic::MIDDLE)) {
// find next CR on same track and check for existence of lyric in same verse
ChordRest* cr = chordRest();
ChordRest* cr = chordRest();
if (cr) {
Segment* s = cr->segment()->next1();
Segment* s = cr->segment()->next1();
ChordRest* ncr = s ? s->nextChordRest(cr->track()) : 0;
if (ncr && !ncr->lyrics(_no, placement())) {
return true;
Expand Down Expand Up @@ -251,6 +252,33 @@ void Lyrics::layout()
styleChanged();
}

ChordRest* cr = chordRest();
if (_removeInvalidSegments) {
removeInvalidSegments();
} else if (_ticks > Fraction(0, 1) || _syllabic == LyricsSyllabic::BEGIN || _syllabic == LyricsSyllabic::MIDDLE) {
if (!_separator) {
_separator = new LyricsLine(score()->dummy());
_separator->setTick(cr->tick());
score()->addUnmanagedSpanner(_separator);
}
_separator->setParent(this);
_separator->setTick(cr->tick());
// HACK separator should have non-zero length to get its layout
// always triggered. A proper ticks length will be set later on the
// separator layout.
_separator->setTicks(Fraction::fromTicks(1));
_separator->setTrack(track());
_separator->setTrack2(track());
_separator->setVisible(visible());
// bbox().setWidth(bbox().width()); // ??
} else {
if (_separator) {
_separator->removeUnmanaged();
delete _separator;
_separator = 0;
}
}

if (isMelisma() || hasNumber) {
// use the melisma style alignment setting
if (isStyled(Pid::ALIGN)) {
Expand Down Expand Up @@ -288,8 +316,6 @@ void Lyrics::layout()
}
}

ChordRest* cr = chordRest();

if (align() == AlignH::HCENTER) {
//
// center under notehead, not origin
Expand All @@ -305,30 +331,6 @@ void Lyrics::layout()

setPosX(x);

if (_ticks > Fraction(0, 1) || _syllabic == LyricsSyllabic::BEGIN || _syllabic == LyricsSyllabic::MIDDLE) {
if (!_separator) {
_separator = new LyricsLine(score()->dummy());
_separator->setTick(cr->tick());
score()->addUnmanagedSpanner(_separator);
}
_separator->setParent(this);
_separator->setTick(cr->tick());
// HACK separator should have non-zero length to get its layout
// always triggered. A proper ticks length will be set later on the
// separator layout.
_separator->setTicks(Fraction::fromTicks(1));
_separator->setTrack(track());
_separator->setTrack2(track());
_separator->setVisible(visible());
// bbox().setWidth(bbox().width()); // ??
} else {
if (_separator) {
_separator->removeUnmanaged();
delete _separator;
_separator = 0;
}
}

if (_ticks.isNotZero()) {
// set melisma end
ChordRest* ecr = score()->findCR(endTick(), track());
Expand Down Expand Up @@ -547,6 +549,11 @@ void Lyrics::removeFromScore()
delete _separator;
_separator = 0;
}
Lyrics* prev = prevLyrics(this);
if (prev) {
// check to make sure we haven't created an invalid segment by deleting this lyric
prev->setRemoveInvalidSegments();
}
}

//---------------------------------------------------------
Expand Down Expand Up @@ -596,6 +603,11 @@ bool Lyrics::setProperty(Pid propertyId, const PropertyValue& v)
}

_ticks = v.value<Fraction>();
if (_ticks <= Fraction(0, 1)) {
// if no ticks, we have to relayout in order to remove invalid melisma segments
setRemoveInvalidSegments();
layout();
}
break;
case Pid::VERSE:
_no = v.toInt();
Expand Down Expand Up @@ -711,4 +723,27 @@ KerningType Lyrics::doComputeKerningType(const EngravingItem* nextItem) const
}
return KerningType::KERNING;
}

//---------------------------------------------------------
// removeInvalidSegments
//
// Remove lyric-final melisma lines and reset the alignment of the lyric
//---------------------------------------------------------

void Lyrics::removeInvalidSegments()
RomanPudashkin marked this conversation as resolved.
Show resolved Hide resolved
{
_removeInvalidSegments = false;
if (_separator && isMelisma() && _ticks < _separator->startCR()->ticks()) {
setTicks(Fraction(0, 1));
_separator->removeUnmanaged();
delete _separator;
_separator = nullptr;
setAlign(propertyDefault(Pid::ALIGN).value<Align>());
if (_syllabic == LyricsSyllabic::BEGIN || _syllabic == LyricsSyllabic::SINGLE) {
_syllabic = LyricsSyllabic::SINGLE;
} else {
_syllabic = LyricsSyllabic::END;
}
}
}
}
3 changes: 3 additions & 0 deletions src/engraving/libmscore/lyrics.h
Expand Up @@ -55,12 +55,14 @@ class Lyrics final : public TextBase
///< (melisma)
LyricsSyllabic _syllabic;
LyricsLine* _separator;
bool _removeInvalidSegments = false;

friend class Factory;
Lyrics(ChordRest* parent);
Lyrics(const Lyrics&);

bool isMelisma() const;
void removeInvalidSegments();
void undoChangeProperty(Pid id, const PropertyValue&, PropertyFlags ps) override;

protected:
Expand Down Expand Up @@ -103,6 +105,7 @@ class Lyrics final : public TextBase
void setTicks(const Fraction& tick) { _ticks = tick; }
Fraction endTick() const;
void removeFromScore();
void setRemoveInvalidSegments() { _removeInvalidSegments = true; }

using EngravingObject::undoChangeProperty;
void paste(EditData& ed, const String& txt) override;
Expand Down
3 changes: 2 additions & 1 deletion src/engraving/libmscore/lyricsline.cpp
Expand Up @@ -199,7 +199,8 @@ SpannerSegment* LyricsLine::layoutSystem(System* system)
SpannerSegmentType sst;
if (tick() >= stick) {
layout();
if (ticks().isZero()) { // only do layout if some time span
if (ticks().isZero() && isEndMelisma()) { // only do layout if some time span
// dash lines still need to be laid out, though
return nullptr;
}
SLine::layout();
Expand Down
26 changes: 26 additions & 0 deletions src/engraving/libmscore/navigate.cpp
Expand Up @@ -24,6 +24,7 @@

#include "chord.h"
#include "engravingitem.h"
#include "lyrics.h"
#include "measure.h"
#include "measurerepeat.h"
#include "note.h"
Expand Down Expand Up @@ -965,4 +966,29 @@ EngravingItem* Score::prevElement()
}
return score()->firstElement();
}

//---------------------------------------------------------
// prevLyrics
// - find the lyric (if any) before this one (not including lines)
// - currently used to determine the first lyric of a melisma
//---------------------------------------------------------

Lyrics* prevLyrics(const Lyrics* lyrics)
{
track_idx_t currTrack = lyrics->track();
Segment* seg = lyrics->segment();
if (!seg) {
return nullptr;
}
PropertyFlags pFlags = lyrics->propertyFlags(mu::engraving::Pid::PLACEMENT);
Segment* prevSegment = seg;
while ((prevSegment = prevSegment->prev1(mu::engraving::SegmentType::ChordRest))) {
EngravingItem* el = prevSegment->element(currTrack);
Lyrics* prevLyrics = el && el->isChord() ? toChordRest(el)->lyrics(lyrics->no(), lyrics->placement()) : nullptr;
if (prevLyrics) {
return prevLyrics;
}
}
return nullptr;
}
}
2 changes: 2 additions & 0 deletions src/engraving/libmscore/navigate.h
Expand Up @@ -25,9 +25,11 @@

namespace mu::engraving {
class ChordRest;
class Lyrics;

extern int pitch2y(int pitch, int enh, int clefOffset, int key, int& prefix, const char* tversatz);
extern ChordRest* nextChordRest(const ChordRest* cr, bool skipGrace = false, bool skipMeasureRepeatRests = true);
extern ChordRest* prevChordRest(const ChordRest* cr, bool skipGrace = false, bool skipMeasureRepeatRests = true);
extern Lyrics* prevLyrics(const Lyrics* lyrics);
} // namespace mu::engraving
#endif
2 changes: 1 addition & 1 deletion src/engraving/libmscore/spanner.cpp
Expand Up @@ -748,7 +748,7 @@ void Spanner::computeEndElement()
if (endCR() && !endCR()->measure()->isMMRest() && !systemFlag()) {
ChordRest* cr = endCR();
Fraction nticks = cr->tick() + cr->actualTicks() - _tick;
if ((_ticks - nticks).isNotZero()) {
if ((_ticks - nticks) > Fraction(0, 1)) {
LOGD("%s ticks changed, %d -> %d", typeName(), _ticks.ticks(), nticks.ticks());
setTicks(nticks);
if (isOttava()) {
Expand Down
9 changes: 9 additions & 0 deletions src/engraving/libmscore/textedit.cpp
Expand Up @@ -23,9 +23,11 @@
#include "textedit.h"

#include "mscoreview.h"
#include "navigate.h"
#include "score.h"
#include "iengravingfont.h"
#include "types/symnames.h"
#include "lyrics.h"

#include "log.h"

Expand Down Expand Up @@ -187,6 +189,13 @@ void TextBase::endEdit(EditData& ed)
}

commitText();
if (isLyrics()) {
Lyrics* prev = prevLyrics(toLyrics(this));
if (prev) {
prev->setRemoveInvalidSegments();
prev->layout();
}
}
return;
}

Expand Down