LCOV - code coverage report
Current view: top level - src/notationscene/qml/MuseScore/NotationScene - continuouspanel.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 292 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  *
      22              :  */
      23              : 
      24              : #include "continuouspanel.h"
      25              : 
      26              : #include "containers.h"
      27              : 
      28              : #include "draw/painter.h"
      29              : 
      30              : #include "engraving/dom/barline.h"
      31              : #include "engraving/dom/factory.h"
      32              : #include "engraving/dom/instrument.h"
      33              : #include "engraving/dom/keysig.h"
      34              : #include "engraving/dom/measure.h"
      35              : #include "engraving/dom/page.h"
      36              : #include "engraving/dom/part.h"
      37              : #include "engraving/dom/score.h"
      38              : #include "engraving/dom/segment.h"
      39              : #include "engraving/dom/staff.h"
      40              : #include "engraving/dom/stafflines.h"
      41              : #include "engraving/dom/system.h"
      42              : #include "engraving/dom/text.h"
      43              : #include "engraving/dom/timesig.h"
      44              : #include "engraving/rendering/score/tlayout.h"
      45              : 
      46              : #include "notation/inotationelements.h" // IWYU pragma: keep
      47              : 
      48              : #include "log.h"
      49              : 
      50              : using namespace muse;
      51              : using namespace muse::draw;
      52              : using namespace mu::notation;
      53              : using namespace mu::engraving::rendering::score;
      54              : 
      55              : static constexpr bool ACCESSIBILITY_DISABLED = false;
      56              : 
      57            0 : ContinuousPanel::~ContinuousPanel()
      58              : {
      59            0 :     clearCache();
      60            0 : }
      61              : 
      62            0 : void ContinuousPanel::clearCache()
      63              : {
      64            0 :     delete m_cachedMeasureNumberText;
      65            0 :     m_cachedMeasureNumberText = nullptr;
      66              : 
      67            0 :     muse::DeleteAll(m_cachedStaffNameTexts);
      68            0 :     m_cachedStaffNameTexts.clear();
      69              : 
      70            0 :     muse::DeleteAll(m_cachedClefs);
      71            0 :     m_cachedClefs.clear();
      72              : 
      73            0 :     muse::DeleteAll(m_cachedKeySigs);
      74            0 :     m_cachedKeySigs.clear();
      75              : 
      76            0 :     muse::DeleteAll(m_cachedTimeSigs);
      77            0 :     m_cachedTimeSigs.clear();
      78              : 
      79            0 :     muse::DeleteAll(m_cachedBarLines);
      80            0 :     m_cachedBarLines.clear();
      81            0 : }
      82              : 
      83            0 : void ContinuousPanel::ensureCacheSize(size_t staffCount)
      84              : {
      85              :     // Resize vectors if needed and create objects
      86            0 :     if (m_cachedStaffNameTexts.size() < staffCount) {
      87            0 :         m_cachedStaffNameTexts.resize(staffCount, nullptr);
      88              :     }
      89            0 :     if (m_cachedClefs.size() < staffCount) {
      90            0 :         m_cachedClefs.resize(staffCount, nullptr);
      91              :     }
      92            0 :     if (m_cachedKeySigs.size() < staffCount) {
      93            0 :         m_cachedKeySigs.resize(staffCount, nullptr);
      94              :     }
      95            0 :     if (m_cachedTimeSigs.size() < staffCount) {
      96            0 :         m_cachedTimeSigs.resize(staffCount, nullptr);
      97              :     }
      98            0 :     if (m_cachedBarLines.size() < staffCount) {
      99            0 :         m_cachedBarLines.resize(staffCount, nullptr);
     100              :     }
     101            0 : }
     102              : 
     103            0 : void ContinuousPanel::setNotation(INotationPtr notation)
     104              : {
     105            0 :     if (notation == m_notation) {
     106            0 :         return;
     107              :     }
     108              : 
     109            0 :     clearCache();
     110            0 :     m_width = 0;
     111            0 :     m_notation = notation;
     112              : }
     113              : 
     114            0 : qreal ContinuousPanel::width() const
     115              : {
     116            0 :     return m_width;
     117              : }
     118              : 
     119            0 : void ContinuousPanel::paint(Painter& painter, const NotationViewContext& ctx, const engraving::rendering::PaintOptions& opt)
     120              : {
     121            0 :     TRACEFUNC;
     122              : 
     123            0 :     if (!m_notation) {
     124            0 :         return;
     125              :     }
     126              : 
     127            0 :     const engraving::Score* score = m_notation->elements()->msScore();
     128              : 
     129            0 :     double offsetPanel = -ctx.xOffset / ctx.scaling;
     130            0 :     double y = 0;
     131            0 :     double oldWidth = 0;          // The last final panel width
     132            0 :     double newWidth = 0;          // New panel width
     133            0 :     double height = 0;
     134            0 :     double leftMarginTotal = 0;   // Sum of all elements left margin
     135            0 :     double panelRightPadding = 5;    // Extra space for the panel after last element
     136              : 
     137            0 :     const engraving::Measure* measure = score->firstMeasure();
     138            0 :     if (!measure) {
     139            0 :         return;
     140              :     }
     141              : 
     142            0 :     measure = measure->coveringMMRestOrThis();
     143            0 :     if (!measure) {
     144            0 :         return;
     145              :     }
     146              : 
     147            0 :     const engraving::System* system = measure->system();
     148            0 :     if (!system) {
     149            0 :         return;
     150              :     }
     151              : 
     152            0 :     const double spatium = score->style().spatium();
     153            0 :     if (m_width <= 0) {
     154            0 :         m_width = measure->first()->x();
     155              :     }
     156              : 
     157              :     //
     158              :     // Set panel height for whole system
     159              :     //
     160            0 :     height = 6 * spatium;
     161            0 :     y = system->staffYpage(0) + system->page()->pos().y();
     162            0 :     double y2 = 0.0;
     163            0 :     const size_t staffCount = score->nstaves();
     164            0 :     for (mu::engraving::staff_idx_t i = 0; i < staffCount; ++i) {
     165            0 :         mu::engraving::SysStaff* ss = system->staff(i);
     166            0 :         if (!ss->show() || !score->staff(i)->show()) {
     167            0 :             continue;
     168              :         }
     169            0 :         y2 = ss->y() + ss->bbox().height();
     170              :     }
     171            0 :     height += y2 + 6 * spatium;
     172            0 :     y -= 6 * spatium;
     173              : 
     174              :     //
     175              :     // Check elements at current panel position
     176              :     //
     177              : 
     178            0 :     m_rect = RectF(offsetPanel + m_width, y, 1, height);
     179              : 
     180            0 :     mu::engraving::Page* page = score->pages().front();
     181            0 :     std::vector<mu::engraving::EngravingItem*> el = page->items(m_rect);
     182            0 :     if (el.empty()) {
     183            0 :         return;
     184              :     }
     185              : 
     186            0 :     std::stable_sort(el.begin(), el.end(), mu::engraving::elementLessThan);
     187              : 
     188            0 :     const mu::engraving::Measure* currentMeasure = nullptr;
     189            0 :     const bool showInvisible = score->isShowInvisible();
     190            0 :     for (const mu::engraving::EngravingItem* e : el) {
     191            0 :         e->itemDiscovered = false;
     192            0 :         if (!e->visible() && !showInvisible) {
     193            0 :             continue;
     194              :         }
     195              : 
     196            0 :         if (e->isStaffLines()) {
     197            0 :             currentMeasure = toStaffLines(e)->measure();
     198            0 :             break;
     199              :         }
     200              :     }
     201              : 
     202            0 :     if (!currentMeasure) {
     203            0 :         return;
     204              :     }
     205              : 
     206              :     // Don't show panel if staff names are visible
     207            0 :     if (!ctx.fromLogical) {
     208            0 :         return;
     209              :     }
     210              : 
     211            0 :     const double canvasPosX = ctx.fromLogical(currentMeasure->canvasPos()).x();
     212            0 :     if (currentMeasure == score->firstMeasure() && canvasPosX > 0) {
     213            0 :         return;
     214              :     }
     215              : 
     216            0 :     double xPosMeasure = currentMeasure->canvasX();
     217            0 :     const double _measureWidth = currentMeasure->width();
     218            0 :     const engraving::Fraction tick = currentMeasure->tick();
     219            0 :     const engraving::Fraction currentTimeSigFraction = currentMeasure->timesig();
     220              : 
     221              :     //---------------------------------------------------------
     222              :     //   findElementWidths
     223              :     //      determines the max width for each element types
     224              :     //---------------------------------------------------------
     225              : 
     226              :     // The first pass serves to get the maximum width for each elements
     227              : 
     228            0 :     double lineWidthName = 0;
     229            0 :     double widthClef    = 0;
     230            0 :     double widthKeySig  = 0;
     231            0 :     double widthTimeSig = 0;
     232            0 :     double xPosTimeSig  = 0;
     233              : 
     234              :     // Ensure we have a cached segment to use as parent for temporary objects
     235            0 :     Segment* seg = score->tick2segmentMM(tick);
     236              : 
     237              :     // Ensure cache size matches staff count
     238            0 :     ensureCacheSize(staffCount);
     239              : 
     240            0 :     if (!m_cachedMeasureNumberText) {
     241            0 :         m_cachedMeasureNumberText = engraving::Factory::createText(seg, mu::engraving::TextStyleType::DEFAULT, ACCESSIBILITY_DISABLED);
     242            0 :         m_cachedMeasureNumberText->setFlag(engraving::ElementFlag::MOVABLE, false);
     243            0 :         m_cachedMeasureNumberText->setFamily(u"FreeSans");
     244            0 :         m_cachedMeasureNumberText->setSizeIsSpatiumDependent(true);
     245              :     }
     246              : 
     247            0 :     for (const engraving::EngravingItem* e : std::as_const(el)) {
     248            0 :         e->itemDiscovered = false;
     249            0 :         if (!e->visible() && !showInvisible) {
     250            0 :             continue;
     251              :         }
     252              : 
     253            0 :         if (e->isStaffLines()) {
     254            0 :             const engraving::staff_idx_t staffIdx = e->staffIdx();
     255            0 :             const engraving::Staff* currentStaff = score->staff(staffIdx);
     256            0 :             const engraving::Instrument* instrument = currentStaff->part()->instrument(tick);
     257              : 
     258              :             // Staff name
     259            0 :             String staffName = instrument->longName();
     260            0 :             if (staffName.empty()) {
     261            0 :                 staffName = instrument->shortName();
     262              :             }
     263              : 
     264            0 :             engraving::Text*& nameText = m_cachedStaffNameTexts[staffIdx];
     265            0 :             if (!nameText) {
     266            0 :                 nameText = engraving::Factory::createText(seg, mu::engraving::TextStyleType::DEFAULT, ACCESSIBILITY_DISABLED);
     267            0 :                 nameText->setFlag(engraving::ElementFlag::MOVABLE, false);
     268            0 :                 nameText->setFamily(u"FreeSans");
     269            0 :                 nameText->setSizeIsSpatiumDependent(true);
     270              :             }
     271              : 
     272            0 :             nameText->setOwnershipParent(seg);
     273            0 :             nameText->setXmlText(staffName);
     274            0 :             nameText->setTrack(e->track());
     275            0 :             nameText->mutldata()->reset();
     276            0 :             scoreRender()->layoutItem(nameText);
     277            0 :             nameText->setPlainText(nameText->plainText());
     278            0 :             nameText->mutldata()->reset();
     279            0 :             scoreRender()->layoutItem(nameText);
     280              : 
     281            0 :             if (nameText->width() > lineWidthName && !nameText->xmlText().empty()) {
     282            0 :                 lineWidthName = nameText->width();
     283              :             }
     284              : 
     285              :             // Clef
     286            0 :             engraving::Clef*& clef = m_cachedClefs[staffIdx];
     287            0 :             if (!clef) {
     288            0 :                 clef = engraving::Factory::createClef(seg, ACCESSIBILITY_DISABLED);
     289            0 :                 clef->setTrack(e->track());
     290              :             }
     291              : 
     292            0 :             clef->setOwnershipParent(seg);
     293            0 :             engraving::ClefType currentClef = currentStaff->clef(tick);
     294            0 :             clef->setClefType(currentClef);
     295            0 :             clef->mutldata()->reset();
     296            0 :             scoreRender()->layoutItem(clef);
     297            0 :             if (clef->width() > widthClef) {
     298            0 :                 widthClef = clef->width();
     299              :             }
     300              : 
     301              :             // Key Signature
     302            0 :             engraving::KeySig*& keySig = m_cachedKeySigs[staffIdx];
     303            0 :             if (!keySig) {
     304            0 :                 keySig = engraving::Factory::createKeySig(seg, ACCESSIBILITY_DISABLED);
     305            0 :                 keySig->setHideNaturals(true);
     306            0 :                 keySig->setTrack(e->track());
     307              :             }
     308              : 
     309            0 :             keySig->setOwnershipParent(seg);
     310            0 :             engraving::KeySigEvent currentKeySigEvent = currentStaff->keySigEvent(tick);
     311            0 :             keySig->setKeySigEvent(currentKeySigEvent);
     312            0 :             keySig->mutldata()->reset();
     313            0 :             scoreRender()->layoutItem(keySig);
     314            0 :             if (keySig->width() > widthKeySig) {
     315            0 :                 widthKeySig = keySig->width();
     316              :             }
     317              : 
     318              :             // Time Signature
     319            0 :             engraving::TimeSig*& timeSig = m_cachedTimeSigs[staffIdx];
     320            0 :             if (!timeSig) {
     321            0 :                 timeSig = engraving::Factory::createTimeSig(seg, ACCESSIBILITY_DISABLED);
     322            0 :                 timeSig->setTrack(e->track());
     323              :             }
     324            0 :             timeSig->setOwnershipParent(seg);
     325              : 
     326              :             // Try to get local time signature, if not, get the current measure one
     327            0 :             engraving::TimeSig* currentTimeSig = currentStaff->timeSig(tick);
     328            0 :             if (currentTimeSig) {
     329            0 :                 timeSig->setFrom(currentTimeSig);
     330              :             } else {
     331            0 :                 timeSig->setSig(currentTimeSigFraction, TimeSigType::NORMAL);
     332              :             }
     333            0 :             timeSig->mutldata()->reset();
     334            0 :             scoreRender()->layoutItem(timeSig);
     335              : 
     336            0 :             if (timeSig->width() > widthTimeSig) {
     337            0 :                 widthTimeSig = timeSig->width();
     338              :             }
     339              : 
     340              :             // Bar line - layout and store
     341            0 :             engraving::BarLine*& barLine = m_cachedBarLines[staffIdx];
     342            0 :             if (!barLine) {
     343            0 :                 barLine = engraving::Factory::createBarLine(seg, ACCESSIBILITY_DISABLED);
     344            0 :                 barLine->setBarLineType(engraving::BarLineType::NORMAL);
     345            0 :                 barLine->setTrack(e->track());
     346              :             }
     347              : 
     348            0 :             barLine->setOwnershipParent(seg);
     349            0 :             barLine->setSpanStaff(currentStaff->barLineSpan());
     350            0 :             barLine->setSpanFrom(currentStaff->barLineFrom());
     351            0 :             barLine->setSpanTo(currentStaff->barLineTo());
     352            0 :             barLine->mutldata()->reset();
     353            0 :             scoreRender()->layoutItem(barLine);
     354            0 :         }
     355              :     }
     356              : 
     357            0 :     const double clefLeftMargin = score->style().styleAbsolute(engraving::Sid::clefLeftMargin);
     358            0 :     const double keySigLeftMargin = score->style().styleAbsolute(engraving::Sid::keysigLeftMargin);
     359            0 :     const double timeSigLeftMargin = score->style().styleAbsolute(engraving::Sid::timesigLeftMargin);
     360              : 
     361            0 :     leftMarginTotal = clefLeftMargin;
     362            0 :     leftMarginTotal += keySigLeftMargin;
     363            0 :     leftMarginTotal += timeSigLeftMargin;
     364              : 
     365            0 :     newWidth = widthClef + widthKeySig + widthTimeSig + leftMarginTotal + panelRightPadding;
     366            0 :     xPosMeasure -= offsetPanel;
     367              : 
     368            0 :     lineWidthName += spatium + clefLeftMargin + widthClef;
     369            0 :     if (newWidth < lineWidthName) {
     370            0 :         newWidth = lineWidthName;
     371            0 :         oldWidth = 0;
     372              :     }
     373            0 :     if (oldWidth == 0) {
     374            0 :         oldWidth = newWidth;
     375            0 :         m_width = newWidth;
     376            0 :     } else if (newWidth > 0) {
     377            0 :         if (newWidth == m_width) {
     378            0 :             oldWidth = m_width;
     379            0 :             m_width = newWidth;
     380            0 :         } else if (((xPosMeasure <= newWidth) && (xPosMeasure >= oldWidth))
     381            0 :                    || ((xPosMeasure >= newWidth) && (xPosMeasure <= oldWidth))) {
     382            0 :             m_width = xPosMeasure;
     383            0 :         } else if (((xPosMeasure + _measureWidth <= newWidth) && (xPosMeasure + _measureWidth >= oldWidth))
     384            0 :                    || ((xPosMeasure + _measureWidth >= newWidth) && (xPosMeasure + _measureWidth <= oldWidth))) {
     385            0 :             m_width = xPosMeasure + _measureWidth;
     386              :         } else {
     387            0 :             oldWidth = m_width;
     388            0 :             m_width = newWidth;
     389              :         }
     390              :     }
     391              : 
     392            0 :     m_rect = RectF(0, y, m_width, height);
     393              : 
     394              :     //====================
     395              : 
     396            0 :     painter.save();
     397              : 
     398              :     // Draw background rectangle
     399            0 :     PointF pos(offsetPanel, 0);
     400              : 
     401            0 :     painter.translate(pos);
     402            0 :     Pen pen;
     403            0 :     pen.setWidthF(0.0);
     404            0 :     pen.setStyle(PenStyle::NoPen);
     405            0 :     painter.setPen(pen);
     406            0 :     painter.setBrush(notationConfiguration()->foregroundColor());
     407              : 
     408            0 :     RectF bg(m_rect);
     409            0 :     bg.setWidth(widthClef + widthKeySig + widthTimeSig + leftMarginTotal + panelRightPadding);
     410              : 
     411            0 :     const QPixmap& wallpaper = notationConfiguration()->foregroundWallpaper();
     412              : 
     413            0 :     if (notationConfiguration()->foregroundUseColor() || wallpaper.isNull()) {
     414            0 :         painter.fillRect(bg, notationConfiguration()->foregroundColor());
     415              :     } else {
     416            0 :         painter.drawTiledPixmap(bg, wallpaper, bg.topLeft() - PointF(ctx.xOffset, ctx.yOffset));
     417              :     }
     418              : 
     419            0 :     const Color color = engravingConfiguration()->invisibleColor();
     420              : 
     421              :     // Draw measure number
     422            0 :     m_cachedMeasureNumberText->setXmlText(String(u"#%1").arg(currentMeasure->measureNumber() + 1));
     423            0 :     m_cachedMeasureNumberText->setColor(color);
     424            0 :     m_cachedMeasureNumberText->mutldata()->reset();
     425            0 :     m_cachedMeasureNumberText->renderer()->layoutText1(m_cachedMeasureNumberText);
     426            0 :     pos = PointF(clefLeftMargin + widthClef, y + m_cachedMeasureNumberText->height());
     427            0 :     painter.translate(pos);
     428            0 :     m_cachedMeasureNumberText->renderer()->drawItem(m_cachedMeasureNumberText, &painter, opt);
     429              : 
     430            0 :     pos += PointF(offsetPanel, 0);
     431            0 :     painter.translate(-pos);
     432              : 
     433              :     // This second pass draws the elements using already laid-out cached objects
     434            0 :     for (const engraving::EngravingItem* e : std::as_const(el)) {
     435            0 :         if (!e->visible() && !showInvisible) {
     436            0 :             continue;
     437              :         }
     438              : 
     439            0 :         if (e->isStaffLines()) {
     440            0 :             painter.save();
     441            0 :             const engraving::staff_idx_t staffIdx = e->staffIdx();
     442            0 :             const engraving::Staff* currentStaff = score->staff(staffIdx);
     443              : 
     444            0 :             pos = PointF(offsetPanel, e->pagePos().y());
     445            0 :             painter.translate(pos);
     446              : 
     447              :             // Staff lines
     448            0 :             engraving::StaffLines newStaffLines(*toStaffLines(e));
     449            0 :             newStaffLines.setOwnershipParent(seg->measure());
     450            0 :             newStaffLines.setTrack(e->track());
     451              :             {
     452            0 :                 LayoutContext cntx(newStaffLines.score());
     453            0 :                 TLayout::layoutForWidth(&newStaffLines, bg.width(), cntx);
     454            0 :             }
     455            0 :             newStaffLines.setColor(color);
     456            0 :             scoreRender()->drawItem(&newStaffLines, &painter, opt);
     457              : 
     458              :             // Barline
     459            0 :             engraving::BarLine*& barLine = m_cachedBarLines[staffIdx];
     460            0 :             barLine->setColor(color);
     461            0 :             barLine->renderer()->drawItem(barLine, &painter, opt);
     462              : 
     463              :             // Staff name
     464            0 :             if (currentStaff->part()->staff(0) == currentStaff) {
     465            0 :                 pos = PointF(clefLeftMargin + widthClef, -spatium * 2);
     466            0 :                 painter.translate(pos);
     467            0 :                 engraving::Text*& staffNameText = m_cachedStaffNameTexts[staffIdx];
     468            0 :                 staffNameText->setColor(color);
     469            0 :                 scoreRender()->drawItem(staffNameText, &painter, opt);
     470              : 
     471            0 :                 painter.translate(-pos);
     472              :             }
     473              : 
     474            0 :             double posX = 0.0;
     475              : 
     476              :             // Clef
     477            0 :             engraving::Clef*& clef = m_cachedClefs[staffIdx];
     478            0 :             clef->setColor(color);
     479            0 :             posX += clefLeftMargin;
     480            0 :             const PointF clefPos = PointF(posX, clef->pos().y());
     481            0 :             painter.translate(clefPos);
     482            0 :             scoreRender()->drawItem(clef, &painter, opt);
     483            0 :             painter.translate(-clefPos);
     484            0 :             posX += widthClef;
     485              : 
     486              :             // Key signature
     487            0 :             engraving::KeySig*& keySig = m_cachedKeySigs[staffIdx];
     488            0 :             keySig->setColor(color);
     489            0 :             posX += keySigLeftMargin;
     490            0 :             const PointF ksPos = PointF(posX, 0.0);
     491            0 :             painter.translate(ksPos);
     492            0 :             scoreRender()->drawItem(keySig, &painter, opt);
     493            0 :             painter.translate(-ksPos);
     494              : 
     495            0 :             posX += widthKeySig + xPosTimeSig;
     496              : 
     497              :             // Time signature
     498            0 :             engraving::TimeSig*& timeSig = m_cachedTimeSigs[staffIdx];
     499            0 :             timeSig->setColor(color);
     500            0 :             posX += timeSigLeftMargin;
     501            0 :             const PointF tsPos = PointF(posX, 0.0);
     502            0 :             painter.translate(tsPos);
     503            0 :             scoreRender()->drawItem(timeSig, &painter, opt);
     504            0 :             painter.translate(-tsPos);
     505              : 
     506            0 :             painter.restore();
     507            0 :         }
     508              :     }
     509              : 
     510            0 :     painter.restore();
     511            0 : }
        

Generated by: LCOV version 2.0-1