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 : }
|