diff --git a/libmscore/layout.cpp b/libmscore/layout.cpp index faf118aaa404..6ccd3512dc75 100644 --- a/libmscore/layout.cpp +++ b/libmscore/layout.cpp @@ -1626,7 +1626,7 @@ void Score::fixupLaissezVibrer() // checkDivider //--------------------------------------------------------- -static void checkDivider(bool left, System* s, qreal yOffset, bool remove = false) +void LayoutContext::checkDivider(bool left, System* s, qreal yOffset, bool remove) { SystemDivider* divider = left ? s->systemDividerLeft() : s->systemDividerRight(); if ((s->score()->styleB(left ? Sid::dividerLeft : Sid::dividerRight)) && !remove) { @@ -1672,7 +1672,7 @@ bool inline almostZero(qreal value) // distributeStaves //--------------------------------------------------------- -static void distributeStaves(Page* page) +void LayoutContext::distributeStaves(Page* page, qreal footerPadding) { Score* score { page->score() }; VerticalGapDataList vgdl; @@ -1759,24 +1759,24 @@ static void distributeStaves(Page* page) } --ngaps; - qreal spaceLeft { page->height() - page->bm() - score->styleP(Sid::staffLowerBorder) - yBottom }; + qreal spaceRemaining { page->height() - page->bm() - footerPadding - score->styleP(Sid::staffLowerBorder) - yBottom }; if (nextSpacer) - spaceLeft -= qMax(0.0, nextSpacer->gap() - spacerOffset - score->styleP(Sid::staffLowerBorder)); - if (spaceLeft <= 0.0) + spaceRemaining -= qMax(0.0, nextSpacer->gap() - spacerOffset - score->styleP(Sid::staffLowerBorder)); + if (spaceRemaining <= 0.0) return; // Try to make the gaps equal, taking the spread factors and maximum spacing into account. static const int maxPasses { 20 }; // Saveguard to prevent endless loops. int pass { 0 }; - while (!almostZero(spaceLeft) && (ngaps > 0) && (++pass < maxPasses)) { + while (!almostZero(spaceRemaining) && (ngaps > 0) && (++pass < maxPasses)) { ngaps = 0; qreal smallest { vgdl.smallest() }; qreal nextSmallest { vgdl.smallest(smallest) }; if (almostZero(smallest) || almostZero(nextSmallest)) break; - if ((nextSmallest - smallest) * vgdl.sumStretchFactor() > spaceLeft) - nextSmallest = smallest + spaceLeft/vgdl.sumStretchFactor(); + if ((nextSmallest - smallest) * vgdl.sumStretchFactor() > spaceRemaining) + nextSmallest = smallest + spaceRemaining / vgdl.sumStretchFactor(); qreal addedSpace { 0.0 }; VerticalGapDataList modified; @@ -1792,10 +1792,10 @@ static void distributeStaves(Page* page) modified.append(vgd); ++ngaps; } - if ((spaceLeft - addedSpace) <= 0.0) + if ((spaceRemaining - addedSpace) <= 0.0) break; } - if ((spaceLeft - addedSpace) <= 0.0) + if ((spaceRemaining - addedSpace) <= 0.0) { for (VerticalGapData* vgd : modified) { vgd->undoLastAddSpacing(); @@ -1803,20 +1803,20 @@ static void distributeStaves(Page* page) ngaps = 0; } else { - spaceLeft -= addedSpace; + spaceRemaining -= addedSpace; } } // If there is still space left, distribute the space of the staves. // However, there is a limit on how much space is added per gap. const qreal maxPageFill { score->styleP(Sid::maxPageFillSpread) }; - spaceLeft = qMin(maxPageFill * vgdl.length(), spaceLeft); + spaceRemaining = qMin(maxPageFill * vgdl.length(), spaceRemaining); pass = 0; ngaps = 1; - while (!almostZero(spaceLeft) && !almostZero(maxPageFill) && (ngaps > 0) && (++pass < maxPasses)) { + while (!almostZero(spaceRemaining) && !almostZero(maxPageFill) && (ngaps > 0) && (++pass < maxPasses)) { ngaps = 0; qreal addedSpace { 0.0 }; - qreal step {spaceLeft / vgdl.sumStretchFactor() }; + qreal step {spaceRemaining / vgdl.sumStretchFactor() }; for (VerticalGapData* vgd : vgdl) { qreal res { vgd->addFillSpacing(step, maxPageFill) }; if (!almostZero(res)) { @@ -1824,7 +1824,7 @@ static void distributeStaves(Page* page) ++ngaps; } } - spaceLeft -= addedSpace; + spaceRemaining -= addedSpace; } QSet systems; @@ -1872,7 +1872,7 @@ static void distributeStaves(Page* page) // systems. //--------------------------------------------------------- -static void layoutPage(Page* page, qreal restHeight) +void LayoutContext::layoutPage(Page* page, qreal restHeight, qreal footerPadding) { if (restHeight < 0.0) { qDebug("restHeight < 0.0: %f\n", restHeight); @@ -1914,7 +1914,7 @@ static void layoutPage(Page* page, qreal restHeight) system->move(QPointF(0.0, y)); } else if ((score->layoutMode() != LayoutMode::SYSTEM) && score->enableVerticalSpread()) - distributeStaves(page); + distributeStaves(page, footerPadding); // system dividers for (int i = 0; i < gaps; ++i) { @@ -4742,8 +4742,11 @@ void LayoutContext::collectPage() { const qreal slb = score->styleP(Sid::staffLowerBorder); bool breakPages = score->layoutMode() != LayoutMode::SYSTEM; - qreal ey = page->height() - page->bm(); - qreal y = 0.0; + qreal footerExtension = page->footerExtension(); + qreal headerExtension = page->headerExtension(); + qreal headerFooterPadding = score->styleP(Sid::staffHeaderFooterPadding); + qreal endY = page->height() - page->bm(); + qreal y = 0.0; System* nextSystem = 0; int systemIdx = -1; @@ -4777,8 +4780,11 @@ void LayoutContext::collectPage() distance = prevSystem->minDistance(curSystem); else { // this is the first system on page - if (curSystem->vbox()) - distance = 0.0; + if (curSystem->vbox()) { + // if the header exists and there is a frame, move the frame downwards + // to avoid collisions + distance = headerExtension ? headerExtension + headerFooterPadding : 0.0; + } else { distance = score->styleP(Sid::staffUpperBorder); bool fixedDistance = false; @@ -4796,15 +4802,17 @@ void LayoutContext::collectPage() else distance = qMax(distance, sp->gap()); } -//TODO::ws distance = qMax(distance, -m->staffShape(0).top()); } } - if (!fixedDistance) - distance = qMax(distance, curSystem->minTop()); + if (!fixedDistance) { + qreal top = curSystem->minTop(); + // ensure it doesn't collide with header + if (headerExtension > 0.0) + top += headerExtension + headerFooterPadding; + distance = qMax(distance, top); + } } } -//TODO-ws ?? -// distance += score->staves().front()->userDist(); y += distance; curSystem->setPos(page->lm(), y); @@ -4881,17 +4889,28 @@ void LayoutContext::collectPage() Box* vbox = curSystem->vbox(); if (vbox) { dist += vbox->bottomGap(); + if (footerExtension > 0) + dist += footerExtension; } else if (!prevSystem->hasFixedDownDistance()) { qreal margin = qMax(curSystem->minBottom(), curSystem->spacerDistance(false)); + // ensure it doesn't collide with footer + if (footerExtension > 0) + margin += footerExtension + headerFooterPadding; dist += qMax(margin, slb); } - breakPage = (y + dist) >= ey && breakPages; + breakPage = (y + dist) >= endY && breakPages; } if (breakPage) { qreal dist = qMax(prevSystem->minBottom(), prevSystem->spacerDistance(false)); + qreal footerPadding = 0.0; + // ensure it doesn't collide with footer + if (footerExtension > 0) { + footerPadding = footerExtension + headerFooterPadding; + dist += footerPadding; + } dist = qMax(dist, slb); - layoutPage(page, ey - (y + dist)); + layoutPage(page, endY - (y + dist), footerPadding); // if we collected a system we cannot fit onto this page, // we need to collect next page in order to correctly set system positions if (collected) diff --git a/libmscore/layout.h b/libmscore/layout.h index a68192137c3b..643d1ee9f720 100644 --- a/libmscore/layout.h +++ b/libmscore/layout.h @@ -114,6 +114,11 @@ struct LayoutContext { int adjustMeasureNo(MeasureBase*); void getNextPage(); void collectPage(); + + private: + static void layoutPage(Page* page, qreal restHeight, qreal footerPadding); + static void checkDivider(bool left, System* s, qreal yOffset, bool remove = false); + static void distributeStaves(Page* page, qreal footerPadding); }; //--------------------------------------------------------- diff --git a/libmscore/page.cpp b/libmscore/page.cpp index 8154fb59ee62..b4ba55876a64 100644 --- a/libmscore/page.cpp +++ b/libmscore/page.cpp @@ -147,10 +147,25 @@ void Page::draw(QPainter* painter) const //--------------------------------------------------------- void Page::drawHeaderFooter(QPainter* p, int area, const QString& ss) const + { + Text* text = layoutHeaderFooter(area, ss); + if (!text) + return; + p->translate(text->pos()); + text->draw(p); + p->translate(-text->pos()); + text->setParent(0); + } + +//--------------------------------------------------------- +// layoutHeaderFooter +//--------------------------------------------------------- + +Text* Page::layoutHeaderFooter(int area, const QString& ss) const { QString s = replaceTextMacros(ss); if (s.isEmpty()) - return; + return nullptr; Text* text; if (area < MAX_HEADERS) { @@ -187,10 +202,94 @@ void Page::drawHeaderFooter(QPainter* p, int area, const QString& ss) const text->setAlign(flags); text->setXmlText(s); text->layout(); - p->translate(text->pos()); - text->draw(p); - p->translate(-text->pos()); - text->setParent(0); + return text; + } + +//--------------------------------------------------------- +// headerExtension +// - how much the header extends into the page (i.e., not in the margins) +//--------------------------------------------------------- + +qreal Page::headerExtension() const + { + if (!score()->pageMode()) + return 0.0; + + int n = no() + 1 + score()->pageNumberOffset(); + + QString s1, s2, s3; + + if (score()->styleB(Sid::showHeader) && (no() || score()->styleB(Sid::headerFirstPage))) { + bool odd = (n & 1) || !score()->styleB(Sid::headerOddEven); + if (odd) { + s1 = score()->styleSt(Sid::oddHeaderL); + s2 = score()->styleSt(Sid::oddHeaderC); + s3 = score()->styleSt(Sid::oddHeaderR); + } + else { + s1 = score()->styleSt(Sid::evenHeaderL); + s2 = score()->styleSt(Sid::evenHeaderC); + s3 = score()->styleSt(Sid::evenHeaderR); + } + + Text* headerLeft = layoutHeaderFooter(0, s1); + Text* headerCenter = layoutHeaderFooter(1, s2); + Text* headerRight = layoutHeaderFooter(2, s3); + + qreal headerLeftHeight = headerLeft ? headerLeft->height() : 0.0; + qreal headerCenterHeight = headerCenter ? headerCenter->height() : 0.0; + qreal headerRightHeight = headerRight ? headerRight->height() : 0.0; + + qreal headerHeight = qMax(headerLeftHeight, qMax(headerCenterHeight, headerRightHeight)); + qreal headerOffset = score()->styleV(Sid::headerOffset).value().y() * DPMM; + return qMax(0.0, headerHeight - headerOffset); + } + + return 0.0; + } + +//--------------------------------------------------------- +// footerExtension +// - how much the footer extends into the page (i.e., not in the margins) +//--------------------------------------------------------- + +qreal Page::footerExtension() const + { + if (!score()->pageMode()) + return 0.0; + + int n = no() + 1 + score()->pageNumberOffset(); + + QString s1, s2, s3; + + if (score()->styleB(Sid::showFooter) && (no() || score()->styleB(Sid::footerFirstPage))) { + bool odd = (n & 1) || !score()->styleB(Sid::footerOddEven); + if (odd) { + s1 = score()->styleSt(Sid::oddFooterL); + s2 = score()->styleSt(Sid::oddFooterC); + s3 = score()->styleSt(Sid::oddFooterR); + } + else { + s1 = score()->styleSt(Sid::evenFooterL); + s2 = score()->styleSt(Sid::evenFooterC); + s3 = score()->styleSt(Sid::evenFooterR); + } + + Text* footerLeft = layoutHeaderFooter(3, s1); + Text* footerCenter = layoutHeaderFooter(4, s2); + Text* footerRight = layoutHeaderFooter(5, s3); + + qreal footerLeftHeight = footerLeft ? footerLeft->height() : 0.0; + qreal footerCenterHeight = footerCenter ? footerCenter->height() : 0.0; + qreal footerRightHeight = footerRight ? footerRight->height() : 0.0; + + qreal footerHeight = qMax(footerLeftHeight, qMax(footerCenterHeight, footerRightHeight)); + + qreal footerOffset = score()->styleV(Sid::footerOffset).value().y() * DPMM; + return qMax(0.0, footerHeight - footerOffset); + } + + return 0.0; } #if 0 diff --git a/libmscore/page.h b/libmscore/page.h index bea5ef2271cb..718f83de89fb 100644 --- a/libmscore/page.h +++ b/libmscore/page.h @@ -42,6 +42,7 @@ class Page final : public Element { QString replaceTextMacros(const QString&) const; void drawHeaderFooter(QPainter*, int area, const QString&) const; + Text* layoutHeaderFooter(int area, const QString& ss) const; public: Page(Score*); @@ -65,6 +66,8 @@ class Page final : public Element { qreal bm() const; qreal lm() const; qreal rm() const; + qreal headerExtension() const; + qreal footerExtension() const; void draw(QPainter*) const override; void scanElements(void* data, void (*func)(void*, Element*), bool all=true) override; diff --git a/libmscore/style.cpp b/libmscore/style.cpp index f0ac47043eda..e0d65200f682 100644 --- a/libmscore/style.cpp +++ b/libmscore/style.cpp @@ -76,6 +76,7 @@ static const StyleType styleTypes[] { { Sid::staffUpperBorder, "staffUpperBorder", Spatium(7.0) }, { Sid::staffLowerBorder, "staffLowerBorder", Spatium(7.0) }, + { Sid::staffHeaderFooterPadding, "staffHeaderFooterPadding", Spatium(1.0) }, { Sid::staffDistance, "staffDistance", Spatium(6.5) }, { Sid::akkoladeDistance, "akkoladeDistance", Spatium(6.5) }, { Sid::minSystemDistance, "minSystemDistance", Spatium(8.5) }, @@ -1119,7 +1120,7 @@ static const StyleType styleTypes[] { { Sid::footerFontStyle, "footerFontStyle", int(FontStyle::Normal) }, { Sid::footerColor, "footerColor", QColor(0, 0, 0, 255) }, { Sid::footerAlign, "footerAlign", QVariant::fromValue(Align::CENTER | Align::BOTTOM) }, - { Sid::footerOffset, "footerOffset", QPointF(0.0, 5.0) }, + { Sid::footerOffset, "footerOffset", QPointF(0.0, 0.0) }, { Sid::footerFrameType, "footerFrameType", int(FrameType::NO_FRAME) }, { Sid::footerFramePadding, "footerFramePadding", 0.2 }, { Sid::footerFrameWidth, "footerFrameWidth", 0.1 }, diff --git a/libmscore/style.h b/libmscore/style.h index ebf0d241ccb6..695e9b55570f 100644 --- a/libmscore/style.h +++ b/libmscore/style.h @@ -64,6 +64,7 @@ enum class Sid { staffUpperBorder, staffLowerBorder, + staffHeaderFooterPadding, staffDistance, akkoladeDistance, minSystemDistance, diff --git a/libmscore/textbase.cpp b/libmscore/textbase.cpp index 480c4ac1363f..f5fc91f17852 100644 --- a/libmscore/textbase.cpp +++ b/libmscore/textbase.cpp @@ -1627,7 +1627,8 @@ static qreal parseNumProperty(const QString& s) void TextBase::createLayout() { - _layout.clear(); // deletes the text fragments so we lose all formatting information + // reset all previous formatting information + _layout.clear(); TextCursor cursor(this); cursor.init(); @@ -1798,6 +1799,8 @@ void TextBase::layout1() _layout.append(TextBlock()); QRectF bb; qreal y = 0; + + // adjust the bounding box for the text item for (int i = 0; i < rows(); ++i) { TextBlock* t = &_layout[i]; t->layout(this);