Skip to content

Commit

Permalink
Fix GH#9043: Music should automatically clear headers and footers
Browse files Browse the repository at this point in the history
Backport of musescore#9193, all 3 parts
  • Loading branch information
Nick-Mazuk authored and Jojo-Schmitz committed Oct 5, 2021
1 parent d2d651e commit 9a2b9f3
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 35 deletions.
75 changes: 47 additions & 28 deletions libmscore/layout.cpp
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -1792,39 +1792,39 @@ 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();
}
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)) {
addedSpace += res * vgd->factor();
++ngaps;
}
}
spaceLeft -= addedSpace;
spaceRemaining -= addedSpace;
}

QSet<System*> systems;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions libmscore/layout.h
Expand Up @@ -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);
};

//---------------------------------------------------------
Expand Down
109 changes: 104 additions & 5 deletions libmscore/page.cpp
Expand Up @@ -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) {
Expand Down Expand Up @@ -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<QPointF>().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<QPointF>().y() * DPMM;
return qMax(0.0, footerHeight - footerOffset);
}

return 0.0;
}

#if 0
Expand Down
3 changes: 3 additions & 0 deletions libmscore/page.h
Expand Up @@ -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*);
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion libmscore/style.cpp
Expand Up @@ -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) },
Expand Down Expand Up @@ -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 },
Expand Down
1 change: 1 addition & 0 deletions libmscore/style.h
Expand Up @@ -64,6 +64,7 @@ enum class Sid {

staffUpperBorder,
staffLowerBorder,
staffHeaderFooterPadding,
staffDistance,
akkoladeDistance,
minSystemDistance,
Expand Down
5 changes: 4 additions & 1 deletion libmscore/textbase.cpp
Expand Up @@ -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();

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 9a2b9f3

Please sign in to comment.