-
Notifications
You must be signed in to change notification settings - Fork 3k
Make lyric lines selectable and editable #31429
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
base: master
Are you sure you want to change the base?
Changes from all commits
402311e
1c52916
3062937
1d7a75e
a2b16b3
ec2563e
0fc5e70
bf3cde4
3135cbf
ffd9830
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -596,14 +596,6 @@ void LineSegment::rebaseAnchors(EditData& ed, Grip grip) | |
| return; | ||
| } | ||
|
|
||
| if (isTrillSegment()) { | ||
| EngravingItem* startElement = spanner()->startElement(); | ||
| if (startElement && startElement->isChord() && toChord(startElement)->staffMove() != 0) { | ||
| // This trill is on a cross-staff chord. Don't try to rebase its anchors when dragging. | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // don't change anchors on keyboard adjustment or if Ctrl is pressed | ||
| // (Ctrl+Left/Right is handled elsewhere!) | ||
| if (ed.key == Key_Left || ed.key == Key_Right || ed.key == Key_Up || ed.key == Key_Down || ed.modifiers & ControlModifier) { | ||
|
|
@@ -771,6 +763,8 @@ void LineSegment::dragGrip(EditData& ed) | |
| } | ||
| } | ||
|
|
||
| undoChangeProperty(Pid::GENERATED, false); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels like a slightly weird place to do this, because:
|
||
|
|
||
| EditTimeTickAnchors::updateAnchors(this); | ||
|
|
||
| triggerLayout(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,7 +91,13 @@ TranslatableString Lyrics::subtypeUserName() const | |
|
|
||
| void Lyrics::add(EngravingItem* el) | ||
| { | ||
| LOGD("Lyrics::add: unknown element %s", el->typeName()); | ||
| if (el->isLyricsLine()) { | ||
| LyricsLine* separator = toLyricsLine(el); | ||
| m_separator = separator; | ||
| score()->addUnmanagedSpanner(separator); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you actually understood what "unmanaged spanners" are? Cause I never had the time to properly look into it so I really never did 😅
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No not really! We could probably remove this distinction at this point. |
||
| } else { | ||
| LOGD("Lyrics::add: unknown element %s", el->typeName()); | ||
| } | ||
| } | ||
|
|
||
| //--------------------------------------------------------- | ||
|
|
@@ -107,7 +113,6 @@ void Lyrics::remove(EngravingItem* el) | |
| // be sure each finds a clean context | ||
| LyricsLine* separ = m_separator; | ||
| m_separator = 0; | ||
| separ->resetExplicitParent(); | ||
| separ->removeUnmanaged(); | ||
| } | ||
| } else { | ||
|
|
@@ -591,4 +596,10 @@ void Lyrics::removeInvalidSegments() | |
| } | ||
| } | ||
| } | ||
|
|
||
| void mu::engraving::PartialLyricsLine::setIsEndMelisma(bool val) | ||
| { | ||
| m_isEndMelisma = val; | ||
| styleChanged(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,24 +40,28 @@ namespace mu::engraving { | |
| // LyricsLine | ||
| //--------------------------------------------------------- | ||
|
|
||
| static const ElementStyle lyricsLineElementStyle { | ||
| { Sid::lyricsDashLineThickness, Pid::LINE_WIDTH } | ||
| }; | ||
|
|
||
| LyricsLine::LyricsLine(EngravingItem* parent) | ||
| : SLine(ElementType::LYRICSLINE, parent, ElementFlag::NOT_SELECTABLE) | ||
| : SLine(ElementType::LYRICSLINE, parent) | ||
| { | ||
| setGenerated(true); // no need to save it, as it can be re-generated | ||
| setDiagonal(false); | ||
| setLineWidth(style().styleS(Sid::lyricsDashLineThickness)); | ||
| initElementStyle(&lyricsLineElementStyle); | ||
| setAnchor(Spanner::Anchor::SEGMENT); | ||
| m_nextLyrics = 0; | ||
| setGenerated(true); // no need to save it, as it can be re-generated | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GENERATED is also a flag I believe, so you could add the flag to the constructor's arguments instead of setting it here
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generated needs to be set to true after |
||
| } | ||
|
|
||
| LyricsLine::LyricsLine(const ElementType& type, EngravingItem* parent, ElementFlags f) | ||
| : SLine(type, parent, f) | ||
| { | ||
| setGenerated(true); // no need to save it, as it can be re-generated | ||
| setDiagonal(false); | ||
| setLineWidth(style().styleS(Sid::lyricsDashLineThickness)); | ||
| initElementStyle(&lyricsLineElementStyle); | ||
| setAnchor(Spanner::Anchor::SEGMENT); | ||
| m_nextLyrics = 0; | ||
| setGenerated(true); // no need to save it, as it can be re-generated | ||
| } | ||
|
|
||
| LyricsLine::LyricsLine(const LyricsLine& g) | ||
|
|
@@ -66,15 +70,6 @@ LyricsLine::LyricsLine(const LyricsLine& g) | |
| m_nextLyrics = 0; | ||
| } | ||
|
|
||
| //--------------------------------------------------------- | ||
| // styleChanged | ||
| //--------------------------------------------------------- | ||
|
|
||
| void LyricsLine::styleChanged() | ||
| { | ||
| setLineWidth(style().styleS(Sid::lyricsDashLineThickness)); | ||
| } | ||
|
|
||
| //--------------------------------------------------------- | ||
| // createLineSegment | ||
| //--------------------------------------------------------- | ||
|
|
@@ -128,6 +123,27 @@ bool LyricsLine::setProperty(Pid propertyId, const engraving::PropertyValue& v) | |
| return true; | ||
| } | ||
|
|
||
| PropertyValue LyricsLine::propertyDefault(Pid id) const | ||
| { | ||
| switch (id) { | ||
| case Pid::LINE_WIDTH: | ||
| return styleValue(Pid::LINE_WIDTH, getPropertyStyle(Pid::LINE_WIDTH)); | ||
| case Pid::OFFSET: | ||
| return lyrics() ? lyrics()->offset() : SLine::propertyDefault(id); | ||
| default: | ||
| return SLine::propertyDefault(id); | ||
| } | ||
| } | ||
|
|
||
| Sid LyricsLine::getPropertyStyle(Pid propertyId) const | ||
| { | ||
| if (propertyId == Pid::LINE_WIDTH) { | ||
| return isEndMelisma() ? Sid::lyricsLineThickness : Sid::lyricsDashLineThickness; | ||
| } | ||
|
|
||
| return SLine::getPropertyStyle(propertyId); | ||
| } | ||
|
|
||
| void LyricsLine::doComputeEndElement() | ||
| { | ||
| if (!isEndMelisma()) { | ||
|
|
@@ -146,8 +162,14 @@ void LyricsLine::doComputeEndElement() | |
| // LyricsLineSegment | ||
| //========================================================= | ||
|
|
||
| void LyricsLineSegment::rebaseAnchors(EditData&, Grip) | ||
| { | ||
| // Don't rebase lyric line anchors on drag | ||
| return; | ||
| } | ||
|
|
||
| LyricsLineSegment::LyricsLineSegment(LyricsLine* sp, System* parent) | ||
| : LineSegment(ElementType::LYRICSLINE_SEGMENT, sp, parent, ElementFlag::ON_STAFF | ElementFlag::NOT_SELECTABLE) | ||
| : LineSegment(ElementType::LYRICSLINE_SEGMENT, sp, parent, ElementFlag::ON_STAFF) | ||
| { | ||
| setGenerated(true); | ||
| } | ||
|
|
@@ -168,6 +190,42 @@ double LyricsLineSegment::baseLineShift() const | |
| return -style().styleD(Sid::lyricsDashYposRatio) * segLyrics->fontMetrics().xHeight(); | ||
| } | ||
|
|
||
| PropertyValue LyricsLineSegment::getProperty(Pid propertyId) const | ||
| { | ||
| if (EngravingObject* delegate = propertyDelegate(propertyId)) { | ||
| return delegate->getProperty(propertyId); | ||
| } | ||
|
|
||
| return LineSegment::getProperty(propertyId); | ||
| } | ||
|
|
||
| bool LyricsLineSegment::setProperty(Pid propertyId, const PropertyValue& val) | ||
| { | ||
| if (EngravingObject* delegate = propertyDelegate(propertyId)) { | ||
| return delegate->setProperty(propertyId, val); | ||
| } | ||
|
|
||
| return LineSegment::setProperty(propertyId, val); | ||
| } | ||
|
|
||
| PropertyValue LyricsLineSegment::propertyDefault(Pid propertyId) const | ||
| { | ||
| if (EngravingObject* delegate = propertyDelegate(propertyId)) { | ||
| return delegate->propertyDefault(propertyId); | ||
| } | ||
|
|
||
| return LineSegment::propertyDefault(propertyId); | ||
| } | ||
|
|
||
| EngravingObject* LyricsLineSegment::propertyDelegate(Pid propertyId) const | ||
| { | ||
| if (propertyId == Pid::GENERATED) { | ||
| return lyricsLine(); | ||
| } | ||
|
|
||
| return LineSegment::propertyDelegate(propertyId); | ||
| } | ||
|
|
||
| //========================================================= | ||
| // PartialLyricsLine | ||
| //========================================================= | ||
|
|
@@ -251,7 +309,6 @@ void PartialLyricsLine::doComputeEndElement() | |
|
|
||
| static const ElementStyle partialLyricsLineSegmentElementStyle { | ||
| { Sid::lyricsPlacement, Pid::PLACEMENT }, | ||
| { Sid::lyricsPosBelow, Pid::OFFSET }, | ||
| { Sid::lyricsMinTopDistance, Pid::MIN_DISTANCE }, | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1847,4 +1847,33 @@ std::vector<EngravingItem*> filterTargetElements(const Selection& sel, Engraving | |
| } | ||
| return result; | ||
| } | ||
|
|
||
| Lyrics* searchNextLyrics(Segment* s, staff_idx_t staffIdx, int verse, PlacementV p) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not a big fan of this utils class, as it ends up containing a miscellaneous of things that are completely unrelated to each other, whereas I've always felt that most of these utiliy functions could just be in their related DOM class, perhaps as static functions. But let's rethink about that another time, I've no problem with this staying here for now. |
||
| { | ||
| Lyrics* l = nullptr; | ||
| const Segment* originalSeg = s; | ||
| while ((s = s->next1(SegmentType::ChordRest))) { | ||
| if (!segmentsAreAdjacentInRepeatStructure(originalSeg, s)) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| track_idx_t strack = staffIdx * VOICES; | ||
| track_idx_t etrack = strack + VOICES; | ||
| // search through all tracks of current staff looking for a lyric in specified verse | ||
| for (track_idx_t track = strack; track < etrack; ++track) { | ||
| ChordRest* cr = toChordRest(s->element(track)); | ||
| if (cr) { | ||
| // cr with lyrics found, but does it have a syllable in specified verse? | ||
| l = cr->lyrics(verse, p); | ||
| if (l) { | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| if (l) { | ||
| break; | ||
| } | ||
| } | ||
| return l; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good one, thanks for the tidy up