From facefb9a223280940a46d8ad7677ec383ce9ce11 Mon Sep 17 00:00:00 2001 From: mnorrisvuw Date: Sun, 13 Sep 2026 16:31:12 +1200 Subject: [PATCH 1/2] Align tempo markings with preceding grace notes (#33949) --- src/engraving/rendering/score/tlayout.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/engraving/rendering/score/tlayout.cpp b/src/engraving/rendering/score/tlayout.cpp index 74ca225fce31b..ccbaf0e48447d 100644 --- a/src/engraving/rendering/score/tlayout.cpp +++ b/src/engraving/rendering/score/tlayout.cpp @@ -5757,6 +5757,23 @@ void TLayout::layoutTempoText(const TempoText* item, TempoText::LayoutData* ldat // tempo text on first chordrest of measure should align over time sig if present, unless time sig is above staff Segment* s = item->segment(); + // Align with preceding grace notes on all visible staves, unless the time signature overrides it below. + double graceNoteX = 0.0; + const System* system = s->measure()->system(); + for (track_idx_t track = 0; track < staff2track(item->score()->nstaves()); ++track) { + if (!item->score()->staff(track2staff(track))->show() + || (system && !system->staff(track2staff(track))->show())) { + continue; + } + const EngravingItem* element = s->element(track); + if (!element || !element->isChord()) { + continue; + } + for (const Chord* grace : toChord(element)->graceNotesBefore()) { + graceNoteX = std::min(graceNoteX, grace->pagePos().x() - s->pagePos().x()); + } + } + RehearsalMark* rehearsalMark = toRehearsalMark(s->findAnnotation(ElementType::REHEARSAL_MARK, item->track(), item->track())); RectF rehearsalMarkBbox = rehearsalMark ? rehearsalMark->ldata()->bbox().translated(rehearsalMark->pos()) : RectF(); RectF thisBbox = ldata->bbox().translated(item->pos()); @@ -5764,6 +5781,7 @@ void TLayout::layoutTempoText(const TempoText* item, TempoText::LayoutData* ldat if (s->rtick().isZero()) { Segment* p = item->segment()->prev(SegmentType::TimeSig); if (p && !p->allElementsInvisible()) { + graceNoteX = 0.0; ldata->moveX(-(s->x() - p->x())); EngravingItem* e = p->element(item->staffIdx() * VOICES); if (e) { @@ -5772,6 +5790,8 @@ void TLayout::layoutTempoText(const TempoText* item, TempoText::LayoutData* ldat } } + ldata->moveX(graceNoteX); + if (rehearsalMark) { const bool sameSide = item->placeAbove() == rehearsalMark->placeAbove(); const bool collision From e71ac345737f01e8af7c389ddeb9b40d00e2e214 Mon Sep 17 00:00:00 2001 From: mnorrisvuw Date: Tue, 15 Sep 2026 15:44:46 +1200 Subject: [PATCH 2/2] Resolve visible time signatures for tempo alignment --- src/engraving/rendering/score/tlayout.cpp | 39 +++++++++++++++++++---- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/engraving/rendering/score/tlayout.cpp b/src/engraving/rendering/score/tlayout.cpp index ccbaf0e48447d..905f03e2f6519 100644 --- a/src/engraving/rendering/score/tlayout.cpp +++ b/src/engraving/rendering/score/tlayout.cpp @@ -5778,14 +5778,41 @@ void TLayout::layoutTempoText(const TempoText* item, TempoText::LayoutData* ldat RectF rehearsalMarkBbox = rehearsalMark ? rehearsalMark->ldata()->bbox().translated(rehearsalMark->pos()) : RectF(); RectF thisBbox = ldata->bbox().translated(item->pos()); - if (s->rtick().isZero()) { + const staff_idx_t tempoStaffIdx = item->effectiveStaffIdx(); + if (s->rtick().isZero() && tempoStaffIdx != muse::nidx) { Segment* p = item->segment()->prev(SegmentType::TimeSig); if (p && !p->allElementsInvisible()) { - graceNoteX = 0.0; - ldata->moveX(-(s->x() - p->x())); - EngravingItem* e = p->element(item->staffIdx() * VOICES); - if (e) { - ldata->moveX(p->hasTimeSigAboveStaves() ? e->x() + e->width() + e->spatium() : e->x()); + const TimeSig* timeSig = nullptr; + const TimeSig* firstVisibleTimeSig = nullptr; + for (const EngravingItem* element : p->elist()) { + if (!element || !element->isTimeSig() || !element->visible()) { + continue; + } + const TimeSig* candidate = toTimeSig(element); + const staff_idx_t staffIdx = candidate->effectiveStaffIdx(); + if (!candidate->showOnThisStaff() || staffIdx == muse::nidx || candidate->ldata()->bbox().isEmpty() + || !item->score()->staff(staffIdx)->show() || (system && !system->staff(staffIdx)->show())) { + continue; + } + if (!firstVisibleTimeSig || staffIdx < firstVisibleTimeSig->effectiveStaffIdx()) { + firstVisibleTimeSig = candidate; + } + if (candidate->isAboveStaves() || candidate->isAcrossStaves()) { + if (staffIdx <= tempoStaffIdx + && (!timeSig || staffIdx > timeSig->effectiveStaffIdx())) { + timeSig = candidate; + } + } else if (staffIdx == tempoStaffIdx) { + timeSig = candidate; + } + } + if (!timeSig) { + timeSig = firstVisibleTimeSig; + } + if (timeSig) { + graceNoteX = 0.0; + ldata->moveX(-(s->x() - p->x())); + ldata->moveX(timeSig->isAboveStaves() ? timeSig->x() + timeSig->width() + timeSig->spatium() : timeSig->x()); } } }