-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Implement partial & repeat ties #25745
base: master
Are you sure you want to change the base?
Conversation
Add separate style option for hanging ties
@@ -1080,7 +1080,7 @@ std::set<size_t> CompatMidiRender::getNotesIndexesToRender(Chord* chord) | |||
} | |||
|
|||
auto noteShouldBeRendered = [](Note* n) { | |||
while (n->tieBack() && n != n->tieBack()->startNote()) { | |||
while (n->tieBack() && !n->incomingPartialTie() && n != n->tieBack()->startNote()) { |
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.
I've seen this check repeated several times: every time you are looking for a tie-back-but-not-incoming-partial. Perhaps this is worth adding a tieBackNonPartial()
method?
src/engraving/dom/edit.cpp
Outdated
static Tie* createAndAddTie(Note* startNote, Note* endNote) | ||
{ | ||
Score* score = startNote->score(); | ||
const bool createPartialTie = !endNote; |
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.
I actually find it more readable without this additional bool that's just the negation of another
Tie* tie = toTie(originalNote->tieFor()->linkedClone()); | ||
tie->setScore(score); | ||
newNote->setTieFor(tie); | ||
tie->setStartNote(newNote); | ||
tie->setTrack(newNote->track()); | ||
tieMap.add(originalNote->tieFor(), tie); | ||
} | ||
if (originalNote->tieBack()) { | ||
if (originalNote->tieBack() && originalNote->tieBack()->type() == ElementType::TIE) { |
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.
Same as above (kind of). Maybe we could modify tieBack
and tieFor
to return only "real" ties, and not LV and/or partial ties?
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.
I considered this, but it seemed to me that there were far more scenarios where we want tieFor
and tieBack
to return LVs & partial ties than when we didn't.
src/engraving/dom/measure.cpp
Outdated
|
||
Segment* seg = first(SegmentType::ChordRest); | ||
while (seg) { | ||
for (track_idx_t track = startTrack; track < ntracks; track++) { |
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.
Since you're checking all the available tracks, I think you can more compactly do
for (EngravingItem* item : seg->elist()) {}
src/engraving/dom/note.cpp
Outdated
tieFor()->endNote()->setTieBack(tieFor()); | ||
} | ||
} | ||
|
||
bool Note::followingJumpItem() |
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.
From the name of this function I expected it to return an EngravingItem. You could either rename it into hasFollowingJumpItem
, or it seems like it could also be useful to return the items themselves rather than bools?
src/engraving/dom/note.cpp
Outdated
return false; | ||
} | ||
|
||
String Note::precedingJumpItemName() |
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.
I think this function really should not be here. At a first glance, I thought this was the symmetric of followingJumpItem but it looks like it has a completely different use and the fact that it returns a String left me quite confused.
From what I understand, the string returned by this function has a UI-related purpose which is exclusively referred to partial ties, so it definitely should be in the PartialTie class (perhaps as a static method)
src/engraving/dom/tie.h
Outdated
void toggleEndPoint(const String& id); | ||
|
||
void addTie(TieEndPoint* endPoint); | ||
void removeTie(TieEndPoint* endPoint); |
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.
The fact that there is a public add
method but also an addTie
is a bit odd. Someone reading the public interface of your class wouldn't know why they are different and which one they should use. Could there be a more descriptive name?
src/engraving/dom/tie.cpp
Outdated
{ | ||
Note* note = endPoint->note(); | ||
Score* score = note ? note->score() : nullptr; | ||
Tie* _startTie = startTie(); |
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.
m_startTie
src/engraving/dom/tie.cpp
Outdated
if (std::find_if((*tieEndPoints()).begin(), (*tieEndPoints()).end(), findEndTie) != (*tieEndPoints()).end()) { | ||
continue; | ||
} | ||
score()->undoRemoveElement(tie); |
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.
A method of the Tie class should not be removing other ties from the Score, it could be a bit dangerous. Also, from a function named collectPossibleEndPoints I'd probably expect something which returns me a list of TieEndPoint*, not something which actively modifies the current tie and even the score. Here I would try to do a combination of (not necessarily all of them)
- Giving this method a more descriptive name
- Moving out the code which removes other ties into a separate function
- Given that this method performs an undoable action on the score, it looks more like the kind of methods we usually put in edit.cpp, that's also an option
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.
Yes, this code to remove ties is absolutely in the wrong place - it doesn't even cover all the situations I was hoping.
I think we need to remove stale endpoints when the repeat structure is recalculated - either inside or just after RepeatList::unwind
.
src/engraving/dom/tie.h
Outdated
@@ -25,6 +25,64 @@ | |||
#include "slurtie.h" | |||
|
|||
namespace mu::engraving { | |||
class TieEndPointList; | |||
class TieEndPoint |
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.
Side question: would TieJumpPoint be perhaps a better name for this class? I know what you mean by TieEndPoint, but it feels to me like it doesn't communicate the main purpose. Also because we often call "end point" the graphical end points too.
src/engraving/dom/note.cpp
Outdated
case ElementType::PARTIAL_TIE: { | ||
PartialTie* pt = toPartialTie(e); | ||
pt->setTick(tick()); | ||
if (pt->partialSpannerDirection() == PartialSpannerDirection::OUTGOING) { |
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.
pt->itOutGoing()
?
Resolves: #25426
This PR introduces partial ties to MuseScore, enabling ties to be connected across repeats. When adding a tie before a repeat item (repeat barlines, markers, jumps, and voltas) if any note on the other side of the repeat destination can be tied, a partial tie will be added.
When more than one endpoint is available, clicking the outgoing tie will open a menu of possible endpoints. These can be toggled on and off.
Screen.Recording.2024-12-05.at.14.33.29.mov
Playback implementation to follow.