LCOV - code coverage report
Current view: top level - src/engraving/rendering/score - tappinglayout.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 71.9 % 128 92
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) 2023 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              : #include "slurtielayout.h"
      24              : #include "tappinglayout.h"
      25              : #include "tlayout.h"
      26              : #include "textlayout.h"
      27              : 
      28              : #include "dom/chord.h"
      29              : #include "dom/slur.h"
      30              : #include "dom/staff.h"
      31              : #include "dom/system.h"
      32              : 
      33              : using namespace mu::engraving;
      34              : using namespace mu::engraving::rendering::score;
      35              : 
      36          134 : void TappingLayout::layoutTapping(Tapping* item, Tapping::LayoutData* ldata, LayoutContext& ctx)
      37              : {
      38          134 :     IF_ASSERT_FAILED(item->hand() != TappingHand::INVALID) {
      39            0 :         return;
      40              :     }
      41              : 
      42          134 :     const MStyle& style = item->style();
      43          134 :     bool tabStaff = item->staffType()->isTabStaff();
      44              : 
      45          134 :     if (item->hand() == TappingHand::LEFT) {
      46           32 :         layoutLeftHandTapping(item, ldata, style, tabStaff, ctx);
      47              :     } else {
      48          102 :         layoutRightHandTapping(item, ldata, style, tabStaff, ctx);
      49              :     }
      50              : 
      51          134 :     if (ldata->symId != SymId::noSym) {
      52           54 :         ldata->setShape(Shape(item->symBbox(ldata->symId), item));
      53           80 :     } else if (TappingText* text = item->text()) {
      54           48 :         RectF textBbox = text->ldata()->bbox();
      55           48 :         text->setPos(PointF(-textBbox.x(), 0.0));
      56           48 :         ldata->setShape(Shape(textBbox.translated(text->pos()), item));
      57              :     }
      58              : 
      59          134 :     if (tabStaff) {
      60              :         // Slightly increase vertical padding on TAB staves
      61           64 :         item->setMinDistance(item->staff()->lineDistance(item->tick()) * style.styleS(Sid::articulationMinDistance));
      62              :     }
      63              : }
      64              : 
      65           32 : void TappingLayout::layoutLeftHandTapping(Tapping* item, Tapping::LayoutData* ldata, const MStyle& style, bool tabStaff, LayoutContext& ctx)
      66              : {
      67           32 :     updateHalfSlurs(item, style, tabStaff, ctx);
      68              : 
      69           32 :     LHTappingSymbol lhSym = tabStaff ? style.styleV(Sid::lhTappingSymbolTab).value<LHTappingSymbol>()
      70           16 :                             : style.styleV(Sid::lhTappingSymbolNormalStave).value<LHTappingSymbol>();
      71              : 
      72           32 :     bool dontShowSymbols = item->lhShowItems() == LHTappingShowItems::HALF_SLUR;
      73              : 
      74           32 :     if (lhSym != LHTappingSymbol::DOT || dontShowSymbols) {
      75           32 :         ldata->symId = SymId::noSym;
      76              :     }
      77           32 :     if (lhSym != LHTappingSymbol::CIRCLED_T || dontShowSymbols) {
      78           32 :         delete item->text();
      79           32 :         item->setText(nullptr);
      80              :     }
      81              : 
      82           32 :     if (dontShowSymbols) {
      83           32 :         return;
      84              :     }
      85              : 
      86            0 :     if (lhSym == LHTappingSymbol::DOT) {
      87            0 :         ldata->symId = SymId::windClosedHole;
      88            0 :         ldata->setMag(0.5);
      89            0 :     } else if (lhSym == LHTappingSymbol::CIRCLED_T) {
      90            0 :         ldata->setMag(1.0);
      91            0 :         TappingText* text = item->text();
      92            0 :         if (!text) {
      93            0 :             text = new TappingText(item);
      94              :         }
      95            0 :         item->setText(text);
      96            0 :         text->setTrack(item->track());
      97            0 :         text->setXmlText("T");
      98            0 :         text->setFrameType(FrameType::CIRCLE);
      99            0 :         text->setAlign(Align(AlignH::LEFT, AlignV::BASELINE));
     100            0 :         TextLayout::layoutBaseTextBase(text, ctx);
     101              :         // Move the circle up very slightly to look better centered on the T
     102            0 :         text->mutldata()->frame.translate(0.0, -0.02 * text->height());
     103              :     }
     104              : }
     105              : 
     106           32 : void TappingLayout::updateHalfSlurs(Tapping* item, const MStyle& style, bool tabStaff, LayoutContext& ctx)
     107              : {
     108           32 :     Chord* chord = item->parent() && item->parent()->isChord() ? toChord(item->parent()) : nullptr;
     109           32 :     if (!chord) {
     110            0 :         return;
     111              :     }
     112              : 
     113           32 :     bool showHalfSlurAbove = item->lhShowItems() != LHTappingShowItems::SYMBOL;
     114           32 :     if (showHalfSlurAbove) {
     115           32 :         TappingHalfSlur* halfSlurAbove = item->halfSlurAbove();
     116           32 :         if (!halfSlurAbove) {
     117            8 :             halfSlurAbove = new TappingHalfSlur(item);
     118            8 :             item->setHalfSlurAbove(halfSlurAbove);
     119            8 :             halfSlurAbove->setSelected(item->selected());
     120              :         }
     121              : 
     122           32 :         halfSlurAbove->setOwnershipParent(item);
     123           32 :         halfSlurAbove->setIsHalfSlurAbove(true);
     124           32 :         halfSlurAbove->setTrack(item->track());
     125           32 :         halfSlurAbove->setTick(item->tick());
     126           32 :         halfSlurAbove->setTicks(Fraction(0, 1));
     127           32 :         halfSlurAbove->setStartElement(chord);
     128           32 :         halfSlurAbove->setEndElement(chord);
     129           32 :         if (tabStaff) {
     130           16 :             halfSlurAbove->setSlurDirection(DirectionV::UP);
     131              :         }
     132              : 
     133           32 :         layoutHalfSlur(item, halfSlurAbove, ctx);
     134            0 :     } else if (item->halfSlurAbove()) {
     135            0 :         delete item->halfSlurAbove();
     136            0 :         item->setHalfSlurAbove(nullptr);
     137              :     }
     138              : 
     139           16 :     bool showHalfSlurBelow = tabStaff && showHalfSlurAbove && style.styleB(Sid::lhTappingSlurTopAndBottomNoteOnTab)
     140           48 :                              && toChord(item->parent())->notes().size() > 1;
     141           32 :     if (showHalfSlurBelow) {
     142            0 :         TappingHalfSlur* halfSlurBelow = item->halfSlurBelow();
     143            0 :         if (!halfSlurBelow) {
     144            0 :             halfSlurBelow = new TappingHalfSlur(item);
     145            0 :             item->setHalfSlurBelow(halfSlurBelow);
     146              :         }
     147              : 
     148            0 :         halfSlurBelow->setOwnershipParent(item);
     149            0 :         halfSlurBelow->setIsHalfSlurAbove(false);
     150            0 :         halfSlurBelow->setTrack(item->track());
     151            0 :         halfSlurBelow->setTick(item->tick());
     152            0 :         halfSlurBelow->setTicks(Fraction(0, 1));
     153            0 :         halfSlurBelow->setStartElement(chord);
     154            0 :         halfSlurBelow->setEndElement(chord);
     155            0 :         halfSlurBelow->setSlurDirection(DirectionV::DOWN);
     156              : 
     157            0 :         layoutHalfSlur(item, halfSlurBelow, ctx);
     158           32 :     } else if (item->halfSlurBelow()) {
     159            0 :         delete item->halfSlurBelow();
     160            0 :         item->setHalfSlurBelow(nullptr);
     161              :     }
     162              : }
     163              : 
     164           32 : void TappingLayout::layoutHalfSlur(Tapping* item, TappingHalfSlur* slur, LayoutContext& ctx)
     165              : {
     166           32 :     System* system = toSystem(item->findAncestor(ElementType::SYSTEM));
     167           32 :     IF_ASSERT_FAILED(system) {
     168            0 :         return;
     169              :     }
     170              : 
     171           56 :     for (SpannerSegment* seg : slur->spannerSegments()) {
     172           24 :         seg->moveToSystem(nullptr);
     173              :     }
     174              : 
     175           32 :     Skyline& skyline = system->staff(item->staffIdx())->skyline();
     176           32 :     TappingHalfSlurSegment* slurSeg = toTappingHalfSlurSegment(SlurTieLayout::layoutSystem(slur, system, ctx));
     177              : 
     178           32 :     Shape segShape = slurSeg->shape();
     179              : 
     180           32 :     RectF mask(segShape.bbox());
     181           32 :     mask.setWidth(0.5 * mask.width());
     182           32 :     const double maskPad = 0.25 * item->spatium();
     183           32 :     mask.adjust(-maskPad, -maskPad, 0.0, maskPad);
     184           32 :     slurSeg->mutldata()->setMask(mask);
     185              : 
     186           32 :     Shape maskedShape;
     187           32 :     double roundingMargin = 0.05 * item->spatium(); // otherwise rounding errors may affect the threshold
     188          672 :     for (ShapeElement& el : segShape.elements()) {
     189          640 :         if (el.left() > mask.right() - roundingMargin) {
     190          320 :             maskedShape.add(el);
     191              :         }
     192              :     }
     193           32 :     slurSeg->mutldata()->setShape(maskedShape);
     194              : 
     195           32 :     skyline.add(maskedShape.translated(slurSeg->pos()));
     196           32 : }
     197              : 
     198          102 : void TappingLayout::layoutRightHandTapping(Tapping* item, Tapping::LayoutData* ldata, const MStyle& style, bool tabStaff,
     199              :                                            LayoutContext& ctx)
     200              : {
     201          102 :     RHTappingSymbol rhSym = tabStaff ? style.styleV(Sid::rhTappingSymbolTab).value<RHTappingSymbol>()
     202           54 :                             : style.styleV(Sid::rhTappingSymbolNormalStave).value<RHTappingSymbol>();
     203              : 
     204          102 :     if (rhSym != RHTappingSymbol::PLUS) {
     205           48 :         ldata->symId = SymId::noSym;
     206              :     }
     207          102 :     if (rhSym != RHTappingSymbol::T) {
     208           54 :         delete item->text();
     209           54 :         item->setText(nullptr);
     210              :     }
     211              : 
     212          102 :     if (rhSym == RHTappingSymbol::PLUS) {
     213           54 :         ldata->symId = SymId::pluckedLeftHandPizzicato;
     214           48 :     } else if (rhSym == RHTappingSymbol::T) {
     215           48 :         TappingText* text = item->text();
     216           48 :         if (!text) {
     217           12 :             text = new TappingText(item);
     218              :         }
     219           48 :         item->setText(text);
     220           48 :         text->setTrack(item->track());
     221           48 :         text->setXmlText("T");
     222           48 :         text->setAlign(Align(AlignH::LEFT, AlignV::BASELINE));
     223           48 :         TextLayout::layoutBaseTextBase(text, ctx);
     224              :     }
     225          102 : }
        

Generated by: LCOV version 2.0-1