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

Implement repeat courtesies #26264

Merged
merged 21 commits into from
Feb 7, 2025
Merged

Conversation

miiizen
Copy link
Contributor

@miiizen miiizen commented Jan 28, 2025

This PR implements a selection of style options to create courtesy time signatures, key signatures and clefs before and after repeats and jumps.
Screenshot 2025-01-29 at 13 12 18

  • "Place all changes before the barline" - If a clef, key or time signature applies both to a repeat/jump's destination and continuation, it is moved to the end of the previous measure
    Screenshot 2025-01-29 at 15 40 05
  • Clefs can be placed on either side of a repeat barline
Screenshot 2025-01-29 at 15 41 42 Screenshot 2025-01-29 at 15 43 03
  • Changes can be placed between repeat barlines, or after them
Screenshot 2025-01-29 at 15 43 42
  • If a clef, key or time signature is different at a repeat or jump desitnation, courtesies can be placed before the repeat or jump. Cancellation courtesies can optionally be placed at the continuation from the repeat or jump. Two styles of parentheses are provided
Screenshot 2025-01-29 at 15 47 12 Screenshot 2025-01-29 at 15 47 45
Screen.Recording.2025-01-29.at.15.50.30.mov

@miiizen miiizen force-pushed the repeat-courtesies branch 2 times, most recently from 2bacb0c to e656d9e Compare February 6, 2025 10:32
@miiizen miiizen force-pushed the repeat-courtesies branch 2 times, most recently from 4fd33e2 to beaed54 Compare February 6, 2025 17:40
Copy link
Contributor

@mike-spa mike-spa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely heroic work

@@ -287,6 +287,7 @@ PropertyValue Clef::getProperty(Pid propertyId) const
case Pid::SMALL: return isSmall();
case Pid::CLEF_TO_BARLINE_POS: return m_clefToBarlinePosition;
case Pid::IS_HEADER: return m_isHeader;
case Pid::IS_COURTESY: return _isCourtesy;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_isCourtesy, for consistency

@@ -212,6 +212,8 @@ PropertyValue KeySig::getProperty(Pid propertyId) const
return int(showCourtesy());
case Pid::KEYSIG_MODE:
return int(mode());
case Pid::IS_COURTESY:
return _isCourtesy;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_isCourtesy

@@ -89,6 +89,8 @@ class Clef final : public EngravingItem
OBJECT_ALLOCATOR(engraving, Clef)
DECLARE_CLASSOF(ElementType::CLEF)

M_PROPERTY2(bool, isCourtesy, setIsCourtesy, false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I've used this too in the past, but lately I've always avoided this macro, and taken the boredome to write member/getter/setter. It's boilerplate, but you'll thank yourself for the boilerplate the first time you'll need to place a breakpoint to see who's getting/setting this variable.

void setHasCourtesyTimeSig(bool v) const { setFlag(ElementFlag::TIMESIG, v); }

bool hasCourtesyClef() const { return flag(ElementFlag::CLEF); }
void setHasCourtesyClef(bool v) const { setFlag(ElementFlag::CLEF, v); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps the flags could be better named as COURTESY_TIMESIG, COURTESY_CLEF?

if (seg->segmentType()
& (SegmentType::EndBarLine | SegmentType::TimeSigAnnounce | SegmentType::KeySigAnnounce
| SegmentType::TimeSigRepeatAnnounce | SegmentType::KeySigRepeatAnnounce | SegmentType::TimeSigStartRepeatAnnounce
| SegmentType::KeySigStartRepeatAnnounce)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure you can use some of the aliases you've created among SegmentType to make this statement a bit shorter/more readable?

if (m_segmentType == SegmentType::KeySigAnnounce
|| m_segmentType == SegmentType::KeySigRepeatAnnounce || m_segmentType == SegmentType::KeySigStartRepeatAnnounce) {
toKeySig(el)->setIsCourtesy(true);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and all of the ones above I think you can use the aliases (and also take advantage of the bitwise | operations which are faster)

|| segmentType() == SegmentType::KeySigRepeatAnnounce
|| segmentType() == SegmentType::ClefStartRepeatAnnounce
|| segmentType() == SegmentType::TimeSigStartRepeatAnnounce
|| segmentType() == SegmentType::KeySigStartRepeatAnnounce) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait... why doesn't this function simply return segmentType() != SegmentType::ChordRest? 😅

@@ -2532,11 +2564,15 @@ void Segment::createShape(staff_idx_t staffIdx)
}
}

rendering::score::SegmentLayout::placeParentheses(this, staffIdx);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a tempting thing to do, but it's a mistake that I've made myself before and rolling it back later was ugly. We should never call layout functions from here. This function should have the one and only task of adding the (already laid out) elements to the segment shape, because that's what we expect it to do when we call it. If we let this function actively modify things by doing layout calls we open ouselves to side effects and properly shoot ourselves in the foot (ask me how I know...).

You're calling it here because obvs you need to know the shape of things in order to place the parenthesis correctly. But you can know that shape right after you've laid out the courtesy elements. So perhaps a good place to call this may be instead MeasureLayout::addRepeatContinuationCourtesies?

// if time sig not at beginning of measure => courtesy time sig

// PRE 4.5: if time sig not at beginning of measure => courtesy time sig
// 4.5+ just tag it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YES. This insane thing of inferring that something is a courtesy because it's not at the start of the measure gave me nightmares. Let's purge it wherever we can

@@ -231,3 +233,84 @@ void SegmentLayout::layoutChordsStem(const Segment& segment, track_idx_t startTr
}
}
}

void SegmentLayout::placeParentheses(const Segment* segment, staff_idx_t staffIdx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, just for organization, it's a bit weird to have this in SegmentLayout, given that this is stricly related to all the rest of the courtesy layout code. So I'd rather put this in MeasureLayout together with all the rest. Or, if you want or think it could make sense, even create a new layout class like CourtesyLayout and put everything in there that relates to courtesies

@oktophonie oktophonie added the vtests This PR produces approved changes to vtest results label Feb 7, 2025
Copy link
Contributor

@mike-spa mike-spa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of cleanup comments, but you can do that in the next PR :)

@@ -52,6 +52,8 @@ class TimeSig final : public EngravingItem
OBJECT_ALLOCATOR(engraving, TimeSig)
DECLARE_CLASSOF(ElementType::TIMESIG)

M_PROPERTY2(bool, isCourtesy, setIsCourtesy, false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before about avoiding this macro. And m_isCourtesy


#include "tlayout.h"
#include "chordlayout.h"
#include "horizontalspacing.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can be now removed

@mike-spa mike-spa merged commit a494713 into musescore:master Feb 7, 2025
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
engraving vtests This PR produces approved changes to vtest results
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants