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

Music should automatically clear headers and footers #9193

Merged
merged 8 commits into from Oct 7, 2021
67 changes: 45 additions & 22 deletions src/engraving/layout/layoutpage.cpp
Expand Up @@ -102,8 +102,11 @@ void LayoutPage::collectPage(const LayoutOptions& options, LayoutContext& ctx)
{
const qreal slb = ctx.score()->styleP(Sid::staffLowerBorder);
bool breakPages = ctx.score()->layoutMode() != LayoutMode::SYSTEM;
qreal ey = ctx.page->height() - ctx.page->bm();
qreal y = 0.0;
qreal footerExtension = ctx.page->footerExtension();
qreal headerExtension = ctx.page->headerExtension();
qreal headerFooterPadding = ctx.score()->styleP(Sid::staffHeaderFooterPadding);
qreal endY = ctx.page->height() - ctx.page->bm();
qreal y = 0.0;

System* nextSystem = 0;
int systemIdx = -1;
Expand Down Expand Up @@ -137,7 +140,9 @@ void LayoutPage::collectPage(const LayoutOptions& options, LayoutContext& ctx)
} else {
// this is the first system on page
if (ctx.curSystem->vbox()) {
distance = 0.0;
// if the header exists and there is a frame, move the frame downwards
// to avoid collisions
distance = headerExtension ? headerExtension + headerFooterPadding : 0.0;
} else {
distance = ctx.score()->styleP(Sid::staffUpperBorder);
bool fixedDistance = false;
Expand All @@ -157,7 +162,12 @@ void LayoutPage::collectPage(const LayoutOptions& options, LayoutContext& ctx)
}
}
if (!fixedDistance) {
distance = qMax(distance, ctx.curSystem->minTop());
qreal top = ctx.curSystem->minTop();
// ensure it doesn't collide with header
if (headerExtension > 0.0) {
top += headerExtension + headerFooterPadding;
}
distance = qMax(distance, top);
}
}
}
Expand Down Expand Up @@ -202,16 +212,29 @@ void LayoutPage::collectPage(const LayoutOptions& options, LayoutContext& ctx)
Box* vbox = ctx.curSystem->vbox();
if (vbox) {
dist += vbox->bottomGap();
if (footerExtension > 0) {
dist += footerExtension;
}
} else if (!ctx.prevSystem->hasFixedDownDistance()) {
qreal margin = qMax(ctx.curSystem->minBottom(), ctx.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(ctx.prevSystem->minBottom(), ctx.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(ctx, ctx.page, ey - (y + dist));
layoutPage(ctx, ctx.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 Expand Up @@ -307,7 +330,7 @@ void LayoutPage::collectPage(const LayoutOptions& options, LayoutContext& ctx)
// systems.
//---------------------------------------------------------

void LayoutPage::layoutPage(const LayoutContext& ctx, Page* page, qreal restHeight)
void LayoutPage::layoutPage(const LayoutContext& ctx, Page* page, qreal restHeight, qreal footerPadding)
{
if (restHeight < 0.0) {
qDebug("restHeight < 0.0: %f\n", restHeight);
Expand Down Expand Up @@ -349,7 +372,7 @@ void LayoutPage::layoutPage(const LayoutContext& ctx, Page* page, qreal restHeig
system->move(PointF(0.0, y));
}
} else if ((score->layoutMode() != LayoutMode::SYSTEM) && score->enableVerticalSpread()) {
distributeStaves(ctx, page);
distributeStaves(ctx, page, footerPadding);
}

// system dividers
Expand Down Expand Up @@ -454,7 +477,7 @@ void LayoutPage::checkDivider(const LayoutContext& ctx, bool left, System* s, qr
}
}

void LayoutPage::distributeStaves(const LayoutContext& ctx, Page* page)
void LayoutPage::distributeStaves(const LayoutContext& ctx, Page* page, qreal footerPadding)
{
Score* score = ctx.score();
VerticalGapDataList vgdl;
Expand Down Expand Up @@ -540,27 +563,27 @@ void LayoutPage::distributeStaves(const LayoutContext& ctx, 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));
spaceRemaining -= qMax(0.0, nextSpacer->gap() - spacerOffset - score->styleP(Sid::staffLowerBorder));
}
if (spaceLeft <= 0.0) {
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 (!RealIsNull(spaceLeft) && (ngaps > 0) && (++pass < maxPasses)) {
while (!RealIsNull(spaceRemaining) && (ngaps > 0) && (++pass < maxPasses)) {
ngaps = 0;
qreal smallest { vgdl.smallest() };
qreal nextSmallest { vgdl.smallest(smallest) };
if (RealIsNull(smallest) || RealIsNull(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 };
Expand All @@ -579,38 +602,38 @@ void LayoutPage::distributeStaves(const LayoutContext& ctx, 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 (!RealIsNull(spaceLeft) && !RealIsNull(maxPageFill) && (ngaps > 0) && (++pass < maxPasses)) {
while (!RealIsNull(spaceRemaining) && !RealIsNull(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 (!RealIsNull(res)) {
addedSpace += res * vgd->factor();
++ngaps;
}
}
spaceLeft -= addedSpace;
spaceRemaining -= addedSpace;
}

QSet<System*> systems;
Expand Down
4 changes: 2 additions & 2 deletions src/engraving/layout/layoutpage.h
Expand Up @@ -39,9 +39,9 @@ class LayoutPage
static void collectPage(const LayoutOptions& options, LayoutContext& lc);

private:
static void layoutPage(const LayoutContext& ctx, Ms::Page* page, qreal restHeight);
static void layoutPage(const LayoutContext& ctx, Ms::Page* page, qreal restHeight, qreal footerPadding);
static void checkDivider(const LayoutContext& ctx, bool left, Ms::System* s, qreal yOffset, bool remove = false);
static void distributeStaves(const LayoutContext& ctx, Ms::Page* page);
static void distributeStaves(const LayoutContext& ctx, Ms::Page* page, qreal footerPadding);
};
}

Expand Down
110 changes: 105 additions & 5 deletions src/engraving/libmscore/page.cpp
Expand Up @@ -169,10 +169,26 @@ void Page::draw(mu::draw::Painter* painter) const
//---------------------------------------------------------

void Page::drawHeaderFooter(mu::draw::Painter* 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->moveToDummy();
}

//---------------------------------------------------------
// layoutHeaderFooter
//---------------------------------------------------------

Text* Page::layoutHeaderFooter(int area, const QString& ss) const
{
QString s = replaceTextMacros(ss);
if (s.isEmpty()) {
return;
return nullptr;
}

Text* text;
Expand Down Expand Up @@ -214,10 +230,94 @@ void Page::drawHeaderFooter(mu::draw::Painter* p, int area, const QString& ss) c
text->setAlign(flags);
text->setXmlText(s);
text->layout();
p->translate(text->pos());
text->draw(p);
p->translate(-text->pos());
text->moveToDummy();
return text;
}

//---------------------------------------------------------
// headerExtension
// - how much the header extends into the page (i.e., not in the margins)
//---------------------------------------------------------

qreal Page::headerExtension() const
{
if (!score()->isLayoutMode(LayoutMode::PAGE)) {
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<PointF>().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()->isLayoutMode(LayoutMode::PAGE)) {
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<PointF>().y() * DPMM;
return qMax(0.0, footerHeight - footerOffset);
}

return 0.0;
}

//---------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions src/engraving/libmscore/page.h
Expand Up @@ -60,6 +60,7 @@ class Page final : public EngravingItem

QString replaceTextMacros(const QString&) const;
void drawHeaderFooter(mu::draw::Painter*, int area, const QString&) const;
Text* layoutHeaderFooter(int area, const QString& ss) const;

public:

Expand Down Expand Up @@ -87,6 +88,8 @@ class Page final : public EngravingItem
qreal bm() const;
qreal lm() const;
qreal rm() const;
qreal headerExtension() const;
qreal footerExtension() const;

void draw(mu::draw::Painter*) const override;
void scanElements(void* data, void (* func)(void*, EngravingItem*), bool all=true) override;
Expand Down
7 changes: 6 additions & 1 deletion src/engraving/libmscore/textbase.cpp
Expand Up @@ -1818,8 +1818,11 @@ 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 = *_cursor;
cursor.setRow(0);
cursor.setColumn(0);

int state = 0;
QString token;
Expand Down Expand Up @@ -2009,6 +2012,8 @@ void TextBase::layout1()
}
RectF 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
3 changes: 2 additions & 1 deletion src/engraving/style/styledef.cpp
Expand Up @@ -57,6 +57,7 @@ const std::array<StyleDef::StyleValue, size_t(Sid::STYLES)> StyleDef::styleValue

{ 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::instrumentNameOffset, "instrumentNameOffset", Spatium(1.0) },
{ Sid::akkoladeDistance, "akkoladeDistance", Spatium(6.5) },
Expand Down Expand Up @@ -1115,7 +1116,7 @@ const std::array<StyleDef::StyleValue, size_t(Sid::STYLES)> StyleDef::styleValue
{ Sid::footerFontStyle, "footerFontStyle", int(FontStyle::Normal) },
{ Sid::footerColor, "footerColor", QVariant::fromValue(draw::Color::black) },
{ Sid::footerAlign, "footerAlign", QVariant::fromValue(Align::CENTER | Align::BOTTOM) },
{ Sid::footerOffset, "footerOffset", PointF(0.0, 5.0) },
{ Sid::footerOffset, "footerOffset", PointF(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 src/engraving/style/styledef.h
Expand Up @@ -72,6 +72,7 @@ enum class Sid {

staffUpperBorder,
staffLowerBorder,
staffHeaderFooterPadding,
staffDistance,
instrumentNameOffset,
akkoladeDistance,
Expand Down