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 : #include <cfloat>
23 :
24 : #include "tlayout.h"
25 :
26 : #include "global/realfn.h"
27 : #include "global/types/number.h"
28 : #include "draw/fontmetrics.h"
29 :
30 : #include "iengravingconfiguration.h" // IWYU pragma: keep
31 : #include "iengravingfont.h"
32 :
33 : #include "infrastructure/rtti.h"
34 : #include "infrastructure/ld_access.h"
35 :
36 : #include "types/typesconv.h"
37 : #include "types/symnames.h"
38 : #include "dom/score.h"
39 : #include "dom/utils.h"
40 :
41 : #include "dom/accidental.h"
42 : #include "dom/ambitus.h"
43 : #include "dom/arpeggio.h"
44 : #include "dom/articulation.h"
45 :
46 : #include "dom/barline.h"
47 : #include "dom/beam.h"
48 : #include "dom/bend.h"
49 : #include "dom/box.h"
50 : #include "dom/bracket.h"
51 : #include "dom/breath.h"
52 :
53 : #include "dom/chord.h"
54 : #include "dom/chordline.h"
55 : #include "dom/clef.h"
56 : #include "dom/capo.h"
57 :
58 : #include "dom/deadslapped.h"
59 : #include "dom/dynamic.h"
60 :
61 : #include "dom/expression.h"
62 :
63 : #include "dom/fermata.h"
64 : #include "dom/figuredbass.h"
65 : #include "dom/fingering.h"
66 : #include "dom/fret.h"
67 :
68 : #include "dom/glissando.h"
69 : #include "dom/gradualtempochange.h"
70 : #include "dom/guitarbend.h"
71 :
72 : #include "dom/hairpin.h"
73 : #include "dom/hammeronpulloff.h"
74 : #include "dom/harppedaldiagram.h"
75 : #include "dom/harmonicmark.h"
76 : #include "dom/harmony.h"
77 : #include "dom/hook.h"
78 :
79 : #include "dom/image.h"
80 : #include "dom/instrchange.h"
81 : #include "dom/instrumentname.h"
82 :
83 : #include "dom/jump.h"
84 :
85 : #include "dom/keysig.h"
86 :
87 : #include "dom/layoutbreak.h"
88 : #include "dom/ledgerline.h"
89 : #include "dom/letring.h"
90 : #include "dom/line.h"
91 : #include "dom/linkedobjects.h"
92 : #include "dom/lyrics.h"
93 :
94 : #include "dom/marker.h"
95 : #include "dom/measure.h"
96 : #include "dom/measurebase.h"
97 : #include "dom/measurenumber.h"
98 : #include "dom/measurenumberbase.h"
99 : #include "dom/measurerepeat.h"
100 : #include "dom/mmrest.h"
101 : #include "dom/mmrestrange.h"
102 :
103 : #include "dom/note.h"
104 : #include "dom/notedot.h"
105 : #include "dom/noteline.h"
106 :
107 : #include "dom/ornament.h"
108 : #include "dom/ottava.h"
109 :
110 : #include "dom/page.h"
111 : #include "dom/palmmute.h"
112 : #include "dom/parenthesis.h"
113 : #include "dom/part.h"
114 : #include "dom/pedal.h"
115 : #include "dom/pickscrape.h"
116 : #include "dom/playtechannotation.h"
117 :
118 : #include "dom/rasgueado.h"
119 : #include "dom/rehearsalmark.h"
120 : #include "dom/rest.h"
121 :
122 : #include "dom/shadownote.h"
123 : #include "dom/slur.h"
124 : #include "dom/spacer.h"
125 : #include "dom/staff.h"
126 : #include "dom/stafflines.h"
127 : #include "dom/staffstate.h"
128 : #include "dom/stafftext.h"
129 : #include "dom/stafftype.h"
130 : #include "dom/stafftypechange.h"
131 : #include "dom/stem.h"
132 : #include "dom/stemslash.h"
133 : #include "dom/sticking.h"
134 : #include "dom/stringtunings.h"
135 : #include "dom/symbol.h"
136 : #include "dom/system.h"
137 : #include "dom/systemdivider.h"
138 : #include "dom/systemlockindicator.h"
139 : #include "dom/systemtext.h"
140 : #include "dom/soundflag.h"
141 :
142 : #include "dom/tapping.h"
143 : #include "dom/tempotext.h"
144 : #include "dom/text.h"
145 : #include "dom/textline.h"
146 : #include "dom/tie.h"
147 : #include "dom/timesig.h"
148 : #include "dom/tremolosinglechord.h"
149 : #include "dom/tremolotwochord.h"
150 : #include "dom/tremolobar.h"
151 : #include "dom/trill.h"
152 : #include "dom/tripletfeel.h"
153 : #include "dom/tuplet.h"
154 :
155 : #include "dom/vibrato.h"
156 : #include "dom/volta.h"
157 :
158 : #include "dom/whammybar.h"
159 :
160 : #include "dom/factory.h"
161 :
162 : #include "editing/editchord.h"
163 : #include "editing/navigation.h"
164 :
165 : #include "accidentalslayout.h"
166 : #include "arpeggiolayout.h"
167 : #include "autoplace.h"
168 : #include "beamlayout.h"
169 : #include "boxlayout.h"
170 : #include "chordlayout.h"
171 : #include "dynamicslayout.h"
172 : #include "guitarbendlayout.h"
173 : #include "lyricslayout.h"
174 : #include "masklayout.h"
175 : #include "slurtielayout.h"
176 : #include "tremololayout.h"
177 : #include "tupletlayout.h"
178 : #include "horizontalspacing.h"
179 : #include "measurelayout.h"
180 : #include "tappinglayout.h"
181 : #include "harmonylayout.h"
182 : #include "markerlayout.h"
183 : #include "measurenumberlayout.h"
184 : #include "parenthesislayout.h"
185 : #include "restlayout.h"
186 : #include "textlayout.h"
187 :
188 : using namespace muse;
189 : using namespace muse::draw;
190 : using namespace mu::engraving;
191 : using namespace mu::engraving::rtti;
192 : using namespace mu::engraving::rendering::score;
193 :
194 : #define LAYOUT_CALL_ITEM(item) LAYOUT_CALL() << LAYOUT_ITEM_INFO(item);
195 :
196 147115 : void TLayout::layoutItem(EngravingItem* item, LayoutContext& ctx)
197 : {
198 : //DO_ASSERT(!ctx.conf().isPaletteMode());
199 :
200 147115 : EngravingItem::LayoutData* ldata = item->mutldata();
201 :
202 147115 : switch (item->type()) {
203 0 : case ElementType::ACCIDENTAL:
204 0 : layoutAccidental(item_cast<const Accidental*>(item), static_cast<Accidental::LayoutData*>(ldata), ctx.conf());
205 0 : break;
206 0 : case ElementType::ACTION_ICON:
207 0 : layoutActionIcon(item_cast<const ActionIcon*>(item), static_cast<ActionIcon::LayoutData*>(ldata));
208 0 : break;
209 0 : case ElementType::AMBITUS:
210 0 : layoutAmbitus(item_cast<const Ambitus*>(item), static_cast<Ambitus::LayoutData*>(ldata), ctx);
211 0 : break;
212 445 : case ElementType::ARPEGGIO:
213 445 : layoutArpeggio(item_cast<const Arpeggio*>(item), static_cast<Arpeggio::LayoutData*>(ldata), ctx.conf());
214 445 : break;
215 0 : case ElementType::CHORD_BRACKET:
216 0 : layoutChordBracket(item_cast<const ChordBracket*>(item), static_cast<ChordBracket::LayoutData*>(ldata), ctx.conf());
217 0 : break;
218 2684 : case ElementType::ARTICULATION:
219 2684 : layoutArticulation(item_cast<Articulation*>(item), static_cast<Articulation::LayoutData*>(ldata));
220 2684 : break;
221 0 : case ElementType::BAR_LINE:
222 0 : layoutBarLine(item_cast<const BarLine*>(item), static_cast<BarLine::LayoutData*>(ldata), ctx);
223 0 : break;
224 0 : case ElementType::BEAM: layoutBeam(item_cast<Beam*>(item), ctx);
225 0 : break;
226 4 : case ElementType::BEND:
227 4 : layoutBend(item_cast<const Bend*>(item), static_cast<Bend::LayoutData*>(ldata));
228 4 : break;
229 147 : case ElementType::HBOX:
230 147 : layoutHBox(item_cast<const HBox*>(item), static_cast<HBox::LayoutData*>(ldata), ctx);
231 147 : break;
232 0 : case ElementType::VBOX:
233 0 : layoutVBox(item_cast<const VBox*>(item), static_cast<VBox::LayoutData*>(ldata), ctx);
234 0 : break;
235 0 : case ElementType::FBOX:
236 0 : layoutFBox(item_cast<const FBox*>(item), static_cast<FBox::LayoutData*>(ldata), ctx);
237 0 : break;
238 0 : case ElementType::TBOX:
239 0 : layoutTBox(item_cast<const TBox*>(item), static_cast<TBox::LayoutData*>(ldata), ctx);
240 0 : break;
241 0 : case ElementType::BRACKET:
242 0 : layoutBracket(item_cast<const Bracket*>(item), static_cast<Bracket::LayoutData*>(ldata), ctx.conf());
243 0 : break;
244 168 : case ElementType::BREATH:
245 168 : layoutBreath(item_cast<const Breath*>(item), static_cast<Breath::LayoutData*>(ldata), ctx.conf());
246 168 : break;
247 49663 : case ElementType::CHORD: layoutChord(item_cast<Chord*>(item), ctx);
248 49663 : break;
249 168 : case ElementType::CHORDLINE:
250 168 : layoutChordLine(item_cast<const ChordLine*>(item), static_cast<ChordLine::LayoutData*>(ldata), ctx.conf());
251 168 : break;
252 0 : case ElementType::CLEF:
253 0 : layoutClef(item_cast<const Clef*>(item), static_cast<Clef::LayoutData*>(ldata), ctx.conf());
254 0 : break;
255 44 : case ElementType::CAPO:
256 44 : layoutCapo(item_cast<const Capo*>(item), static_cast<Capo::LayoutData*>(ldata), ctx);
257 44 : break;
258 0 : case ElementType::DEAD_SLAPPED:
259 0 : layoutDeadSlapped(item_cast<const DeadSlapped*>(item), static_cast<DeadSlapped::LayoutData*>(ldata));
260 0 : break;
261 1145 : case ElementType::DYNAMIC:
262 1145 : layoutDynamic(item_cast<Dynamic*>(item), static_cast<Dynamic::LayoutData*>(ldata), ctx.conf());
263 1145 : break;
264 34 : case ElementType::EXPRESSION:
265 34 : layoutExpression(item_cast<const Expression*>(item), static_cast<Expression::LayoutData*>(ldata));
266 34 : break;
267 566 : case ElementType::FERMATA:
268 566 : layoutFermata(item_cast<const Fermata*>(item), static_cast<Fermata::LayoutData*>(ldata));
269 566 : break;
270 156 : case ElementType::FIGURED_BASS:
271 156 : layoutFiguredBass(item_cast<const FiguredBass*>(item), static_cast<FiguredBass::LayoutData*>(ldata), ctx);
272 156 : break;
273 0 : case ElementType::FINGERING:
274 0 : layoutFingering(item_cast<const Fingering*>(item), static_cast<Fingering::LayoutData*>(ldata));
275 0 : break;
276 412 : case ElementType::FRET_DIAGRAM:
277 412 : layoutFretDiagram(item_cast<const FretDiagram*>(item), static_cast<FretDiagram::LayoutData*>(ldata), ctx);
278 412 : break;
279 234 : case ElementType::GLISSANDO: layoutGlissando(item_cast<Glissando*>(item), ctx);
280 234 : break;
281 910 : case ElementType::GLISSANDO_SEGMENT: layoutGlissandoSegment(item_cast<GlissandoSegment*>(item), ctx);
282 910 : break;
283 0 : case ElementType::GRADUAL_TEMPO_CHANGE: layoutGradualTempoChange(item_cast<GradualTempoChange*>(item), ctx);
284 0 : break;
285 22 : case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT: layoutGradualTempoChangeSegment(item_cast<GradualTempoChangeSegment*>(item), ctx);
286 22 : break;
287 409 : case ElementType::GUITAR_BEND: layoutGuitarBend(item_cast<GuitarBend*>(item), ctx);
288 409 : break;
289 1250 : case ElementType::GUITAR_BEND_SEGMENT: layoutGuitarBendSegment(item_cast<GuitarBendSegment*>(item), ctx);
290 1250 : break;
291 6 : case ElementType::GUITAR_BEND_HOLD_SEGMENT: GuitarBendLayout::layoutHoldLine(item_cast<GuitarBendHoldSegment*>(item));
292 6 : break;
293 0 : case ElementType::HAIRPIN: layoutHairpin(item_cast<Hairpin*>(item), ctx);
294 0 : break;
295 472 : case ElementType::HAIRPIN_SEGMENT: layoutHairpinSegment(item_cast<HairpinSegment*>(item), ctx);
296 472 : break;
297 0 : case ElementType::HAMMER_ON_PULL_OFF: layoutHammerOnPullOff(item_cast<HammerOnPullOff*>(item), ctx);
298 0 : break;
299 0 : case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: layoutHammerOnPullOffSegment(item_cast<HammerOnPullOffSegment*>(item), ctx);
300 0 : break;
301 75 : case ElementType::HAMMER_ON_PULL_OFF_TEXT: layoutHammerOnPullOffText(item_cast<HammerOnPullOffText*>(item), ctx);
302 75 : break;
303 32 : case ElementType::HARP_DIAGRAM:
304 32 : layoutHarpPedalDiagram(item_cast<const HarpPedalDiagram*>(item), static_cast<HarpPedalDiagram::LayoutData*>(ldata));
305 32 : break;
306 2276 : case ElementType::HARMONY:
307 2276 : layoutHarmony(item_cast<Harmony*>(item), static_cast<Harmony::LayoutData*>(ldata), ctx);
308 2276 : break;
309 29 : case ElementType::HARMONIC_MARK_SEGMENT: layoutHarmonicMarkSegment(item_cast<HarmonicMarkSegment*>(item), ctx);
310 29 : break;
311 0 : case ElementType::HOOK:
312 0 : layoutHook(item_cast<const Hook*>(item), static_cast<Hook::LayoutData*>(ldata));
313 0 : break;
314 60 : case ElementType::IMAGE:
315 60 : layoutImage(item_cast<const Image*>(item), static_cast<Image::LayoutData*>(ldata));
316 60 : break;
317 91 : case ElementType::INSTRUMENT_CHANGE:
318 91 : layoutInstrumentChange(item_cast<const InstrumentChange*>(item), static_cast<InstrumentChange::LayoutData*>(ldata));
319 91 : break;
320 0 : case ElementType::INSTRUMENT_NAME:
321 0 : layoutInstrumentName(item_cast<const InstrumentName*>(item), static_cast<InstrumentName::LayoutData*>(ldata));
322 0 : break;
323 212 : case ElementType::JUMP:
324 212 : layoutJump(item_cast<const Jump*>(item), static_cast<Jump::LayoutData*>(ldata));
325 212 : break;
326 0 : case ElementType::KEYSIG:
327 0 : layoutKeySig(item_cast<const KeySig*>(item), static_cast<KeySig::LayoutData*>(ldata), ctx.conf());
328 0 : break;
329 0 : case ElementType::LAISSEZ_VIB:
330 0 : layoutLaissezVib(item_cast<LaissezVib*>(item));
331 0 : break;
332 2168 : case ElementType::LAYOUT_BREAK:
333 2168 : layoutLayoutBreak(item_cast<const LayoutBreak*>(item), static_cast<LayoutBreak::LayoutData*>(ldata));
334 2168 : break;
335 : // TODO: case ElementType::STAFF_VISIBILTY_INDICATOR:
336 0 : case ElementType::SYSTEM_LOCK_INDICATOR:
337 0 : layoutIndicatorIcon(item_cast<const IndicatorIcon*>(item), static_cast<IndicatorIcon::LayoutData*>(ldata));
338 0 : break;
339 0 : case ElementType::LET_RING: layoutLetRing(item_cast<LetRing*>(item), ctx);
340 0 : break;
341 31 : case ElementType::LET_RING_SEGMENT: layoutLetRingSegment(item_cast<LetRingSegment*>(item), ctx);
342 31 : break;
343 0 : case ElementType::LEDGER_LINE: layoutLedgerLine(item_cast<LedgerLine*>(item), ctx);
344 0 : break;
345 0 : case ElementType::LYRICS: layoutLyrics(item_cast<Lyrics*>(item), ctx);
346 0 : break;
347 0 : case ElementType::LYRICSLINE_SEGMENT: layoutLyricsLineSegment(item_cast<LyricsLineSegment*>(item), ctx);
348 0 : break;
349 6 : case ElementType::PARTIAL_LYRICSLINE_SEGMENT: layoutLyricsLineSegment(item_cast<LyricsLineSegment*>(item), ctx);
350 6 : break;
351 309 : case ElementType::MARKER:
352 309 : layoutMarker(item_cast<Marker*>(item), static_cast<Marker::LayoutData*>(ldata), ctx);
353 309 : break;
354 0 : case ElementType::MEASURE_NUMBER:
355 0 : layoutMeasureNumber(item_cast<MeasureNumber*>(item), static_cast<MeasureNumber::LayoutData*>(ldata), ctx);
356 0 : break;
357 365 : case ElementType::MEASURE_REPEAT:
358 365 : layoutMeasureRepeat(item_cast<const MeasureRepeat*>(item), static_cast<MeasureRepeat::LayoutData*>(ldata), ctx);
359 365 : break;
360 175 : case ElementType::MMREST:
361 175 : layoutMMRest(item_cast<const MMRest*>(item), static_cast<MMRest::LayoutData*>(ldata), ctx);
362 175 : break;
363 0 : case ElementType::MMREST_RANGE:
364 0 : layoutMMRestRange(item_cast<MMRestRange*>(item), static_cast<MMRestRange::LayoutData*>(ldata), ctx);
365 0 : break;
366 0 : case ElementType::NOTE:
367 0 : layoutNote(item_cast<const Note*>(item), static_cast<Note::LayoutData*>(ldata));
368 0 : break;
369 0 : case ElementType::NOTEDOT:
370 0 : layoutNoteDot(item_cast<const NoteDot*>(item), static_cast<NoteDot::LayoutData*>(ldata));
371 0 : break;
372 0 : case ElementType::NOTEHEAD:
373 0 : layoutSymbol(item_cast<const NoteHead*>(item), static_cast<NoteHead::LayoutData*>(ldata), ctx);
374 0 : break;
375 3 : case ElementType::NOTELINE:
376 3 : layoutNoteLine(item_cast<NoteLine*>(item), ctx);
377 3 : break;
378 12 : case ElementType::NOTELINE_SEGMENT:
379 12 : layoutNoteLineSegment(item_cast<NoteLineSegment*>(item), ctx);
380 12 : break;
381 636 : case ElementType::ORNAMENT:
382 636 : layoutOrnament(item_cast<const Ornament*>(item), static_cast<Ornament::LayoutData*>(ldata), ctx.conf());
383 636 : break;
384 0 : case ElementType::OTTAVA: layoutOttava(item_cast<Ottava*>(item), ctx);
385 0 : break;
386 161 : case ElementType::OTTAVA_SEGMENT: layoutOttavaSegment(item_cast<OttavaSegment*>(item), ctx);
387 161 : break;
388 0 : case ElementType::PALM_MUTE: layoutPalmMute(item_cast<PalmMute*>(item), ctx);
389 0 : break;
390 14 : case ElementType::PALM_MUTE_SEGMENT: layoutPalmMuteSegment(item_cast<PalmMuteSegment*>(item), ctx);
391 14 : break;
392 0 : case ElementType::PAGE_LOCK_INDICATOR:
393 0 : layoutPageLockIndicator(item_cast<const PageLockIndicator*>(item), static_cast<PageLockIndicator::LayoutData*>(ldata));
394 0 : break;
395 0 : case ElementType::PARENTHESIS: layoutParenthesis(item_cast<Parenthesis*>(item), static_cast<Parenthesis::LayoutData*>(ldata), ctx);
396 0 : break;
397 0 : case ElementType::PEDAL: layoutPedal(item_cast<Pedal*>(item), ctx);
398 0 : break;
399 119 : case ElementType::PEDAL_SEGMENT: layoutPedalSegment(item_cast<PedalSegment*>(item), ctx);
400 119 : break;
401 2 : case ElementType::PICK_SCRAPE_SEGMENT: layoutPickScrapeSegment(item_cast<PickScrapeSegment*>(item), ctx);
402 2 : break;
403 0 : case ElementType::PLAY_COUNT_TEXT:
404 0 : layoutPlayCountText(item_cast<PlayCountText*>(item), static_cast<PlayCountText::LayoutData*>(ldata));
405 0 : break;
406 87 : case ElementType::PLAYTECH_ANNOTATION:
407 87 : layoutPlayTechAnnotation(item_cast<const PlayTechAnnotation*>(item), static_cast<PlayTechAnnotation::LayoutData*>(ldata));
408 87 : break;
409 4 : case ElementType::RASGUEADO_SEGMENT: layoutRasgueadoSegment(item_cast<RasgueadoSegment*>(item), ctx);
410 4 : break;
411 127 : case ElementType::REHEARSAL_MARK:
412 127 : layoutRehearsalMark(item_cast<const RehearsalMark*>(item), static_cast<RehearsalMark::LayoutData*>(ldata));
413 127 : break;
414 66760 : case ElementType::REST:
415 66760 : layoutRest(item_cast<const Rest*>(item), static_cast<Rest::LayoutData*>(ldata), ctx);
416 66760 : break;
417 0 : case ElementType::SHADOW_NOTE: layoutShadowNote(item_cast<ShadowNote*>(item), ctx);
418 0 : break;
419 0 : case ElementType::SLUR: layoutSlur(item_cast<Slur*>(item), ctx);
420 0 : break;
421 0 : case ElementType::SPACER: layoutSpacer(item_cast<Spacer*>(item), ctx);
422 0 : break;
423 0 : case ElementType::STAFF_STATE:
424 0 : layoutStaffState(item_cast<const StaffState*>(item), static_cast<StaffState::LayoutData*>(ldata));
425 0 : break;
426 1133 : case ElementType::STAFF_TEXT:
427 1133 : layoutStaffText(item_cast<const StaffText*>(item), static_cast<StaffText::LayoutData*>(ldata));
428 1133 : break;
429 6 : case ElementType::STAVE_SHARING_LABEL:
430 6 : layoutStaveSharingLabel(item_cast<const StaveSharingLabel*>(item), static_cast<StaffText::LayoutData*>(ldata));
431 6 : break;
432 19 : case ElementType::STAFFTYPE_CHANGE:
433 19 : layoutStaffTypeChange(item_cast<const StaffTypeChange*>(item), static_cast<StaffTypeChange::LayoutData*>(ldata), ctx.conf());
434 19 : break;
435 0 : case ElementType::SOUND_FLAG:
436 0 : layoutSoundFlag(item_cast<const SoundFlag*>(item), static_cast<SoundFlag::LayoutData*>(ldata));
437 0 : break;
438 0 : case ElementType::STEM:
439 0 : layoutStem(item_cast<const Stem*>(item), static_cast<Stem::LayoutData*>(ldata), ctx.conf());
440 0 : break;
441 0 : case ElementType::STEM_SLASH:
442 0 : layoutStemSlash(item_cast<const StemSlash*>(item), static_cast<StemSlash::LayoutData*>(ldata), ctx.conf());
443 0 : break;
444 131 : case ElementType::STICKING:
445 131 : layoutSticking(item_cast<const Sticking*>(item), static_cast<Sticking::LayoutData*>(ldata));
446 131 : break;
447 19 : case ElementType::STRING_TUNINGS:
448 19 : layoutStringTunings(item_cast<StringTunings*>(item), ctx);
449 19 : break;
450 50 : case ElementType::SYMBOL:
451 50 : layoutSymbol(item_cast<const Symbol*>(item), static_cast<Symbol::LayoutData*>(ldata), ctx);
452 50 : break;
453 0 : case ElementType::FSYMBOL:
454 0 : layoutFSymbol(item_cast<const FSymbol*>(item), static_cast<FSymbol::LayoutData*>(ldata));
455 0 : break;
456 0 : case ElementType::SYSTEM_DIVIDER:
457 0 : layoutSystemDivider(item_cast<const SystemDivider*>(item), static_cast<SystemDivider::LayoutData*>(ldata), ctx);
458 0 : break;
459 0 : case ElementType::SYSTEM_TEXT:
460 0 : layoutSystemText(item_cast<const SystemText*>(item), static_cast<SystemText::LayoutData*>(ldata));
461 0 : break;
462 134 : case ElementType::TAPPING:
463 134 : layoutTapping(toTapping(item), static_cast<Tapping::LayoutData*>(ldata), ctx);
464 134 : break;
465 0 : case ElementType::TAPPING_HALF_SLUR:
466 0 : layoutTappingHalfSlur(toTappingHalfSlur(item));
467 0 : break;
468 1136 : case ElementType::TEMPO_TEXT:
469 1136 : layoutTempoText(item_cast<const TempoText*>(item), static_cast<TempoText::LayoutData*>(ldata));
470 1136 : break;
471 10780 : case ElementType::TEXT:
472 10780 : layoutText(item_cast<const Text*>(item), static_cast<Text::LayoutData*>(ldata));
473 10780 : break;
474 0 : case ElementType::TEXTLINE: layoutTextLine(item_cast<TextLine*>(item), ctx);
475 0 : break;
476 55 : case ElementType::TEXTLINE_SEGMENT: layoutTextLineSegment(item_cast<TextLineSegment*>(item), ctx);
477 55 : break;
478 0 : case ElementType::TIE: layoutTie(item_cast<Tie*>(item), ctx);
479 0 : break;
480 0 : case ElementType::TIMESIG:
481 0 : layoutTimeSig(item_cast<const TimeSig*>(item), static_cast<TimeSig::LayoutData*>(ldata), ctx);
482 0 : break;
483 498 : case ElementType::TIME_TICK_ANCHOR: layoutTimeTickAnchor(static_cast<TimeTickAnchor*>(item), ctx);
484 498 : break;
485 0 : case ElementType::TREMOLO_SINGLECHORD: layoutTremoloSingle(item_cast<TremoloSingleChord*>(item), ctx);
486 0 : break;
487 0 : case ElementType::TREMOLO_TWOCHORD: layoutTremoloTwo(item_cast<TremoloTwoChord*>(item), ctx);
488 0 : break;
489 38 : case ElementType::TREMOLOBAR:
490 38 : layoutTremoloBar(item_cast<const TremoloBar*>(item), static_cast<TremoloBar::LayoutData*>(ldata));
491 38 : break;
492 0 : case ElementType::TRILL: layoutTrill(item_cast<Trill*>(item), ctx);
493 0 : break;
494 116 : case ElementType::TRILL_SEGMENT: layoutTrillSegment(item_cast<TrillSegment*>(item), ctx);
495 116 : break;
496 0 : case ElementType::TRIPLET_FEEL:
497 0 : layoutTripletFeel(item_cast<const TripletFeel*>(item), static_cast<TripletFeel::LayoutData*>(ldata));
498 0 : break;
499 0 : case ElementType::TUPLET: layoutTuplet(item_cast<Tuplet*>(item), ctx);
500 0 : break;
501 0 : case ElementType::VIBRATO: layoutVibrato(item_cast<Vibrato*>(item), ctx);
502 0 : break;
503 28 : case ElementType::VIBRATO_SEGMENT: layoutVibratoSegment(item_cast<VibratoSegment*>(item), ctx);
504 28 : break;
505 0 : case ElementType::VOLTA: layoutVolta(item_cast<Volta*>(item), ctx);
506 0 : break;
507 279 : case ElementType::VOLTA_SEGMENT: layoutVoltaSegment(item_cast<VoltaSegment*>(item), ctx);
508 279 : break;
509 0 : case ElementType::WHAMMY_BAR: layoutLine(item_cast<WhammyBar*>(item), ctx);
510 0 : break;
511 0 : case ElementType::WHAMMY_BAR_SEGMENT: layoutWhammyBarSegment(item_cast<WhammyBarSegment*>(item), ctx);
512 0 : break;
513 0 : default:
514 0 : LOGE() << "not found in layout types item: " << item->typeName();
515 0 : DO_ASSERT(false);
516 : }
517 147115 : }
518 :
519 4086 : void TLayout::layoutAccidental(const Accidental* item, Accidental::LayoutData* ldata, const LayoutConfiguration& conf)
520 : {
521 : LAYOUT_CALL_ITEM(item);
522 : LD_INDEPENDENT;
523 :
524 4086 : if (ldata->isValid()) {
525 95 : return;
526 : }
527 :
528 4001 : ldata->syms.clear();
529 :
530 : // TODO: remove Accidental in layout
531 : // don't show accidentals for tab or slash notation
532 4001 : if (item->onTabStaff() || (item->note() && item->note()->fixed())) {
533 5 : ldata->setIsSkipDraw(true);
534 5 : return;
535 : }
536 3996 : ldata->setIsSkipDraw(false);
537 3996 : ldata->setPos(PointF());
538 :
539 3996 : if (item->accidentalType() == AccidentalType::NONE) {
540 5 : return;
541 : }
542 :
543 3991 : ldata->column = 0;
544 :
545 3991 : auto accidentalSingleSym = [](const Accidental* item) -> SymId
546 : {
547 : // if the accidental is standard (doubleflat, flat, natural, sharp or double sharp)
548 : // and it has either no bracket or parentheses, then we have glyphs straight from smufl.
549 :
550 3991 : if (item->bracket() == AccidentalBracket::PARENTHESIS && !item->parentNoteHasParentheses()) {
551 57 : switch (item->accidentalType()) {
552 4 : case AccidentalType::FLAT: return SymId::accidentalFlatParens;
553 4 : case AccidentalType::FLAT2: return SymId::accidentalDoubleFlatParens;
554 9 : case AccidentalType::NATURAL: return SymId::accidentalNaturalParens;
555 4 : case AccidentalType::SHARP: return SymId::accidentalSharpParens;
556 4 : case AccidentalType::SHARP2: return SymId::accidentalDoubleSharpParens;
557 32 : default:
558 32 : break;
559 : }
560 : }
561 3966 : return SymId::noSym;
562 : };
563 :
564 57 : auto accidentalBracketSyms = [](AccidentalBracket type) -> std::pair<SymId, SymId>
565 : {
566 57 : switch (type) {
567 57 : case AccidentalBracket::PARENTHESIS: return { SymId::accidentalParensLeft, SymId::accidentalParensRight };
568 0 : case AccidentalBracket::BRACKET: return { SymId::accidentalBracketLeft, SymId::accidentalBracketRight };
569 0 : case AccidentalBracket::BRACE: return { SymId::accidentalCombiningOpenCurlyBrace, SymId::accidentalCombiningCloseCurlyBrace };
570 0 : case AccidentalBracket::NONE: return { SymId::noSym, SymId::noSym };
571 : }
572 0 : return { SymId::noSym, SymId::noSym };
573 : };
574 :
575 3991 : Shape shape;
576 :
577 : // Single?
578 3991 : SymId singleSym = accidentalSingleSym(item);
579 3991 : if (singleSym != SymId::noSym && conf.engravingFont()->isValid(singleSym)) {
580 0 : Accidental::LayoutData::Sym s(singleSym, 0.0, 0.0);
581 0 : ldata->syms.push_back(s);
582 :
583 0 : shape.add(item->symShapeWithCutouts(singleSym));
584 : }
585 : // Multi
586 : else {
587 3991 : double margin = conf.styleAbsolute(Sid::bracketedAccidentalPadding);
588 3991 : double x = 0.0;
589 :
590 3991 : std::pair<SymId, SymId> bracketSyms;
591 3991 : bool isNeedBracket = item->bracket() != AccidentalBracket::NONE && !item->parentNoteHasParentheses();
592 3991 : if (isNeedBracket) {
593 57 : bracketSyms = accidentalBracketSyms(item->bracket());
594 : }
595 :
596 : // Left
597 3991 : if (bracketSyms.first != SymId::noSym) {
598 : Accidental::LayoutData::Sym ls(bracketSyms.first, 0.0,
599 57 : item->bracket() == AccidentalBracket::BRACE ? item->spatium() * 0.4 : 0.0);
600 57 : ldata->syms.push_back(ls);
601 57 : shape.add(item->symBbox(bracketSyms.first), item);
602 :
603 57 : x += item->symAdvance(bracketSyms.first) + margin;
604 : }
605 :
606 : // Main
607 3991 : SymId mainSym = item->symId();
608 3991 : Accidental::LayoutData::Sym ms(mainSym, x, 0.0);
609 3991 : ldata->syms.push_back(ms);
610 3991 : shape.add(item->symShapeWithCutouts(mainSym).translated(PointF(x, 0.0)));
611 :
612 : // Right
613 3991 : if (bracketSyms.second != SymId::noSym) {
614 57 : x += item->symAdvance(mainSym) + margin;
615 :
616 : Accidental::LayoutData::Sym rs(bracketSyms.second, x,
617 57 : item->bracket() == AccidentalBracket::BRACE ? item->spatium() * 0.4 : 0.0);
618 57 : ldata->syms.push_back(rs);
619 57 : shape.add(item->symBbox(bracketSyms.second).translated(x, 0.0), item);
620 : }
621 : }
622 :
623 3991 : ldata->setShape(shape);
624 3991 : }
625 :
626 0 : void TLayout::layoutActionIcon(const ActionIcon* item, ActionIcon::LayoutData* ldata)
627 : {
628 : LAYOUT_CALL_ITEM(item);
629 : LD_INDEPENDENT;
630 :
631 0 : if (ldata->isValid()) {
632 0 : return;
633 : }
634 :
635 0 : FontMetrics fontMetrics(item->iconFont());
636 0 : ldata->setBbox(fontMetrics.boundingRect(Char(item->icon())));
637 0 : ldata->setPos(PointF());
638 0 : }
639 :
640 2 : void TLayout::layoutAmbitus(const Ambitus* item, Ambitus::LayoutData* ldata, const LayoutContext& ctx)
641 : {
642 : LAYOUT_CALL_ITEM(item);
643 : LD_INDEPENDENT;
644 :
645 2 : if (ldata->isValid()) {
646 0 : return;
647 : }
648 :
649 2 : const double spatium = item->spatium();
650 2 : const double headWdt = item->headWidth();
651 :
652 2 : Accidental::LayoutData* topAccData = item->topAccidental()->mutldata();
653 2 : Accidental::LayoutData* bottomAccData = item->bottomAccidental()->mutldata();
654 :
655 2 : ldata->setPos(PointF());
656 :
657 : //
658 : // NOTEHEADS Y POS
659 : //
660 : {
661 2 : const Fraction tick = item->segment()->tick();
662 2 : const Staff* stf = ctx.dom().staff(item->staffIdx());
663 2 : const ClefType clf = stf->clef(tick);
664 2 : const Key key = stf->key(tick);
665 2 : const double lineDist = stf->lineDistance(tick) * spatium;
666 :
667 : // top notehead
668 2 : if (!pitchIsValid(item->topPitch()) || !tpcIsValid(item->topTpc())) {
669 0 : ldata->topPos.setY(0.0); // if uninitialized, set to top staff line
670 : } else {
671 2 : int topLine = Ambitus::staffLine(item->topTpc(), item->topPitch(), clf);
672 2 : ldata->topPos.setY(topLine * lineDist * 0.5);
673 : // compute accidental
674 2 : AccidentalType accidType = Ambitus::accidentalType(item->topTpc(), key);
675 2 : item->topAccidental()->setAccidentalType(accidType);
676 2 : TLayout::layoutAccidental(item->topAccidental(), topAccData, ctx.conf());
677 2 : topAccData->setPosY(ldata->topPos.y());
678 : }
679 :
680 : // bottom notehead
681 2 : if (!pitchIsValid(item->bottomPitch()) || !tpcIsValid(item->bottomTpc())) {
682 0 : const int numOfLines = stf->lines(tick);
683 0 : ldata->bottomPos.setY((numOfLines - 1) * lineDist); // if uninitialized, set to last staff line
684 : } else {
685 2 : int bottomLine = Ambitus::staffLine(item->bottomTpc(), item->bottomPitch(), clf);
686 2 : ldata->bottomPos.setY(bottomLine * lineDist * 0.5);
687 : // compute accidental
688 2 : AccidentalType accidType = Ambitus::accidentalType(item->bottomTpc(), key);
689 2 : item->bottomAccidental()->setAccidentalType(accidType);
690 2 : TLayout::layoutAccidental(item->bottomAccidental(), bottomAccData, ctx.conf());
691 2 : bottomAccData->setPosY(ldata->bottomPos.y());
692 : }
693 : }
694 :
695 : //
696 : // NOTEHEAD X POS
697 : //
698 : // Note: manages colliding accidentals
699 : //
700 : {
701 2 : double accNoteDist = item->absoluteFromSpatium(ctx.conf().styleS(Sid::accidentalNoteDistance));
702 2 : double xAccidOffTop = topAccData->bbox().width() + accNoteDist;
703 2 : double xAccidOffBottom = bottomAccData->bbox().width() + accNoteDist;
704 :
705 : // if top accidental extends down more than bottom accidental extends up,
706 : // AND ambitus is not leaning right, bottom accidental needs to be displaced
707 2 : bool collision = (item->direction() != DirectionH::RIGHT)
708 3 : && (topAccData->pos().y() + topAccData->bbox().y() + topAccData->bbox().height()
709 1 : > bottomAccData->pos().y() + bottomAccData->bbox().y());
710 2 : if (collision) {
711 : // displace bottom accidental (also attempting to 'undercut' flats)
712 0 : AccidentalType bottomAccType = item->bottomAccidental()->accidentalType();
713 0 : xAccidOffBottom = xAccidOffTop + ((bottomAccType == AccidentalType::FLAT
714 0 : || bottomAccType == AccidentalType::FLAT2
715 0 : || bottomAccType == AccidentalType::NATURAL)
716 0 : ? bottomAccData->bbox().width() * 0.5 : bottomAccData->bbox().width());
717 : }
718 :
719 2 : switch (item->direction()) {
720 1 : case DirectionH::AUTO: // noteheads one above the other
721 : // left align noteheads and right align accidentals 'hanging' on the left
722 1 : ldata->topPos.setX(0.0);
723 1 : ldata->bottomPos.setX(0.0);
724 1 : topAccData->setPosX(-xAccidOffTop);
725 1 : bottomAccData->setPosX(-xAccidOffBottom);
726 1 : break;
727 0 : case DirectionH::LEFT: // top notehead at the left of bottom notehead
728 : // place top notehead at left margin; bottom notehead at right of top head;
729 : // top accid. 'hanging' on left of top head and bottom accid. 'hanging' at left of bottom head
730 0 : ldata->topPos.setX(0.0);
731 0 : ldata->bottomPos.setX(headWdt);
732 0 : topAccData->setPosX(-xAccidOffTop);
733 0 : bottomAccData->setPosX(collision ? -xAccidOffBottom : headWdt - xAccidOffBottom);
734 0 : break;
735 1 : case DirectionH::RIGHT: // top notehead at the right of bottom notehead
736 : // bottom notehead at left margin; top notehead at right of bottomnotehead
737 : // top accid. 'hanging' on left of top head and bottom accid. 'hanging' at left of bottom head
738 1 : ldata->topPos.setX(headWdt);
739 1 : ldata->bottomPos.setX(0.0);
740 1 : topAccData->setPosX(headWdt - xAccidOffTop);
741 1 : bottomAccData->setPosX(-xAccidOffBottom);
742 1 : break;
743 : }
744 : }
745 :
746 : // LINE
747 : // compute line from top note centre to bottom note centre
748 : {
749 2 : LineF fullLine(ldata->topPos.x() + headWdt * 0.5,
750 : ldata->topPos.y(),
751 2 : ldata->bottomPos.x() + headWdt * 0.5,
752 6 : ldata->bottomPos.y());
753 : // shorten line on each side by offsets
754 2 : double yDelta = ldata->bottomPos.y() - ldata->topPos.y();
755 2 : if (!muse::RealIsNull(yDelta)) {
756 2 : double off = Ambitus::LINEOFFSET_DEFAULT.toAbsolute(spatium);
757 2 : PointF p1 = fullLine.pointAt(off / yDelta);
758 2 : PointF p2 = fullLine.pointAt(1 - (off / yDelta));
759 2 : ldata->line = LineF(p1, p2);
760 : } else {
761 0 : ldata->line = fullLine;
762 : }
763 : }
764 :
765 : // BBOX
766 : {
767 2 : RectF headRect(0, -0.5 * spatium, headWdt, 1 * spatium);
768 4 : ldata->setBbox(headRect.translated(ldata->topPos).united(headRect.translated(ldata->bottomPos))
769 2 : .united(topAccData->bbox().translated(topAccData->pos()))
770 4 : .united(bottomAccData->bbox().translated(bottomAccData->pos())));
771 : }
772 : }
773 :
774 466 : void TLayout::layoutArpeggio(const Arpeggio* item, Arpeggio::LayoutData* ldata, const LayoutConfiguration& conf)
775 : {
776 : LAYOUT_CALL_ITEM(item);
777 :
778 : //! NOTE Can be edited and relayout,
779 : //! in this case the reset layout data has not yet been done
780 : // if (ldata->isValid()) {
781 : // return;
782 : // }
783 :
784 466 : if (conf.styleB(Sid::arpeggioHiddenInStdIfTab)) {
785 0 : if (item->staff() && item->staff()->isPitchedStaff(item->tick())) {
786 0 : for (Staff* s : item->staff()->staffList()) {
787 0 : if (s->onSameScore(item) && s->isTabStaff(item->tick()) && s->visible()) {
788 0 : ldata->setIsSkipDraw(true);
789 : }
790 0 : }
791 : }
792 : }
793 466 : ldata->setIsSkipDraw(false);
794 466 : if (!ldata->isSetPos()) {
795 165 : ldata->setPos(PointF());
796 : }
797 :
798 466 : IF_ASSERT_FAILED(item->ownershipParent()) {
799 0 : return;
800 : }
801 :
802 : //! NOTE Must already be set previously
803 466 : Chord* parentChord = item->chord();
804 466 : LD_CONDITION(parentChord->upNote()->ldata()->isSetPos());
805 466 : LD_CONDITION(parentChord->downNote()->ldata()->isSetPos());
806 :
807 394 : auto symbolLine = [](const std::shared_ptr<const IEngravingFont>& f, Arpeggio::LayoutData* data, SymId end, SymId fill)
808 : {
809 394 : data->symbols.clear();
810 :
811 394 : double w = data->bottom - data->top;
812 394 : double w1 = f->advance(end, data->magS);
813 394 : double w2 = f->advance(fill, data->magS);
814 394 : int n = lrint((w - w1) / w2);
815 1803 : for (int i = 0; i < n; ++i) {
816 1409 : data->symbols.push_back(fill);
817 : }
818 394 : data->symbols.push_back(end);
819 394 : };
820 :
821 466 : ldata->arpeggioHeight = ArpeggioLayout::computeHeight(item);
822 466 : ldata->top = ArpeggioLayout::calcTop(item, conf);
823 466 : ldata->bottom = ArpeggioLayout::calcBottom(item, ldata->arpeggioHeight, conf);
824 :
825 466 : ldata->setMag(item->staff() ? item->staff()->staffMag(item->tick()) : item->mag());
826 466 : ldata->magS = conf.magS(ldata->mag());
827 :
828 466 : std::shared_ptr<const IEngravingFont> font = conf.engravingFont();
829 466 : switch (item->arpeggioType()) {
830 294 : case ArpeggioType::NORMAL: {
831 294 : symbolLine(font, ldata, SymId::wiggleArpeggiatoUp, SymId::wiggleArpeggiatoUp);
832 : // string is rotated -90 degrees
833 294 : ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
834 294 : ldata->setBbox(RectF(0.0, -ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
835 294 : } break;
836 :
837 46 : case ArpeggioType::UP: {
838 46 : symbolLine(font, ldata, SymId::wiggleArpeggiatoUpArrow, SymId::wiggleArpeggiatoUp);
839 : // string is rotated -90 degrees
840 46 : ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
841 46 : ldata->setBbox(RectF(0.0, -ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
842 46 : } break;
843 :
844 54 : case ArpeggioType::DOWN: {
845 54 : symbolLine(font, ldata, SymId::wiggleArpeggiatoUpArrow, SymId::wiggleArpeggiatoUp);
846 : // string is rotated +90 degrees (so that UpArrow turns into a DownArrow)
847 54 : ldata->symsBBox = font->bbox(ldata->symbols, ldata->magS);
848 54 : ldata->setBbox(RectF(0.0, ldata->symsBBox.x() + ldata->top, ldata->symsBBox.height(), ldata->symsBBox.width()));
849 54 : } break;
850 :
851 10 : case ArpeggioType::UP_STRAIGHT: {
852 10 : double x1 = item->spatium() * 0.5;
853 10 : ldata->symsBBox = font->bbox(SymId::arrowheadBlackUp, ldata->magS);
854 10 : double w = ldata->symsBBox.width();
855 10 : ldata->setBbox(RectF(x1 - w * 0.5, ldata->top, w, ldata->bottom));
856 10 : } break;
857 :
858 10 : case ArpeggioType::DOWN_STRAIGHT: {
859 10 : double x1 = item->spatium() * 0.5;
860 10 : ldata->symsBBox = font->bbox(SymId::arrowheadBlackDown, ldata->magS);
861 10 : double w = ldata->symsBBox.width();
862 10 : ldata->setBbox(RectF(x1 - w * 0.5, ldata->top, w, ldata->bottom));
863 10 : } break;
864 :
865 52 : case ArpeggioType::BRACKET: {
866 52 : double w = conf.styleS(Sid::arpeggioHookLen).val() * item->spatium();
867 52 : ldata->setBbox(RectF(0.0, ldata->top, w, ldata->bottom));
868 52 : } break;
869 : }
870 :
871 : // Loop through staves spanned & regenerate chord shape
872 : // This makes sure the arpeggio's shape is added to the shape of each chord it spans
873 466 : Chord* chord = item->chord();
874 466 : Segment* seg = chord->segment();
875 466 : staff_idx_t staveSpan = (item->track() + item->span() - 1) / VOICES;
876 980 : for (staff_idx_t staffIdx = item->staffIdx(); staffIdx <= staveSpan; staffIdx++) {
877 514 : seg->createShape(staffIdx);
878 : }
879 466 : }
880 :
881 0 : static Shape getChordsShape(const ChordBracket* item)
882 : {
883 0 : Chord* owner = item->chord();
884 0 : Shape result = owner->shape();
885 0 : result.removeTypes({ ElementType::CHORD_BRACKET, ElementType::ARPEGGIO });
886 0 : if (!owner->segment() || !owner->staff()) {
887 0 : return result;
888 : }
889 :
890 0 : const track_idx_t startTrack = staff2track(owner->staffIdx());
891 0 : const track_idx_t endTrack = startTrack + VOICES;
892 :
893 0 : for (track_idx_t track = startTrack; track < endTrack; ++track) {
894 0 : EngravingItem* e = owner->segment()->element(track);
895 0 : if (track == owner->track()) {
896 0 : continue;
897 : }
898 :
899 0 : if (!e || !e->isChord()) {
900 0 : continue;
901 : }
902 :
903 0 : Chord* chord = toChord(e);
904 0 : if (chord->vStaffIdx() != owner->vStaffIdx()) {
905 0 : continue;
906 : }
907 :
908 0 : Shape chordShape = chord->shape();
909 0 : chordShape.removeTypes({ ElementType::CHORD_BRACKET, ElementType::ARPEGGIO });
910 0 : chordShape.translate(chord->pos() - owner->pos());
911 0 : result.add(chordShape);
912 0 : }
913 :
914 0 : return result;
915 0 : }
916 :
917 0 : void TLayout::layoutChordBracket(const ChordBracket* item, Arpeggio::LayoutData* ldata, const LayoutConfiguration& conf)
918 : {
919 0 : double spatium = item->spatium();
920 0 : ldata->arpeggioHeight = ArpeggioLayout::computeHeight(item);
921 0 : ldata->top = ArpeggioLayout::calcTop(item, conf) - 0.25 * spatium;
922 0 : ldata->bottom = ArpeggioLayout::calcBottom(item, ldata->arpeggioHeight, conf) + 0.5 * spatium;
923 :
924 0 : const double minLen = 2 * item->spatium();
925 0 : double diff = minLen - std::abs(ldata->bottom);
926 0 : if (diff > 0) {
927 0 : ldata->bottom += diff;
928 0 : if (item->hookPos() == DirectionV::DOWN) {
929 0 : ldata->top -= diff;
930 0 : } else if (item->hookPos() == DirectionV::AUTO) {
931 0 : ldata->top -= 0.5 * diff;
932 : }
933 : }
934 :
935 0 : ldata->setMag(item->staff() ? item->staff()->staffMag(item->tick()) : item->mag());
936 0 : ldata->magS = conf.magS(ldata->mag());
937 :
938 0 : ldata->setShape(Shape(RectF(0.0, ldata->top, item->absoluteFromSpatium(item->hookLength()), ldata->bottom).normalized(), item));
939 :
940 0 : const Note* upnote = item->chord()->upNote();
941 0 : ldata->setPosY(upnote->y() + upnote->ldata()->bbox().top());
942 :
943 0 : Shape chordShape = getChordsShape(item);
944 0 : Shape itemShape = item->shape().translated(PointF(0.0, item->ldata()->pos().y()));
945 0 : if (item->rightSide()) {
946 0 : double x = HorizontalSpacing::minHorizontalDistance(chordShape, itemShape, spatium);
947 0 : x = std::max(x, 0.0);
948 0 : ldata->setPosX(x);
949 : } else {
950 0 : double x = HorizontalSpacing::minHorizontalDistance(itemShape, chordShape, spatium);
951 0 : x = std::max(x, 0.0);
952 0 : ldata->setPosX(-x);
953 : }
954 :
955 : // Loop through staves spanned & regenerate chord shape
956 : // This makes sure the arpeggio's shape is added to the shape of each chord it spans
957 0 : Chord* chord = item->chord();
958 0 : Segment* seg = chord->segment();
959 0 : staff_idx_t staveSpan = (item->track() + item->span() - 1) / VOICES;
960 0 : for (staff_idx_t staffIdx = item->staffIdx(); staffIdx <= staveSpan; staffIdx++) {
961 0 : seg->createShape(staffIdx);
962 : }
963 0 : }
964 :
965 3435 : void TLayout::layoutArticulation(Articulation* item, Articulation::LayoutData* ldata)
966 : {
967 : LAYOUT_CALL_ITEM(item);
968 3435 : if (item->isHiddenOnTabStaff()) {
969 0 : ldata->setIsSkipDraw(true);
970 0 : return;
971 : }
972 3435 : ldata->setIsSkipDraw(false);
973 3435 : ldata->setPos(PointF());
974 :
975 : //! NOTE Must already be set previously
976 3435 : LD_CONDITION(ldata->symId.has_value());
977 :
978 3435 : if (item->textType() != ArticulationTextType::NO_TEXT) {
979 68 : if (!item->text()) {
980 28 : Text* text = new Text(item, TextStyleType::ARTICULATION);
981 28 : static const ElementStyle elementStyle;
982 28 : text->initElementStyle(&elementStyle);
983 28 : item->setText(text);
984 : }
985 :
986 68 : Text* text = item->text();
987 68 : text->setXmlText(TConv::text(item->textType()));
988 68 : text->setTrack(item->track());
989 68 : text->setOwnershipParent(item);
990 68 : text->setSelected(item->selected());
991 :
992 68 : TextLayout::layoutBaseTextBase(item->text(), item->text()->mutldata());
993 : }
994 :
995 3435 : fillArticulationShape(item, ldata);
996 : }
997 :
998 3435 : void TLayout::fillArticulationShape(const Articulation* item, Articulation::LayoutData* ldata)
999 : {
1000 : LAYOUT_CALL_ITEM(item);
1001 :
1002 3435 : SymId sym = item->symId();
1003 3435 : if (sym == SymId::articAccentAbove || sym == SymId::articAccentBelow) {
1004 424 : RectF symBbox = item->symBbox(sym);
1005 424 : double width = symBbox.width();
1006 424 : double height = symBbox.height();
1007 424 : double thirdWidth = width / 3;
1008 424 : double thirdHeight = height / 3;
1009 424 : RectF base = RectF(0.0, 0.0, thirdWidth, height);
1010 424 : RectF center = RectF(thirdWidth, thirdHeight, thirdWidth, thirdHeight);
1011 424 : RectF tip = RectF(2 * thirdWidth, 1.25 * thirdHeight, thirdWidth, 0.5 * thirdHeight);
1012 424 : Shape shape;
1013 424 : shape.add(base, item);
1014 424 : shape.add(center, item);
1015 424 : shape.add(tip, item);
1016 424 : PointF translate(0.0, sym == SymId::articAccentAbove ? -height : 0.0);
1017 424 : ldata->setShape(shape.translate(translate));
1018 3435 : } else if (item->textType() == ArticulationTextType::NO_TEXT) {
1019 2943 : ldata->setShape(Shape(item->symBbox(sym), item));
1020 : } else {
1021 68 : ldata->setShape(Shape(item->text()->ldata()->bbox(), item));
1022 : }
1023 3435 : }
1024 :
1025 112638 : static double barLineWidth(const BarLine* item, const MStyle& style, double dotWidth)
1026 : {
1027 112638 : double w = 0.0;
1028 112638 : switch (item->barLineType()) {
1029 963 : case BarLineType::DOUBLE:
1030 963 : w = style.styleAbsolute(Sid::doubleBarWidth) * 2.0
1031 963 : + style.styleAbsolute(Sid::doubleBarDistance);
1032 963 : break;
1033 32 : case BarLineType::DOUBLE_HEAVY:
1034 32 : w = style.styleAbsolute(Sid::endBarWidth) * 2.0
1035 32 : + style.styleAbsolute(Sid::endBarDistance);
1036 32 : break;
1037 0 : case BarLineType::END_START_REPEAT:
1038 0 : w = style.styleAbsolute(Sid::endBarWidth)
1039 0 : + style.styleAbsolute(Sid::barWidth) * 2.0
1040 0 : + style.styleAbsolute(Sid::endBarDistance) * 2.0
1041 0 : + style.styleAbsolute(Sid::repeatBarlineDotSeparation) * 2.0
1042 0 : + dotWidth * 2;
1043 0 : break;
1044 1385 : case BarLineType::START_REPEAT:
1045 : case BarLineType::END_REPEAT:
1046 1385 : w = style.styleAbsolute(Sid::endBarWidth)
1047 1385 : + style.styleAbsolute(Sid::barWidth)
1048 1385 : + style.styleAbsolute(Sid::endBarDistance)
1049 1385 : + style.styleAbsolute(Sid::repeatBarlineDotSeparation)
1050 : + dotWidth;
1051 1385 : break;
1052 11649 : case BarLineType::END:
1053 : case BarLineType::REVERSE_END:
1054 11649 : w = style.styleAbsolute(Sid::endBarWidth)
1055 11649 : + style.styleAbsolute(Sid::barWidth)
1056 11649 : + style.styleAbsolute(Sid::endBarDistance);
1057 11649 : break;
1058 82 : case BarLineType::BROKEN:
1059 82 : w = style.styleAbsolute(Sid::dashBarWidth);
1060 82 : break;
1061 98487 : case BarLineType::NORMAL:
1062 : case BarLineType::DOTTED:
1063 98487 : w = style.styleAbsolute(Sid::barWidth);
1064 98487 : break;
1065 40 : case BarLineType::HEAVY:
1066 40 : w = style.styleAbsolute(Sid::endBarWidth);
1067 40 : break;
1068 : }
1069 112638 : return w;
1070 : }
1071 :
1072 112638 : void TLayout::layoutBarLine(const BarLine* item, BarLine::LayoutData* ldata, const LayoutContext& ctx)
1073 : {
1074 : LAYOUT_CALL_ITEM(item);
1075 :
1076 : // barlines hidden on this staff
1077 112638 : if (item->staff() && item->segment()) {
1078 112638 : if ((!item->staff()->staffTypeForElement(item)->showBarlines() && item->segment()->segmentType() == SegmentType::EndBarLine)
1079 112638 : || (item->staff()->hideSystemBarLine() && item->segment()->segmentType() == SegmentType::BeginBarLine)) {
1080 0 : ldata->setPos(PointF());
1081 0 : ldata->setBbox(RectF());
1082 0 : ldata->setIsSkipDraw(true);
1083 0 : return;
1084 : }
1085 : }
1086 :
1087 112638 : ldata->setIsSkipDraw(false);
1088 112638 : ldata->setPos(PointF());
1089 :
1090 : // Check conditions
1091 :
1092 : //! NOTE Here the goal is not just to check whether the conditions are met,
1093 : //! but the goal is to clearly show these conditions to the developer.
1094 112638 : for (const EngravingItem* e : *item->el()) {
1095 0 : switch (e->type()) {
1096 0 : case ElementType::ARTICULATION:
1097 : // form Articulation layout
1098 0 : LD_CONDITION(item_cast<const Articulation*>(e)->ldata()->symId.has_value());
1099 0 : break;
1100 0 : case ElementType::SYMBOL:
1101 : LD_INDEPENDENT;
1102 0 : break;
1103 0 : case ElementType::IMAGE:
1104 : // will be done
1105 : // LD_CONDITION(parentLD->isSetBbox());
1106 : LD_INDEPENDENT;
1107 0 : break;
1108 0 : default:
1109 0 : UNREACHABLE;
1110 : }
1111 : }
1112 :
1113 112638 : ldata->setMag(ctx.conf().styleB(Sid::scaleBarlines) && item->staff() ? item->staff()->staffMag(item->tick()) : 1.0);
1114 : // Note: the true values of y1 and y2 are computed in layout2() (can be done only
1115 : // after staff distances are known). This is a temporary layout.
1116 112638 : const double spatium = item->spatium();
1117 :
1118 112638 : ldata->y1 = spatium * .5 * item->spanFrom();
1119 112638 : ldata->y2 = spatium * .5 * (8.0 + item->spanTo());
1120 :
1121 112638 : const IEngravingFontPtr font = ctx.engravingFont();
1122 112638 : const double magS = ctx.conf().magS(ldata->mag());
1123 :
1124 112638 : double w = barLineWidth(item, ctx.conf().style(), font->width(SymId::repeatDot, magS)) * ldata->mag();
1125 112638 : RectF r(0.0, ldata->y1, w, ldata->y2 - ldata->y1);
1126 :
1127 112638 : if (ctx.conf().styleB(Sid::repeatBarTips)) {
1128 157 : switch (item->barLineType()) {
1129 4 : case BarLineType::START_REPEAT: {
1130 4 : r.unite(font->bbox(SymId::bracketTop, magS).translated(0, ldata->y1));
1131 4 : } break;
1132 8 : case BarLineType::END_REPEAT: {
1133 8 : double w1 = font->width(SymId::reversedBracketTop, magS) - w;
1134 8 : r.unite(font->bbox(SymId::reversedBracketTop, magS).translated(-w1, ldata->y1));
1135 8 : } break;
1136 0 : case BarLineType::END_START_REPEAT: {
1137 0 : double w1 = font->width(SymId::reversedBracketTop, magS) - w;
1138 0 : r.unite(font->bbox(SymId::reversedBracketTop, magS).translated(-w1, ldata->y1));
1139 0 : r.unite(font->bbox(SymId::bracketTop, magS).translated(0, ldata->y1));
1140 0 : } break;
1141 145 : default:
1142 145 : break;
1143 : }
1144 : }
1145 :
1146 112638 : ldata->setBbox(r);
1147 :
1148 112638 : updateBarlineShape(item, ldata, ctx);
1149 :
1150 : //! NOTE The types are listed here explicitly to show what types there are (see add method)
1151 : //! and accordingly show what the barline layout depends on.
1152 112638 : for (EngravingItem* e : *item->el()) {
1153 0 : switch (e->type()) {
1154 0 : case ElementType::ARTICULATION: {
1155 0 : Articulation* a = item_cast<Articulation*>(e);
1156 0 : Articulation::LayoutData* aldata = a->mutldata();
1157 0 : TLayout::layoutArticulation(a, aldata);
1158 0 : DirectionV dir = a->direction();
1159 0 : double distance = 0.5 * spatium;
1160 0 : double x = /*barline*/ ldata->bbox().width() * 0.5;
1161 0 : if (dir == DirectionV::DOWN) {
1162 0 : double botY = ldata->y2 + distance;
1163 0 : aldata->setPos(PointF(x, botY));
1164 : } else {
1165 0 : double topY = ldata->y1 - distance;
1166 0 : aldata->setPos(PointF(x, topY));
1167 : }
1168 0 : } break;
1169 0 : case ElementType::SYMBOL: {
1170 0 : Symbol* sb = item_cast<Symbol*>(e);
1171 0 : TLayout::layoutSymbol(sb, sb->mutldata(), ctx);
1172 0 : } break;
1173 0 : case ElementType::IMAGE: {
1174 0 : Image* im = item_cast<Image*>(e);
1175 0 : TLayout::layoutImage(im, im->mutldata());
1176 0 : } break;
1177 0 : default:
1178 0 : UNREACHABLE;
1179 : }
1180 : }
1181 :
1182 112638 : if (!item->segment()) {
1183 0 : return;
1184 : }
1185 :
1186 112638 : if (Fermata* fermata = toFermata(item->segment()->findAnnotation(ElementType::FERMATA, item->track(), item->track() + VOICES))) {
1187 52 : layoutFermata(fermata, fermata->mutldata());
1188 : }
1189 : }
1190 :
1191 164959 : void TLayout::updateBarlineShape(const BarLine* item, BarLine::LayoutData* ldata, const LayoutContext& ctx)
1192 : {
1193 : LAYOUT_CALL_ITEM(item);
1194 :
1195 164959 : RectF bb = ldata->bbox();
1196 164959 : if (item->staff()) {
1197 : // actual height may include span to next staff
1198 : // but this should not be included in shapes or skylines
1199 164959 : double sp = item->spatium();
1200 164959 : int span = item->staff()->lines(item->tick()) - 1;
1201 : int sFrom;
1202 : int sTo;
1203 164959 : if (span == 0 && item->spanTo() == 0) {
1204 177 : sFrom = BARLINE_SPAN_1LINESTAFF_FROM;
1205 177 : sTo = item->spanStaff() ? 0 : BARLINE_SPAN_1LINESTAFF_TO;
1206 : } else {
1207 164782 : sFrom = item->spanFrom();
1208 164782 : sTo = item->spanStaff() ? 0 : item->spanTo();
1209 : }
1210 164959 : double y = sp * sFrom * 0.5;
1211 164959 : double h = sp * (span + (sTo - sFrom) * 0.5);
1212 164959 : if (ctx.conf().styleB(Sid::repeatBarTips)) {
1213 232 : switch (item->barLineType()) {
1214 20 : case BarLineType::START_REPEAT:
1215 : case BarLineType::END_REPEAT:
1216 : case BarLineType::END_START_REPEAT: {
1217 20 : if (item->isTop()) {
1218 10 : double top = item->symBbox(SymId::bracketTop).height();
1219 10 : y -= top;
1220 10 : h += top;
1221 : }
1222 20 : if (item->isBottom()) {
1223 10 : double bottom = item->symBbox(SymId::bracketBottom).height();
1224 10 : h += bottom;
1225 : }
1226 : }
1227 : default:
1228 232 : break;
1229 : }
1230 : }
1231 164959 : bb.setTop(y);
1232 164959 : bb.setHeight(h);
1233 : }
1234 :
1235 164959 : ldata->setShape(Shape(bb, item));
1236 164959 : }
1237 :
1238 : //---------------------------------------------------------
1239 : // called after system layout; set vertical dimensions
1240 : //---------------------------------------------------------
1241 55635 : void TLayout::layoutBarLine2(BarLine* item, LayoutContext& ctx)
1242 : {
1243 : LAYOUT_CALL_ITEM(item);
1244 :
1245 55635 : BarLine::LayoutData* ldata = item->mutldata();
1246 :
1247 55635 : if (ldata->isSkipDraw()) {
1248 0 : return;
1249 : }
1250 :
1251 55635 : item->calcY();
1252 55635 : RectF bbox = ldata->bbox();
1253 55635 : bbox.setTop(ldata->y1);
1254 55635 : bbox.setBottom(ldata->y2);
1255 :
1256 55635 : if (ctx.conf().styleB(Sid::repeatBarTips)) {
1257 75 : switch (item->barLineType()) {
1258 4 : case BarLineType::START_REPEAT:
1259 4 : bbox.unite(item->symBbox(SymId::bracketTop).translated(0, ldata->y1));
1260 4 : bbox.unite(item->symBbox(SymId::bracketBottom).translated(0, ldata->y2));
1261 4 : break;
1262 4 : case BarLineType::END_REPEAT:
1263 : {
1264 4 : double w1 = item->symBbox(SymId::reversedBracketTop).x() - bbox.x();
1265 4 : bbox.unite(item->symBbox(SymId::reversedBracketTop).translated(-w1, ldata->y1));
1266 4 : bbox.unite(item->symBbox(SymId::reversedBracketBottom).translated(-w1, ldata->y2));
1267 4 : break;
1268 : }
1269 0 : case BarLineType::END_START_REPEAT:
1270 : {
1271 0 : double w1 = item->symBbox(SymId::reversedBracketTop).x() - bbox.x();
1272 0 : bbox.unite(item->symBbox(SymId::reversedBracketTop).translated(-w1, ldata->y1));
1273 0 : bbox.unite(item->symBbox(SymId::reversedBracketBottom).translated(-w1, ldata->y2));
1274 0 : bbox.unite(item->symBbox(SymId::bracketTop).translated(0, ldata->y1));
1275 0 : bbox.unite(item->symBbox(SymId::bracketBottom).translated(0, ldata->y2));
1276 0 : break;
1277 : }
1278 67 : default:
1279 67 : break;
1280 : }
1281 : }
1282 :
1283 55635 : ldata->setBbox(bbox);
1284 : }
1285 :
1286 8751 : void TLayout::layoutBeam(Beam* item, const LayoutContext& ctx)
1287 : {
1288 : LAYOUT_CALL_ITEM(item);
1289 8751 : BeamLayout::layout(item, ctx);
1290 8751 : }
1291 :
1292 109 : void TLayout::layoutBeam1(Beam* item, LayoutContext& ctx)
1293 : {
1294 : LAYOUT_CALL_ITEM(item);
1295 109 : BeamLayout::layout1(item, ctx);
1296 109 : }
1297 :
1298 4 : void TLayout::layoutBend(const Bend* item, Bend::LayoutData* ldata)
1299 : {
1300 : LAYOUT_CALL_ITEM(item);
1301 4 : IF_ASSERT_FAILED(item->ownershipParent()) {
1302 0 : return;
1303 : }
1304 :
1305 4 : LD_CONDITION(item->note()->ldata()->isSetPos());
1306 4 : LD_CONDITION(item->note()->ldata()->isSetBbox());
1307 :
1308 4 : double spatium = item->spatium();
1309 4 : double lw = item->absoluteFromSpatium(item->lineWidth());
1310 :
1311 4 : const Note::LayoutData* noteLD = item->note()->ldata();
1312 4 : PointF notePos = noteLD->pos();
1313 4 : notePos.ry() = std::max(notePos.y(), 0.0);
1314 :
1315 4 : ldata->noteWidth = noteLD->bbox().width();
1316 4 : ldata->notePos = notePos;
1317 :
1318 4 : RectF bb;
1319 :
1320 4 : FontMetrics fm(item->font(spatium));
1321 :
1322 4 : size_t n = item->points().size();
1323 4 : double x = ldata->noteWidth;
1324 4 : double y = -spatium * .8;
1325 4 : double x2 = 0.0, y2 = 0.0;
1326 :
1327 4 : double aw = spatium * .5;
1328 4 : PolygonF arrowUp;
1329 4 : arrowUp << PointF(0, 0) << PointF(aw * .5, aw) << PointF(-aw * .5, aw);
1330 4 : PolygonF arrowDown;
1331 4 : arrowDown << PointF(0, 0) << PointF(aw * .5, -aw) << PointF(-aw * .5, -aw);
1332 :
1333 8 : for (size_t pt = 0; pt < n; ++pt) {
1334 8 : if (pt == (n - 1)) {
1335 0 : break;
1336 : }
1337 8 : int pitch = item->points().at(pt).pitch;
1338 8 : if (pt == 0 && pitch) {
1339 0 : y2 = -ldata->notePos.y() - spatium * 2;
1340 0 : x2 = x;
1341 0 : bb.unite(RectF(x, y, x2 - x, y2 - y));
1342 :
1343 0 : bb.unite(arrowUp.translated(x2, y2 + spatium * .2).boundingRect());
1344 :
1345 0 : int idx = (pitch + 12) / 25;
1346 0 : const char* l = Bend::label[idx];
1347 0 : bb.unite(fm.boundingRect(String::fromAscii(l)));
1348 0 : y = y2;
1349 : }
1350 8 : if (pitch == item->points().at(pt + 1).pitch) {
1351 4 : if (pt == (n - 2)) {
1352 4 : break;
1353 : }
1354 0 : x2 = x + spatium;
1355 0 : y2 = y;
1356 0 : bb.unite(RectF(x, y, x2 - x, y2 - y));
1357 4 : } else if (pitch < item->points().at(pt + 1).pitch) {
1358 : // up
1359 4 : x2 = x + spatium * .5;
1360 4 : y2 = -ldata->notePos.y() - spatium * 2;
1361 4 : double dx = x2 - x;
1362 4 : double dy = y2 - y;
1363 :
1364 4 : PainterPath path;
1365 4 : path.moveTo(x, y);
1366 4 : path.cubicTo(x + dx / 2, y, x2, y + dy / 4, x2, y2);
1367 4 : bb.unite(path.boundingRect());
1368 4 : bb.unite(arrowUp.translated(x2, y2 + spatium * .2).boundingRect());
1369 :
1370 4 : int idx = (item->points().at(pt + 1).pitch + 12) / 25;
1371 4 : const char* l = Bend::label[idx];
1372 4 : bb.unite(fm.boundingRect(String::fromAscii(l)));
1373 4 : } else {
1374 : // down
1375 0 : x2 = x + spatium * .5;
1376 0 : y2 = y + spatium * 3;
1377 0 : double dx = x2 - x;
1378 0 : double dy = y2 - y;
1379 :
1380 0 : PainterPath path;
1381 0 : path.moveTo(x, y);
1382 0 : path.cubicTo(x + dx / 2, y, x2, y + dy / 4, x2, y2);
1383 0 : bb.unite(path.boundingRect());
1384 :
1385 0 : bb.unite(arrowDown.translated(x2, y2 - spatium * .2).boundingRect());
1386 0 : }
1387 4 : x = x2;
1388 4 : y = y2;
1389 : }
1390 4 : bb.adjust(-lw, -lw, lw, lw);
1391 :
1392 4 : ldata->setBbox(bb);
1393 4 : ldata->setPos(PointF());
1394 4 : }
1395 :
1396 147 : void TLayout::layoutHBox(const HBox* item, HBox::LayoutData* ldata, const LayoutContext& ctx)
1397 : {
1398 : LAYOUT_CALL_ITEM(item);
1399 147 : BoxLayout::layoutHBox(item, ldata, ctx);
1400 147 : }
1401 :
1402 0 : void TLayout::layoutVBox(const VBox* item, VBox::LayoutData* ldata, const LayoutContext& ctx)
1403 : {
1404 : LAYOUT_CALL_ITEM(item);
1405 0 : BoxLayout::layoutVBox(item, ldata, ctx);
1406 0 : }
1407 :
1408 0 : void TLayout::layoutFBox(const FBox* item, FBox::LayoutData* ldata, const LayoutContext& ctx)
1409 : {
1410 : LAYOUT_CALL_ITEM(item);
1411 :
1412 0 : BoxLayout::layoutFBox(item, ldata, ctx);
1413 0 : }
1414 :
1415 0 : void TLayout::layoutTBox(const TBox* item, TBox::LayoutData* ldata, const LayoutContext& ctx)
1416 : {
1417 : LAYOUT_CALL_ITEM(item);
1418 0 : BoxLayout::layoutTBox(item, ldata, ctx);
1419 0 : }
1420 :
1421 7436 : void TLayout::layoutBracket(const Bracket* item, Bracket::LayoutData* ldata, const LayoutConfiguration& conf)
1422 : {
1423 : LAYOUT_CALL_ITEM(item);
1424 7436 : LD_CONDITION(ldata->bracketHeight.has_value());
1425 7436 : if (!ldata->bracketHeight.has_value()) {
1426 0 : return;
1427 : }
1428 :
1429 7436 : const_cast<Bracket*>(item)->setVisible(item->bi()->visible());
1430 :
1431 7436 : ldata->braceSymbol = item->braceSymbol();
1432 :
1433 7436 : switch (item->bracketType()) {
1434 4532 : case BracketType::BRACE: {
1435 4532 : String musicalSymbolFont = conf.styleSt(Sid::musicalSymbolFont);
1436 4532 : if (musicalSymbolFont == "Emmentaler" || musicalSymbolFont == "Gonville") {
1437 759 : ldata->braceSymbol = SymId::noSym;
1438 759 : double w = conf.styleAbsolute(Sid::akkoladeWidth);
1439 759 : double h2 = ldata->bracketHeight * 0.5;
1440 :
1441 : #define XM(a) (a + 700) * w / 700
1442 : #define YM(a) (a + 7100) * h2 / 7100
1443 :
1444 759 : PainterPath path;
1445 759 : path.moveTo(XM(-8), YM(-2048));
1446 759 : path.cubicTo(XM(-8), YM(-3192), XM(-360), YM(-4304), XM(-360), YM(-5400)); // c 0
1447 759 : path.cubicTo(XM(-360), YM(-5952), XM(-264), YM(-6488), XM(32), YM(-6968)); // c 1
1448 759 : path.cubicTo(XM(36), YM(-6974), XM(38), YM(-6984), XM(38), YM(-6990)); // c 0
1449 759 : path.cubicTo(XM(38), YM(-7008), XM(16), YM(-7024), XM(0), YM(-7024)); // c 0
1450 759 : path.cubicTo(XM(-8), YM(-7024), XM(-22), YM(-7022), XM(-32), YM(-7008)); // c 1
1451 759 : path.cubicTo(XM(-416), YM(-6392), XM(-544), YM(-5680), XM(-544), YM(-4960)); // c 0
1452 759 : path.cubicTo(XM(-544), YM(-3800), XM(-168), YM(-2680), XM(-168), YM(-1568)); // c 0
1453 759 : path.cubicTo(XM(-168), YM(-1016), XM(-264), YM(-496), XM(-560), YM(-16)); // c 1
1454 759 : path.lineTo(XM(-560), YM(0)); // l 1
1455 759 : path.lineTo(XM(-560), YM(16)); // l 1
1456 759 : path.cubicTo(XM(-264), YM(496), XM(-168), YM(1016), XM(-168), YM(1568)); // c 0
1457 759 : path.cubicTo(XM(-168), YM(2680), XM(-544), YM(3800), XM(-544), YM(4960)); // c 0
1458 759 : path.cubicTo(XM(-544), YM(5680), XM(-416), YM(6392), XM(-32), YM(7008)); // c 1
1459 759 : path.cubicTo(XM(-22), YM(7022), XM(-8), YM(7024), XM(0), YM(7024)); // c 0
1460 759 : path.cubicTo(XM(16), YM(7024), XM(38), YM(7008), XM(38), YM(6990)); // c 0
1461 759 : path.cubicTo(XM(38), YM(6984), XM(36), YM(6974), XM(32), YM(6968)); // c 1
1462 759 : path.cubicTo(XM(-264), YM(6488), XM(-360), YM(5952), XM(-360), YM(5400)); // c 0
1463 759 : path.cubicTo(XM(-360), YM(4304), XM(-8), YM(3192), XM(-8), YM(2048)); // c 0
1464 759 : path.cubicTo(XM(-8), YM(1320), XM(-136), YM(624), XM(-512), YM(0)); // c 1
1465 759 : path.cubicTo(XM(-136), YM(-624), XM(-8), YM(-1320), XM(-8), YM(-2048)); // c 0*/
1466 759 : ldata->path = path;
1467 759 : ldata->setBbox(path.boundingRect());
1468 759 : ldata->shape.add(ldata->bbox());
1469 759 : ldata->bracketWidth = w + conf.styleAbsolute(Sid::akkoladeBarDistance);
1470 759 : } else {
1471 3773 : if (item->braceSymbol() == SymId::noSym) {
1472 0 : ldata->braceSymbol = SymId::brace;
1473 : }
1474 3773 : double h = ldata->bracketHeight;
1475 3773 : double w = item->symWidth(ldata->braceSymbol) * item->magx();
1476 3773 : ldata->setBbox(RectF(0, 0, w, h));
1477 3773 : ldata->shape.add(ldata->bbox());
1478 3773 : ldata->bracketWidth = w + conf.styleAbsolute(Sid::akkoladeBarDistance);
1479 : }
1480 4532 : }
1481 4532 : break;
1482 2269 : case BracketType::NORMAL: {
1483 2269 : double w = conf.styleAbsolute(Sid::bracketWidth) * .5;
1484 2269 : double bd = item->spatium() * (conf.styleSt(Sid::musicalSymbolFont) == "Leland" ? .5 : .25);
1485 :
1486 2269 : Shape shape = RectF(-w, -bd, w * 2, 2 * (ldata->bracketHeight * 0.5 + bd));
1487 2269 : shape.add(item->symBbox(SymId::bracketTop).translated(PointF(-w, -bd)));
1488 2269 : shape.add(item->symBbox(SymId::bracketBottom).translated(PointF(-w, bd + ldata->bracketHeight)));
1489 2269 : ldata->setShape(shape);
1490 :
1491 2269 : ldata->bracketWidth = conf.styleAbsolute(Sid::bracketWidth) + conf.styleAbsolute(Sid::bracketDistance);
1492 2269 : }
1493 2269 : break;
1494 593 : case BracketType::SQUARE:
1495 : {
1496 593 : double w = conf.styleAbsolute(Sid::staffLineWidth) * .5;
1497 593 : double x = -w;
1498 593 : double y = -w;
1499 593 : double h = (ldata->bracketHeight * 0.5 + w) * 2;
1500 593 : w += (.5 * item->spatium() + 2 * w);
1501 593 : ldata->setBbox(RectF(x, y, w, h));
1502 593 : ldata->shape.add(ldata->bbox());
1503 :
1504 593 : ldata->bracketWidth = conf.styleAbsolute(Sid::staffLineWidth) / 2 + 0.5 * item->spatium();
1505 : }
1506 593 : break;
1507 42 : case BracketType::LINE: {
1508 42 : double _spatium = item->spatium();
1509 42 : double w = 0.67 * conf.styleAbsolute(Sid::bracketWidth) * .5;
1510 42 : double x = -w;
1511 42 : double bd = _spatium * .25;
1512 42 : double y = -bd;
1513 42 : double h = (-y + ldata->bracketHeight * 0.5) * 2;
1514 42 : ldata->setBbox(RectF(x, y, w, h));
1515 42 : ldata->shape.add(ldata->bbox());
1516 :
1517 42 : ldata->bracketWidth = 0.67 * conf.styleAbsolute(Sid::bracketWidth) + conf.styleAbsolute(Sid::bracketDistance);
1518 : }
1519 42 : break;
1520 0 : case BracketType::GROUP:
1521 0 : layoutGroupBracket(item, ldata, conf);
1522 0 : break;
1523 0 : case BracketType::NO_BRACKET:
1524 0 : break;
1525 : }
1526 : }
1527 :
1528 0 : void TLayout::layoutGroupBracket(const Bracket* item, Bracket::LayoutData* ldata, const LayoutConfiguration& conf)
1529 : {
1530 0 : BracketItem* bracketItem = item->bracketItem();
1531 :
1532 0 : double w = conf.styleAbsolute(Sid::groupBracketLineWidth) * 0.5;
1533 0 : double x = 0.0;
1534 0 : double y = -w;
1535 0 : double h = (ldata->bracketHeight * 0.5 + w) * 2;
1536 0 : double width = conf.styleAbsolute(Sid::groupBracketHookLen);
1537 :
1538 0 : ldata->bracketWidth = std::max(width, 2 * w);
1539 :
1540 0 : Shape shape;
1541 0 : shape.add(RectF(x, y, width, h), item);
1542 0 : ldata->setShape(shape);
1543 :
1544 0 : if (!item->text()) {
1545 0 : Text* bracketText = new Text(const_cast<Bracket*>(item), TextStyleType::GROUP_BRACKET);
1546 0 : bracketText->setSelected(item->selected());
1547 0 : bracketText->setGenerated(true);
1548 0 : const_cast<Bracket*>(item)->setText(bracketText);
1549 : }
1550 :
1551 0 : Text* text = item->text();
1552 0 : text->setVisible(bracketItem->visible() && bracketItem->showText());
1553 0 : text->setAlign(Align(text->align().horizontal, AlignV::VCENTER));
1554 0 : text->setPosition(AlignH::HCENTER);
1555 :
1556 0 : Orientation textOrientation = conf.styleV(Sid::groupBracketTextOrientation).value<Orientation>();
1557 0 : text->setTextAngle(textOrientation == Orientation::VERTICAL ? -90.0 : 0.0);
1558 :
1559 0 : double textPadding = 0.5 * item->spatium();
1560 0 : double bracketHeight = item->ldata()->bbox().height();
1561 0 : double minHeight = bracketHeight - 6 * textPadding;
1562 :
1563 0 : if (textOrientation == Orientation::HORIZONTAL) {
1564 0 : text->setXmlText(item->labelType() == InstrumentNameType::LONG ? bracketItem->longName() : bracketItem->shortName());
1565 0 : layoutText(text, text->mutldata());
1566 : } else {
1567 : // Vertical labels ignore labelType(): use the long name if it fits, otherwise the short name
1568 0 : text->setXmlText(bracketItem->longName());
1569 0 : layoutText(text, text->mutldata());
1570 0 : if (text->ldata()->bbox().height() > minHeight) {
1571 0 : text->setXmlText(bracketItem->shortName());
1572 0 : layoutText(text, text->mutldata());
1573 : }
1574 : }
1575 :
1576 0 : text->mutldata()->setPosY(bracketHeight / 2);
1577 :
1578 0 : switch (conf.styleV(Sid::groupBracketTextAlign).value<DirectionH>()) {
1579 0 : case DirectionH::AUTO:
1580 : {
1581 0 : text->mutldata()->setPosX(0.0);
1582 0 : if (text->visible()) {
1583 0 : RectF mask = text->ldata()->bbox().translated(text->pos()).padded(textPadding);
1584 0 : ldata->setMask(mask);
1585 : }
1586 0 : break;
1587 : }
1588 0 : case DirectionH::LEFT:
1589 0 : text->mutldata()->setPosX(-text->mutldata()->bbox().right() - textPadding);
1590 0 : break;
1591 0 : case DirectionH::RIGHT:
1592 0 : text->mutldata()->setPosX(-text->mutldata()->bbox().left() + textPadding);
1593 : }
1594 :
1595 0 : shape.add(text->ldata()->shape().translated(text->pos()));
1596 0 : ldata->setShape(shape);
1597 0 : }
1598 :
1599 168 : void TLayout::layoutBreath(const Breath* item, Breath::LayoutData* ldata, const LayoutConfiguration& conf)
1600 : {
1601 : LAYOUT_CALL_ITEM(item);
1602 : LD_INDEPENDENT;
1603 :
1604 : // TODO review with all other instances of this
1605 : // if (ldata->isValid()) {
1606 : // return;
1607 : // }
1608 :
1609 168 : const double voiceOffset = item->placeBelow() ? item->staff()->staffHeight(item->tick()) : 0.0;
1610 168 : if (item->isCaesura()) {
1611 42 : if (item->symId() == SymId::chantCaesura) {
1612 : // special handling for chant caesura, which is higher than the normal one
1613 2 : ldata->setPosY(1.5 * item->spatium() + voiceOffset);
1614 : } else {
1615 40 : ldata->setPosY(item->symHeight(item->symId()) / 2 + voiceOffset);
1616 : }
1617 126 : } else if ((conf.styleSt(Sid::musicalSymbolFont) == "Emmentaler") && (item->symId() == SymId::breathMarkComma)) {
1618 15 : const double shift = item->placeBelow() ? -0.5 * item->spatium() + item->symHeight(item->symId()) : 0.5 * item->spatium();
1619 15 : ldata->setPosY(shift + voiceOffset);
1620 : } else {
1621 111 : const double shift = item->placeBelow() ? 0.5 * item->spatium() + item->symHeight(item->symId()) : -0.5 * item->spatium();
1622 111 : ldata->setPosY(shift + voiceOffset);
1623 : }
1624 :
1625 168 : ldata->setBbox(item->symBbox(item->symId()));
1626 :
1627 168 : ldata->setPosX(-ldata->bbox().right());
1628 168 : }
1629 :
1630 49746 : void TLayout::layoutChord(Chord* item, LayoutContext& ctx)
1631 : {
1632 : LAYOUT_CALL_ITEM(item);
1633 49746 : ChordLayout::layout(item, ctx);
1634 49746 : }
1635 :
1636 168 : void TLayout::layoutChordLine(const ChordLine* item, ChordLine::LayoutData* ldata, const LayoutConfiguration& conf)
1637 : {
1638 : LAYOUT_CALL_ITEM(item);
1639 168 : IF_ASSERT_FAILED(item->ownershipParent()) {
1640 0 : return;
1641 : }
1642 :
1643 168 : Note* note = nullptr;
1644 168 : const double spatium = item->spatium();
1645 :
1646 168 : if (item->note()) {
1647 115 : note = item->chord()->findNote(item->note()->pitch());
1648 : }
1649 :
1650 168 : if (!note) {
1651 53 : note = item->chord()->upNote();
1652 : }
1653 :
1654 168 : const Note::LayoutData* noteLD = note->ldata();
1655 :
1656 : //! NOTE Temporary, instead of item->chord()->shape()
1657 168 : LD_CONDITION(item->chord()->ldata()->isSetBbox());
1658 :
1659 168 : LD_CONDITION(noteLD->isSetPos());
1660 168 : LD_CONDITION(noteLD->isSetBbox());
1661 :
1662 168 : ldata->setMag(item->chord()->mag());
1663 :
1664 168 : layoutChordLinePath(item, ldata, spatium, item->chordMag());
1665 :
1666 168 : double x = 0.0;
1667 168 : double y = noteLD->pos().y();
1668 168 : double horOffset = 0.33 * spatium; // one third of a space away from the note
1669 168 : double vertOffset = 0.25 * spatium; // one quarter of a space from the center line
1670 : // Get chord shape
1671 168 : Shape chordShape = item->chord()->shape();
1672 : // ...but remove from the shape items that the chordline shouldn't try to avoid
1673 : // (especially the chordline itself)
1674 168 : chordShape.remove_if([](ShapeElement& shapeEl){
1675 378 : if (!shapeEl.item()) {
1676 0 : return true;
1677 : }
1678 378 : const EngravingItem* item = shapeEl.item();
1679 378 : if (item->isChordLine() || item->isHarmony() || item->isLyrics()) {
1680 1 : return true;
1681 : }
1682 377 : return false;
1683 : });
1684 :
1685 168 : const RectF r = item->isWavy() ? conf.engravingFont()->bbox(item->waveSym(), item->magS()) : ldata->path.boundingRect();
1686 :
1687 168 : y += item->isBelow() ? vertOffset : -vertOffset;
1688 :
1689 168 : const double yBelow = y + r.height();
1690 : double chordEdge
1691 168 : = item->isToTheLeft() ? chordShape.leftMostEdgeAtHeight(y, yBelow) : chordShape.rightMostEdgeAtHeight(y, yBelow);
1692 :
1693 168 : if (!item->isToTheLeft()) {
1694 : // Always place lines to the right of dots
1695 91 : double xMin = chordShape.left();
1696 286 : for (ShapeElement& shapeEl : chordShape.elements()) {
1697 195 : if (!shapeEl.item() || !shapeEl.item()->isNoteDot()) {
1698 195 : continue;
1699 : }
1700 :
1701 0 : xMin = std::max(xMin, shapeEl.right());
1702 : }
1703 91 : chordEdge = std::max(chordEdge, xMin);
1704 : }
1705 :
1706 168 : x += item->isToTheLeft() ? chordEdge - horOffset : chordEdge + horOffset;
1707 168 : ldata->setPos(x, y);
1708 :
1709 168 : if (!item->isWavy()) {
1710 162 : int x1 = 0, y1 = 0, width = 0, height = 0;
1711 :
1712 162 : x1 = r.x();
1713 162 : y1 = r.y();
1714 162 : width = r.width();
1715 162 : height = r.height();
1716 162 : ldata->setBbox(x1, y1, width, height);
1717 : } else {
1718 : // Align an eighth space above/below the centre of the note
1719 6 : if (item->isBelow()) {
1720 4 : ldata->moveY(r.height() - spatium * 0.125);
1721 : } else {
1722 2 : ldata->moveY(spatium * 0.075);
1723 : }
1724 :
1725 6 : if (item->isToTheLeft()) {
1726 2 : ldata->moveX(-r.width());
1727 : }
1728 :
1729 6 : ldata->setBbox(r);
1730 : }
1731 168 : }
1732 :
1733 174 : void TLayout::layoutChordLinePath(const ChordLine* item, ChordLine::LayoutData* ldata, double spatium, double mag)
1734 : {
1735 174 : ldata->path = PainterPath();
1736 174 : if (item->chordLineType() == ChordLineType::NOTYPE || item->isWavy()) {
1737 7 : return;
1738 : }
1739 :
1740 167 : const ChordLine::Points defaultPoints = item->defaultPoints();
1741 167 : const ChordLine::Points& offsets = item->bezierOffsets();
1742 167 : ChordLine::Points points;
1743 1002 : for (size_t i = 0; i < points.size(); ++i) {
1744 668 : points[i] = (defaultPoints[i] * spatium + offsets[i]) * mag;
1745 : }
1746 :
1747 167 : PainterPath path;
1748 167 : path.moveTo(points[size_t(ChordLine::Point::START)]);
1749 167 : if (item->isStraight()) {
1750 95 : path.lineTo(points[size_t(ChordLine::Point::END)]);
1751 : } else {
1752 72 : path.cubicTo(points[size_t(ChordLine::Point::BEZIER1)], points[size_t(ChordLine::Point::BEZIER2)],
1753 72 : points[size_t(ChordLine::Point::END)]);
1754 : }
1755 167 : ldata->path = path;
1756 167 : }
1757 :
1758 24448 : void TLayout::layoutClef(const Clef* item, Clef::LayoutData* ldata, const LayoutConfiguration& conf)
1759 : {
1760 : LAYOUT_CALL_ITEM(item);
1761 : LD_INDEPENDENT;
1762 :
1763 : // determine current number of lines and line distance
1764 24448 : int lines = 5;
1765 24448 : Spatium lineDist = 1.0_sp;
1766 24448 : int stepOffset = 0;
1767 24448 : Spatium staffOffsetY = 0_sp;
1768 :
1769 24448 : Segment* clefSeg = item->segment();
1770 :
1771 : // check clef visibility and type compatibility
1772 24448 : if (clefSeg && item->staff()) {
1773 24448 : StaffType* stVisibility = item->staff()->staffType(item->tick());
1774 24448 : bool show = stVisibility->genClef(); // check staff type allows clef display
1775 :
1776 24448 : const bool endOfMeasureClef = clefSeg->rtick() == clefSeg->measure()->ticks();
1777 24448 : const Fraction tick = endOfMeasureClef && !item->isTrailer() ? item->tick() : clefSeg->measure()->tick();
1778 :
1779 24448 : const Fraction tickPrev = tick - Fraction::eps();
1780 24448 : const StaffType* st = item->staff()->staffType(tick);
1781 24448 : const StaffType* stPrev = !tickPrev.negative() ? item->staff()->staffType(tickPrev) : nullptr;
1782 24448 : StaffGroup staffGroup = st->group();
1783 24448 : const bool hideClef = st->isTabStaff() ? conf.styleB(Sid::hideTabClefAfterFirst) : !conf.styleB(Sid::genClef);
1784 :
1785 : // if not tab, use instrument->useDrumset to set staffGroup (to allow pitched to unpitched in same staff)
1786 24448 : if (staffGroup != StaffGroup::TAB) {
1787 23853 : staffGroup = item->originPart()->instrument(item->tick())->useDrumset() ? StaffGroup::PERCUSSION : StaffGroup::STANDARD;
1788 : }
1789 :
1790 : // check clef is compatible with staff type group:
1791 24448 : if (ClefInfo::staffGroup(item->clefType()) != staffGroup) {
1792 448 : if (tick > Fraction(0, 1) && (!item->generated() || hideClef)) { // if clef is not generated, hide it
1793 0 : show = false;
1794 : } else { // if generated, replace with initial clef type
1795 : // TODO : instead of initial staff clef (which is assumed to be compatible)
1796 : // use the last compatible clef previously found in staff
1797 448 : const_cast<Clef*>(item)->setClefType(item->staff()->clefType(Fraction(0, 1)));
1798 : }
1799 : }
1800 :
1801 : // if clef not to show or not compatible with staff group
1802 24448 : if (!show) {
1803 0 : ldata->setBbox(RectF());
1804 0 : ldata->symId = SymId::noSym;
1805 0 : LOGD("invisible clef at tick %d(%d) staff %zu",
1806 0 : item->segment()->tick().ticks(), item->segment()->tick().ticks() / 1920, item->staffIdx());
1807 0 : return;
1808 : }
1809 24448 : lines = st->lines(); // init values from staff type
1810 24448 : lineDist = st->lineDistance();
1811 24448 : stepOffset = st->stepOffset();
1812 :
1813 24448 : const Spatium stOffset = st->yoffset();
1814 24448 : const Spatium stPrevOffset = stPrev && clefSeg->rtick() != Fraction(0, 1) ? stPrev->yoffset() : 0.0_sp;
1815 24448 : staffOffsetY = item->isHeader() ? 0.0_sp : stOffset - stPrevOffset;
1816 : }
1817 :
1818 24448 : double _spatium = item->spatium();
1819 24448 : Spatium yoff = 0.0_sp;
1820 24448 : if (item->clefType() != ClefType::INVALID && item->clefType() != ClefType::MAX) {
1821 24448 : ldata->symId = ClefInfo::symId(item->clefType());
1822 24448 : yoff = lineDist * (5 - ClefInfo::line(item->clefType()));
1823 : } else {
1824 0 : ldata->symId = SymId::noSym;
1825 : }
1826 :
1827 24448 : switch (item->clefType()) {
1828 3 : case ClefType::C_19C: // 19th C clef is like a G clef
1829 3 : yoff = lineDist * 1.5;
1830 3 : break;
1831 595 : case ClefType::TAB: // TAB clef
1832 : case ClefType::TAB4: // TAB clef 4 strings
1833 : case ClefType::TAB_SERIF: // TAB clef alternate style
1834 : case ClefType::TAB4_SERIF: // TAB clef alternate style
1835 : // on tablature, position clef at half the number of spaces * line distance
1836 595 : yoff = lineDist * (lines - 1) * .5;
1837 595 : stepOffset = 0; // ignore stepOffset for TAB and percussion clefs
1838 595 : break;
1839 1109 : case ClefType::PERC: // percussion clefs
1840 : case ClefType::PERC2:
1841 1109 : yoff = lineDist * (lines - 1) * 0.5;
1842 1109 : stepOffset = 0;
1843 1109 : break;
1844 0 : case ClefType::INVALID:
1845 : case ClefType::MAX:
1846 0 : LOGD("Clef::layout: invalid type");
1847 0 : return;
1848 22741 : default:
1849 22741 : break;
1850 : }
1851 : // clefs on palette or at start of system/measure are left aligned
1852 : // other clefs are right aligned
1853 48896 : Shape shape(item->symShapeWithCutouts(ldata->symId));
1854 24448 : bool isMidMeasureClef = item->isMidMeasureClef();
1855 24448 : double x = isMidMeasureClef ? -shape.right() : 0.0;
1856 24448 : ldata->setPos(PointF(x, (yoff + (Spatium(stepOffset) * 0.5) + staffOffsetY).toAbsolute(_spatium)));
1857 24448 : if (item->isMidMeasureClef()) {
1858 744 : ldata->setShape(shape);
1859 : } else {
1860 23704 : ldata->setBbox(item->symBbox(ldata->symId));
1861 : }
1862 : }
1863 :
1864 44 : void TLayout::layoutCapo(const Capo* item, Capo::LayoutData* ldata, const LayoutContext&)
1865 : {
1866 : LAYOUT_CALL_ITEM(item);
1867 : //! NOTE Looks like it doesn't belong here
1868 44 : if (item->shouldAutomaticallyGenerateText() || item->empty()) {
1869 44 : if (const Part* part = item->originPart()) {
1870 44 : if (const StringData* stringData = part->stringData(item->tick(), item->staff()->idx())) {
1871 44 : String text = item->generateText(stringData->strings());
1872 44 : const_cast<Capo*>(item)->setXmlText(text);
1873 44 : }
1874 : }
1875 : }
1876 :
1877 44 : TextLayout::layoutBaseTextBase(item, ldata);
1878 :
1879 44 : if (item->autoplace()) {
1880 44 : const Segment* s = item->segment();
1881 44 : const Measure* m = s->measure();
1882 44 : LD_CONDITION(ldata->isSetPos());
1883 44 : LD_CONDITION(m->ldata()->isSetPos());
1884 44 : LD_CONDITION(s->ldata()->isSetPos());
1885 : }
1886 :
1887 44 : Autoplace::autoplaceSegmentElement(item, ldata);
1888 44 : }
1889 :
1890 0 : void TLayout::layoutDeadSlapped(const DeadSlapped* item, DeadSlapped::LayoutData* ldata)
1891 : {
1892 : LAYOUT_CALL_ITEM(item);
1893 : LD_INDEPENDENT;
1894 :
1895 0 : if (ldata->isValid()) {
1896 0 : return;
1897 : }
1898 :
1899 0 : const double deadSlappedWidth = item->spatium() * 2;
1900 0 : RectF rect = RectF(0, 0, deadSlappedWidth, item->staff()->staffHeight());
1901 0 : ldata->setBbox(rect);
1902 0 : ldata->setPos(PointF());
1903 :
1904 : // fillPath
1905 : {
1906 0 : constexpr double crossThinknessPercentage = 0.1;
1907 0 : double height = rect.height();
1908 0 : double width = rect.width();
1909 0 : double crossThickness = width * crossThinknessPercentage;
1910 :
1911 0 : PointF topLeft = PointF(rect.x(), rect.y());
1912 0 : PointF bottomRight = topLeft + PointF(width, height);
1913 0 : PointF topRight = topLeft + PointF(width, 0);
1914 0 : PointF bottomLeft = topLeft + PointF(0, height);
1915 0 : PointF offsetX = PointF(crossThickness, 0);
1916 :
1917 0 : PainterPath path1;
1918 0 : path1.moveTo(topLeft);
1919 0 : path1.lineTo(topLeft + offsetX);
1920 0 : path1.lineTo(bottomRight);
1921 0 : path1.lineTo(bottomRight - offsetX);
1922 0 : path1.lineTo(topLeft);
1923 :
1924 0 : PainterPath path2;
1925 0 : path2.moveTo(topRight);
1926 0 : path2.lineTo(topRight - offsetX);
1927 0 : path2.lineTo(bottomLeft);
1928 0 : path2.lineTo(bottomLeft + offsetX);
1929 0 : path2.lineTo(topRight);
1930 :
1931 0 : ldata->path1 = path1;
1932 0 : ldata->path2 = path2;
1933 0 : }
1934 : }
1935 :
1936 1145 : void TLayout::layoutDynamic(Dynamic* item, Dynamic::LayoutData* ldata, const LayoutConfiguration& conf)
1937 : {
1938 1145 : DynamicsLayout::layoutDynamic(item, ldata, conf);
1939 1145 : }
1940 :
1941 34 : void TLayout::layoutExpression(const Expression* item, Expression::LayoutData* ldata)
1942 : {
1943 : LAYOUT_CALL_ITEM(item);
1944 34 : IF_ASSERT_FAILED(item->ownershipParent()) {
1945 0 : return;
1946 : }
1947 :
1948 34 : const_cast<Expression*>(item)->setPlacementBasedOnVoiceAssignment(item->style().styleV(
1949 : Sid::dynamicsHairpinVoiceBasedPlacement).value<DirectionV>());
1950 :
1951 34 : TextLayout::layoutBaseTextBase(item, ldata);
1952 :
1953 34 : const Segment* segment = item->segment();
1954 :
1955 34 : if (item->align().horizontal != AlignH::LEFT) {
1956 0 : Chord* chordToAlign = nullptr;
1957 : // Look for chord in this staff
1958 0 : track_idx_t startTrack = track2staff(item->staffIdx());
1959 0 : track_idx_t endTrack = startTrack + VOICES;
1960 0 : for (track_idx_t track = startTrack; track < endTrack; ++track) {
1961 0 : EngravingItem* e = segment->element(track);
1962 0 : if (e && e->isChord()) {
1963 0 : chordToAlign = toChord(e);
1964 0 : break;
1965 : }
1966 : }
1967 :
1968 0 : if (chordToAlign && !chordToAlign->notes().empty()) {
1969 0 : const Note* note = chordToAlign->notes().at(0);
1970 0 : double headWidth = note->headWidth();
1971 0 : bool center = item->align().horizontal == AlignH::HCENTER;
1972 0 : ldata->moveX(headWidth * (center ? 0.5 : 1));
1973 : }
1974 : }
1975 :
1976 34 : ldata->disconnectSnappedItems();
1977 :
1978 34 : if (!item->autoplace()) {
1979 0 : return;
1980 : }
1981 :
1982 34 : const Segment* s = item->segment();
1983 34 : const Measure* m = s->measure();
1984 34 : LD_CONDITION(ldata->isSetPos());
1985 34 : LD_CONDITION(m->ldata()->isSetPos());
1986 34 : LD_CONDITION(s->ldata()->isSetPos());
1987 :
1988 34 : Dynamic* dynamic = toDynamic(segment->findAnnotation(ElementType::DYNAMIC, item->track(), item->track()));
1989 34 : if (!dynamic) {
1990 32 : Segment* timeTickSeg = m->findSegmentR(SegmentType::TimeTick, s->rtick());
1991 32 : dynamic = timeTickSeg ? toDynamic(timeTickSeg->findAnnotation(ElementType::DYNAMIC, item->track(), item->track())) : nullptr;
1992 : }
1993 2 : if (!dynamic || dynamic->placeAbove() != item->placeAbove() || dynamic->voiceAssignment() != item->voiceAssignment()
1994 36 : || !dynamic->visible()) {
1995 32 : Autoplace::autoplaceSegmentElement(item, ldata);
1996 32 : return;
1997 : }
1998 :
1999 2 : LD_CONDITION(dynamic->ldata()->isSetBbox()); // dynamic->shape()
2000 2 : LD_CONDITION(dynamic->ldata()->isSetPos());
2001 :
2002 : // If there is a dynamic on same segment and track make space for it horizontally
2003 2 : double padding = item->computeDynamicExpressionDistance(dynamic);
2004 2 : double dynamicRight = dynamic->shape().translate(dynamic->pos()).right();
2005 2 : double expressionLeft = ldata->bbox().translated(item->pos()).left();
2006 2 : double difference = expressionLeft - dynamicRight - padding;
2007 2 : ldata->moveX(-difference);
2008 :
2009 2 : if (item->snapToDynamics()) {
2010 2 : ldata->connectItemSnappedBefore(dynamic);
2011 : }
2012 :
2013 2 : Autoplace::autoplaceSegmentElement(item, ldata);
2014 : }
2015 :
2016 618 : void TLayout::layoutFermata(const Fermata* item, Fermata::LayoutData* ldata)
2017 : {
2018 : LAYOUT_CALL_ITEM(item);
2019 618 : const StaffType* stType = item->staffType();
2020 618 : if (stType && stType->isHiddenElementOnTab(Sid::fermataShowTabCommon, Sid::fermataShowTabSimple)) {
2021 0 : ldata->setIsSkipDraw(true);
2022 0 : return;
2023 : }
2024 618 : ldata->setIsSkipDraw(false);
2025 618 : ldata->setPos(PointF());
2026 :
2027 618 : double x = 0.0;
2028 618 : double y = item->placeAbove() ? 0.0 : item->staff()->staffHeight(item->tick());
2029 618 : const Segment* s = item->segment();
2030 618 : const track_idx_t layoutParentTrack = s->isType(SegmentType::BarLineTypes) ? item->staffIdx() * VOICES : item->track();
2031 618 : const EngravingItem* e = s->element(layoutParentTrack);
2032 :
2033 618 : if (e) {
2034 618 : LD_CONDITION(e->ldata()->isSetBbox()); // e->shape()
2035 618 : LD_CONDITION(e->ldata()->isSetPos());
2036 :
2037 618 : if (e->isChord()) {
2038 481 : const Chord* chord = toChord(e);
2039 481 : x = chord->x() + ChordLayout::centerX(chord);
2040 137 : } else if (e->isRest()) {
2041 56 : const Rest* rest = toRest(e);
2042 56 : x = rest->x() + rest->centerX();
2043 : } else {
2044 81 : x = e->x() + e->shape().left() + e->width() * item->staff()->staffMag(Fraction(0, 1)) * .5;
2045 : }
2046 : }
2047 :
2048 618 : String name = String::fromAscii(SymNames::nameForSymId(item->symId()).ascii());
2049 618 : if (item->placeAbove()) {
2050 517 : if (name.endsWith(u"Below")) {
2051 : //! NOTE It is not clear whether SymId is layout data or given data.
2052 0 : const_cast<Fermata*>(item)->setSymId(SymNames::symIdByName(name.left(name.size() - 5) + u"Above"));
2053 : }
2054 : } else {
2055 101 : if (name.endsWith(u"Above")) {
2056 : //! NOTE It is not clear whether SymId is layout data or given data.
2057 19 : const_cast<Fermata*>(item)->setSymId(SymNames::symIdByName(name.left(name.size() - 5) + u"Below"));
2058 : }
2059 : }
2060 :
2061 618 : ldata->setShape(Shape(item->symBbox(item->symId()), item));
2062 618 : x -= 0.5 * ldata->bbox().width();
2063 :
2064 618 : ldata->setPos(x, y);
2065 :
2066 618 : PointF defaultPos = item->defaultPos();
2067 618 : ldata->move(defaultPos);
2068 :
2069 618 : if (item->autoplace()) {
2070 618 : const Segment* s2 = item->segment();
2071 618 : const Measure* m = s2->measure();
2072 618 : LD_CONDITION(ldata->isSetPos());
2073 618 : LD_CONDITION(m->ldata()->isSetPos());
2074 618 : LD_CONDITION(s2->ldata()->isSetPos());
2075 : }
2076 :
2077 618 : Autoplace::autoplaceSegmentElement(item, ldata);
2078 618 : }
2079 :
2080 : //---------------------------------------------------------
2081 : // FiguredBassItem layout
2082 : // creates the display text (set as element text) and computes
2083 : // the horiz. offset needed to align the right part as well as the vert. offset
2084 : //---------------------------------------------------------
2085 211 : void TLayout::layoutFiguredBassItem(const FiguredBassItem* item, FiguredBassItem::LayoutData* ldata, const LayoutContext& ctx)
2086 : {
2087 : LAYOUT_CALL_ITEM(item);
2088 : // construct font metrics
2089 211 : int fontIdx = 0;
2090 211 : Font f(FiguredBass::FBFonts().at(fontIdx).family, Font::Type::Tablature);
2091 :
2092 : // font size in pixels, scaled according to spatium()
2093 : // (use the same font selection as used in draw() below)
2094 211 : double m = ctx.conf().styleD(Sid::figuredBassFontSize) * item->spatium() / item->defaultSpatium();
2095 211 : f.setPointSizeF(m);
2096 211 : FontMetrics fm(f);
2097 :
2098 211 : String str;
2099 211 : double x = item->symWidth(SymId::noteheadBlack) * .5;
2100 211 : double x1 = 0.0;
2101 211 : double x2 = 0.0;
2102 :
2103 : // create display text
2104 211 : int font = 0;
2105 211 : int style = ctx.conf().styleI(Sid::figuredBassStyle);
2106 :
2107 211 : if (item->parenth1() != FiguredBassItem::Parenthesis::NONE) {
2108 0 : str.append(FiguredBass::FBFonts().at(font).displayParenthesis[int(item->parenth1())]);
2109 : }
2110 :
2111 : // prefix
2112 211 : if (item->prefix() != FiguredBassItem::Modifier::NONE) {
2113 : // if no digit, the string created so far 'hangs' to the left of the note
2114 18 : if (item->digit() == FBIDigitNone) {
2115 8 : x1 = fm.width(str);
2116 : }
2117 18 : str.append(FiguredBass::FBFonts().at(font).displayAccidental[int(item->prefix())]);
2118 : // if no digit, the string from here onward 'hangs' to the right of the note
2119 18 : if (item->digit() == FBIDigitNone) {
2120 8 : x2 = fm.width(str);
2121 : }
2122 : }
2123 :
2124 211 : if (item->parenth2() != FiguredBassItem::Parenthesis::NONE) {
2125 4 : str.append(FiguredBass::FBFonts().at(font).displayParenthesis[int(item->parenth2())]);
2126 : }
2127 :
2128 : // digit
2129 211 : if (item->digit() != FBIDigitNone) {
2130 : // if some digit, the string created so far 'hangs' to the left of the note
2131 183 : x1 = fm.width(str);
2132 : // if suffix is a combining shape, combine it with digit (multi-digit numbers cannot be combined)
2133 : // unless there is a parenthesis in between
2134 183 : if ((item->digit() < 10)
2135 183 : && (item->suffix() == FiguredBassItem::Modifier::CROSS
2136 178 : || item->suffix() == FiguredBassItem::Modifier::BACKSLASH
2137 176 : || item->suffix() == FiguredBassItem::Modifier::SLASH)
2138 366 : && item->parenth3() == FiguredBassItem::Parenthesis::NONE) {
2139 11 : str.append(FiguredBass::FBFonts().at(font).displayDigit[style][item->digit()][int(item->suffix())
2140 11 : - (int(FiguredBassItem::Modifier::CROSS)
2141 : - 1)]);
2142 : }
2143 : // if several digits or no shape combination, convert _digit to font styled chars
2144 : else {
2145 172 : String digits;
2146 172 : int digit = item->digit();
2147 : while (true) {
2148 172 : digits.prepend(FiguredBass::FBFonts().at(font).displayDigit[style][(digit % 10)][0]);
2149 172 : digit /= 10;
2150 172 : if (digit == 0) {
2151 172 : break;
2152 : }
2153 : }
2154 172 : str.append(digits);
2155 172 : }
2156 : // if some digit, the string from here onward 'hangs' to the right of the note
2157 183 : x2 = fm.width(str);
2158 : }
2159 :
2160 211 : if (item->parenth3() != FiguredBassItem::Parenthesis::NONE) {
2161 4 : str.append(FiguredBass::FBFonts().at(font).displayParenthesis[int(item->parenth3())]);
2162 : }
2163 :
2164 : // suffix
2165 : // append only if non-combining shape or cannot combine (no digit or parenthesis in between)
2166 211 : if (item->suffix() != FiguredBassItem::Modifier::NONE
2167 243 : && ((item->suffix() != FiguredBassItem::Modifier::CROSS
2168 27 : && item->suffix() != FiguredBassItem::Modifier::BACKSLASH
2169 25 : && item->suffix() != FiguredBassItem::Modifier::SLASH)
2170 11 : || item->digit() == FBIDigitNone
2171 11 : || item->parenth3() != FiguredBassItem::Parenthesis::NONE)) {
2172 21 : str.append(FiguredBass::FBFonts().at(font).displayAccidental[int(item->suffix())]);
2173 : }
2174 :
2175 211 : if (item->parenth4() != FiguredBassItem::Parenthesis::NONE) {
2176 0 : str.append(FiguredBass::FBFonts().at(font).displayParenthesis[int(item->parenth4())]);
2177 : }
2178 :
2179 : //! NOTE Look like, this text not layout data (not depends by position)
2180 211 : ldata->displayText = str; // this text will be displayed
2181 :
2182 211 : if (str.size()) { // if some text
2183 191 : x = x - (x1 + x2) * 0.5; // position the text so that [x1<-->x2] is centered below the note
2184 : } else { // if no text (but possibly a line)
2185 20 : x = 0; // start at note left margin
2186 : }
2187 : // vertical position
2188 211 : double h = fm.lineSpacing();
2189 211 : h *= ctx.conf().styleD(Sid::figuredBassLineHeight);
2190 211 : double y = 0.0;
2191 211 : if (ctx.conf().styleI(Sid::figuredBassAlignment) == 0) { // top alignment: stack down from first item
2192 211 : y = h * item->ord();
2193 : } else { // bottom alignment: stack up from last item
2194 0 : y = -h * (item->figuredBass()->itemsCount() - item->ord());
2195 : }
2196 211 : ldata->setPos(x, y);
2197 : // determine bbox from text width
2198 : // w = fm.width(str);
2199 211 : double w = fm.width(str);
2200 211 : ldata->textWidth = w;
2201 : // if there is a cont.line, extend width to cover the whole FB element duration line
2202 211 : int lineLen = 0;
2203 211 : if (item->contLine() != FiguredBassItem::ContLine::NONE
2204 211 : && (lineLen = item->figuredBass()->ldata()->lineLength(0)) > w) {
2205 32 : w = lineLen;
2206 : }
2207 211 : ldata->setBbox(0, 0, w, h);
2208 211 : }
2209 :
2210 : // lays out the duration indicator line(s), filling the _lineLengths array
2211 : // and the length of printed lines (used by continuation lines)
2212 :
2213 136 : static void layoutLines(const FiguredBass* item, FiguredBass::LayoutData* ldata, const LayoutContext& ctx)
2214 : {
2215 136 : std::vector<double> lineLengths = ldata->lineLengths;
2216 136 : if (item->ticks() <= Fraction(0, 1) || !item->segment()) {
2217 7 : lineLengths.resize(1); // be sure to always have
2218 7 : lineLengths[0] = 0; // at least 1 item in array
2219 7 : ldata->lineLengths = lineLengths;
2220 7 : return;
2221 : }
2222 :
2223 129 : ChordRest* lastCR = nullptr; // the last ChordRest of this
2224 129 : Segment* nextSegm = nullptr; // the Segment beyond this' segment
2225 129 : Fraction nextTick = item->segment()->tick() + item->ticks(); // the tick beyond this' duration
2226 :
2227 : // locate the measure containing the last tick of this; it is either:
2228 : // the same measure containing nextTick, if nextTick is not the first tick of a measure
2229 : // (and line should stop right before it)
2230 : // or the previous measure, if nextTick is the first tick of a measure
2231 : // (and line should stop before any measure terminal segment (bar, clef, ...) )
2232 :
2233 129 : const Measure* m = ctx.dom().tick2measure(nextTick - Fraction::eps());
2234 129 : if (m) {
2235 : // locate the first segment (of ANY type) right after this' last tick
2236 625 : for (nextSegm = m->first(SegmentType::All); nextSegm; nextSegm = nextSegm->next()) {
2237 625 : if (nextSegm->tick() >= nextTick) {
2238 129 : break;
2239 : }
2240 : }
2241 : // locate the last ChordRest of this
2242 129 : if (nextSegm) {
2243 129 : track_idx_t startTrack = trackZeroVoice(item->track());
2244 129 : track_idx_t endTrack = startTrack + VOICES;
2245 143 : for (const Segment* seg = nextSegm->prev1(); seg; seg = seg->prev1()) {
2246 199 : for (track_idx_t t = startTrack; t < endTrack; ++t) {
2247 185 : EngravingItem* el = seg->element(t);
2248 185 : if (el && el->isChordRest()) {
2249 129 : lastCR = toChordRest(el);
2250 129 : break;
2251 : }
2252 : }
2253 143 : if (lastCR) {
2254 129 : break;
2255 : }
2256 : }
2257 : }
2258 : }
2259 129 : if (!m || !nextSegm) {
2260 0 : LOGD("FiguredBass layout: no segment found for tick %d", nextTick.ticks());
2261 0 : lineLengths.resize(1); // be sure to always have
2262 0 : lineLengths[0] = 0; // at least 1 item in array
2263 0 : ldata->lineLengths = lineLengths;
2264 0 : return;
2265 : }
2266 :
2267 : // get length of printed lines from horiz. page position of lastCR
2268 : // (enter a bit 'into' the ChordRest for clarity)
2269 129 : double printedLineLength = lastCR ? lastCR->pageX() - item->pageX() + 1.5 * item->spatium() : 3 * item->spatium();
2270 129 : ldata->printedLineLength = printedLineLength;
2271 :
2272 : // get duration indicator line(s) from page position of nextSegm
2273 129 : const std::vector<System*>& systems = ctx.dom().systems();
2274 129 : System* s1 = item->segment()->measure()->system();
2275 129 : System* s2 = nextSegm->measure()->system();
2276 129 : system_idx_t sysIdx1 = muse::indexOf(systems, s1);
2277 129 : system_idx_t sysIdx2 = muse::indexOf(systems, s2);
2278 :
2279 129 : if (sysIdx2 == muse::nidx || sysIdx2 < sysIdx1) {
2280 0 : sysIdx2 = sysIdx1;
2281 0 : nextSegm = item->segment()->next1();
2282 : // TODO
2283 : // During layout of figured bass next systems' numbers may be still
2284 : // undefined (then sysIdx2 == muse::nidx) or change in the future.
2285 : // A layoutSystem() approach similar to that for spanners should
2286 : // probably be implemented.
2287 : }
2288 :
2289 : system_idx_t i;
2290 : int len;
2291 129 : size_t segIdx = 0;
2292 258 : for (i = sysIdx1, segIdx = 0; i <= sysIdx2; ++i, ++segIdx) {
2293 129 : len = 0;
2294 129 : if (sysIdx1 == sysIdx2 || i == sysIdx1) {
2295 : // single line
2296 129 : len = nextSegm->pageX() - item->pageX() - 4; // stop 4 raster units before next segm
2297 0 : } else if (i == sysIdx1) {
2298 : // initial line
2299 0 : double w = s1->staff(item->staffIdx())->bbox().right();
2300 0 : double x = s1->pageX() + w;
2301 0 : len = x - item->pageX();
2302 0 : } else if (i > 0 && i != sysIdx2) {
2303 : // middle line
2304 0 : LOGD("FiguredBass: duration indicator middle line not implemented");
2305 0 : } else if (i == sysIdx2) {
2306 : // end line
2307 0 : LOGD("FiguredBass: duration indicator end line not implemented");
2308 : }
2309 : // store length item, reusing array items if already present
2310 129 : if (lineLengths.size() <= segIdx) {
2311 74 : lineLengths.push_back(len);
2312 : } else {
2313 55 : lineLengths[segIdx] = len;
2314 : }
2315 : }
2316 : // if more array items than needed, truncate array
2317 129 : if (lineLengths.size() > segIdx) {
2318 0 : lineLengths.resize(segIdx);
2319 : }
2320 :
2321 129 : ldata->lineLengths = lineLengths;
2322 136 : }
2323 :
2324 156 : void TLayout::layoutFiguredBass(const FiguredBass* item, FiguredBass::LayoutData* ldata, const LayoutContext& ctx)
2325 : {
2326 : LAYOUT_CALL_ITEM(item);
2327 : // VERTICAL POSITION:
2328 156 : const double y = ctx.conf().styleD(Sid::figuredBassYOffset) * item->spatium();
2329 156 : ldata->setPos(PointF(0.0, y));
2330 :
2331 : // BOUNDING BOX and individual item layout (if required)
2332 156 : TextLayout::layoutBaseTextBase1(item, ldata); // prepare structs and data expected by Text methods
2333 : // if element could be parsed into items, layout each element
2334 : // Items list will be empty in edit mode (see FiguredBass::startEdit).
2335 : // TODO: consider disabling specific layout in case text style is changed (tid() != TextStyleName::FIGURED_BASS).
2336 156 : if (item->items().size() > 0) {
2337 136 : Shape shape;
2338 136 : layoutLines(item, ldata, ctx);
2339 : // layout each item and enlarge bbox to include items bboxes
2340 347 : for (FiguredBassItem* fit : item->items()) {
2341 211 : FiguredBassItem::LayoutData* fildata = fit->mutldata();
2342 211 : layoutFiguredBassItem(fit, fildata, ctx);
2343 211 : shape.add(fildata->bbox().translated(fit->pos()), fit);
2344 : }
2345 136 : ldata->setShape(shape);
2346 136 : }
2347 156 : }
2348 :
2349 408 : void TLayout::layoutFingering(const Fingering* item, Fingering::LayoutData* ldata)
2350 : {
2351 : LAYOUT_CALL_ITEM(item);
2352 408 : IF_ASSERT_FAILED(item->ownershipParent()) {
2353 10 : return;
2354 : }
2355 :
2356 408 : Fraction tick = item->ownershipParentItem()->tick();
2357 408 : const Staff* st = item->staff();
2358 408 : if (st && st->isTabStaff(tick)
2359 816 : && (!st->staffType(tick)->showTabFingering() || item->textStyleType() == TextStyleType::STRING_NUMBER)) {
2360 10 : ldata->setIsSkipDraw(true);
2361 10 : return;
2362 : }
2363 398 : ldata->setIsSkipDraw(false);
2364 :
2365 398 : TextLayout::layoutBaseTextBase(item, ldata);
2366 398 : ldata->setPosY(0.0); // handle placement below
2367 :
2368 398 : if (item->autoplace()) {
2369 398 : const Note* note = item->note();
2370 398 : const Chord* chord = note->chord();
2371 :
2372 398 : bool voices = chord->measure()->hasVoices(chord->staffIdx(), chord->tick(), chord->actualTicks());
2373 398 : bool tight = voices && chord->notes().size() == 1 && !chord->beam() && item->textStyleType() != TextStyleType::STRING_NUMBER;
2374 :
2375 398 : double headWidth = note->bboxRightPos();
2376 :
2377 : // temporarily exclude self from chord shape
2378 398 : const_cast<Fingering*>(item)->setAutoplace(false);
2379 :
2380 398 : if (item->layoutType() == ElementType::CHORD) {
2381 390 : bool above = item->placeAbove();
2382 390 : const Stem* stem = chord->stem();
2383 390 : const Segment* segment = chord->segment();
2384 390 : const Measure* measure = segment->measure();
2385 390 : double sp = item->spatium();
2386 390 : double md = item->minDistance().val() * sp;
2387 390 : const SysStaff* ss = measure->system()->staff(chord->vStaffIdx());
2388 390 : const Staff* vStaff = chord->staff(); // TODO: use current height at tick
2389 :
2390 390 : LD_CONDITION(measure->ldata()->isSetPos());
2391 390 : LD_CONDITION(segment->ldata()->isSetPos());
2392 390 : LD_CONDITION(chord->ldata()->isSetPos());
2393 390 : LD_CONDITION(note->ldata()->isSetPos());
2394 390 : LD_CONDITION(note->ldata()->isSetBbox());
2395 : //! NOTE A lot of spam
2396 : // LD_CONDITION(note->ldata()->mirror.has_value());
2397 390 : if (stem) {
2398 390 : LD_CONDITION(stem->ldata()->isSetPos());
2399 390 : LD_CONDITION(stem->ldata()->isSetBbox());
2400 : }
2401 390 : LD_CONDITION(chord->upNote()->ldata()->isSetBbox());
2402 390 : LD_CONDITION(chord->upNote()->ldata()->isSetPos());
2403 390 : LD_CONDITION(chord->downNote()->ldata()->isSetBbox());
2404 390 : LD_CONDITION(chord->downNote()->ldata()->isSetPos());
2405 :
2406 390 : if (note->ldata()->mirror.value(LD_ACCESS::BAD)) {
2407 0 : ldata->moveX(-note->ldata()->pos().x());
2408 : }
2409 390 : ldata->moveX(headWidth * .5);
2410 390 : if (above) {
2411 295 : if (tight) {
2412 20 : if (chord->stem()) {
2413 20 : ldata->moveX(-0.8 * sp);
2414 : }
2415 20 : ldata->moveY(-1.5 * sp);
2416 : } else {
2417 275 : RectF r = ldata->bbox().translated(measure->pos() + segment->pos() + chord->pos() + note->pos() + item->pos());
2418 275 : SkylineLine sk(false);
2419 275 : sk.add(r, const_cast<Fingering*>(item));
2420 275 : double d = sk.minDistance(ss->skyline().north());
2421 275 : double yd = 0.0;
2422 275 : if (d > 0.0 && item->isStyled(Pid::MIN_DISTANCE)) {
2423 259 : yd -= d + item->ldata()->bbox().height() * .25;
2424 : }
2425 : // force extra space above staff & chord (but not other fingerings)
2426 275 : double top = 0.0;
2427 275 : if (chord->up() && chord->beam() && stem) {
2428 53 : top = stem->y() + stem->ldata()->bbox().top();
2429 : } else {
2430 222 : const Note* un = chord->upNote();
2431 222 : top = std::min(0.0, un->y() + un->ldata()->bbox().top());
2432 : }
2433 275 : top -= md;
2434 275 : double diff = (ldata->bbox().bottom() + ldata->pos().y() + yd + note->y()) - top;
2435 275 : if (diff > 0.0) {
2436 202 : yd -= diff;
2437 : }
2438 275 : ldata->moveY(yd);
2439 275 : }
2440 : } else {
2441 95 : if (tight) {
2442 10 : if (chord->stem()) {
2443 10 : ldata->moveX(0.8 * sp);
2444 : }
2445 10 : ldata->moveY(1.5 * sp);
2446 : } else {
2447 85 : RectF r = ldata->bbox().translated(measure->pos() + segment->pos() + chord->pos() + note->pos() + item->pos());
2448 85 : SkylineLine sk(true);
2449 85 : sk.add(r, const_cast<Fingering*>(item));
2450 85 : double d = ss->skyline().south().minDistance(sk);
2451 85 : double yd = 0.0;
2452 85 : if (d > 0.0 && item->isStyled(Pid::MIN_DISTANCE)) {
2453 85 : yd += d + item->ldata()->bbox().height() * .25;
2454 : }
2455 : // force extra space below staff & chord (but not other fingerings)
2456 : double bottom;
2457 85 : if (!chord->up() && chord->beam() && stem) {
2458 10 : bottom = stem->y() + stem->ldata()->bbox().bottom();
2459 : } else {
2460 75 : const Note* dn = chord->downNote();
2461 75 : bottom = std::max(vStaff->staffHeight(item->tick()), dn->y() + dn->ldata()->bbox().bottom());
2462 : }
2463 85 : bottom += md;
2464 85 : double diff = bottom - (ldata->bbox().top() + ldata->pos().y() + yd + note->y());
2465 85 : if (diff > 0.0) {
2466 83 : yd += diff;
2467 : }
2468 85 : ldata->moveY(yd);
2469 85 : }
2470 : }
2471 8 : } else if (item->textStyleType() == TextStyleType::LH_GUITAR_FINGERING) {
2472 : // place to left of note
2473 8 : double left = note->shape().left();
2474 8 : if (left + note->x() < 0.0) {
2475 2 : ldata->moveX(left);
2476 : } else {
2477 6 : ldata->moveX(-note->x());
2478 : }
2479 : }
2480 : // for other fingering styles, do not autoplace
2481 :
2482 : // restore autoplace
2483 398 : const_cast<Fingering*>(item)->setAutoplace(true);
2484 : }
2485 : }
2486 :
2487 701 : void TLayout::layoutFretDiagram(const FretDiagram* item, FretDiagram::LayoutData* ldata, const LayoutContext& ctx)
2488 : {
2489 : LAYOUT_CALL_ITEM(item);
2490 701 : double spatium = item->spatium() * item->userMag();
2491 701 : ldata->stringLineWidth = spatium * 0.08;
2492 1358 : ldata->nutLineWidth = ((item->fretOffset() || !item->showNut()) ? ldata->stringLineWidth
2493 657 : : ctx.conf().styleAbsolute(Sid::fretNutThickness) * item->userMag());
2494 701 : ldata->nutY = -0.5 * (ldata->nutLineWidth - ldata->stringLineWidth);
2495 701 : ldata->stringDist = ctx.conf().styleAbsolute(Sid::fretStringSpacing) * item->userMag();
2496 701 : ldata->fretDist = ctx.conf().styleAbsolute(Sid::fretFretSpacing) * item->userMag();
2497 701 : ldata->markerSize = ldata->stringDist * .8;
2498 701 : ldata->markerY = ldata->nutY - 0.5 * ldata->nutLineWidth - ldata->markerSize - 0.20 * spatium;
2499 701 : bool extendedStyle = item->style().styleB(Sid::fretStyleExtended);
2500 701 : ldata->stringExtendTop = item->fretOffset() && extendedStyle ? -spatium * .2 : 0.0;
2501 701 : ldata->stringExtendBottom = extendedStyle ? 0.5 * ldata->fretDist : 0.0;
2502 701 : ldata->dotDiameter = ctx.conf().styleAbsolute(Sid::fretDotSpatiumSize) * item->userMag();
2503 701 : ldata->gridHeight = 0.0;
2504 :
2505 701 : double shapeMarginAboveDiagram = ldata->fretDist * 1.5;
2506 701 : double w = ldata->stringDist * (item->strings() - 1) + ldata->markerSize;
2507 701 : double h = item->frets() * ldata->fretDist + ldata->stringExtendBottom + 0.5 * ldata->stringLineWidth + shapeMarginAboveDiagram;
2508 701 : double y = -shapeMarginAboveDiagram;
2509 701 : double x = -(ldata->markerSize * .5);
2510 :
2511 701 : bool isVertical = item->orientation() == Orientation::VERTICAL;
2512 :
2513 : // Allocate space for fret offset number
2514 701 : if (item->fretOffset() > 0) {
2515 44 : double padding = 0.4 * ldata->stringDist;
2516 48 : for (auto i : item->barres()) {
2517 36 : FretItem::Barre barre = i.second;
2518 36 : if (!barre.exists()) {
2519 0 : continue;
2520 : }
2521 32 : if ((barre.startString == 0 && item->numPos() == 0)
2522 68 : || ((barre.endString == -1 || barre.endString == item->strings() - 1) && item->numPos() == 1)) {
2523 32 : padding += 0.20 * ldata->dotDiameter * ctx.conf().styleD(Sid::barreLineWidth);
2524 32 : break;
2525 : }
2526 : }
2527 44 : Font scaledFont(item->fretNumFont());
2528 44 : FontMetrics fm2(scaledFont);
2529 44 : String fretText = String::number(item->fretOffset() + 1);
2530 44 : if (ctx.conf().styleB(Sid::fretUseCustomSuffix)) {
2531 0 : fretText += ctx.conf().styleSt(Sid::fretCustomSuffix);
2532 : }
2533 44 : ldata->fretText = fretText;
2534 44 : double numw = isVertical ? fm2.width(fretText) : fm2.tightBoundingRect(fretText).height();
2535 44 : double xdiff = numw + padding;
2536 44 : w += xdiff;
2537 44 : x += (item->numPos() == 0) == isVertical ? -xdiff : 0;
2538 44 : ldata->fretNumPadding = padding;
2539 44 : }
2540 :
2541 701 : if (!isVertical) {
2542 0 : double tempW = w,
2543 0 : tempX = x;
2544 0 : w = h;
2545 0 : h = tempW;
2546 0 : x = y;
2547 0 : y = tempX;
2548 : }
2549 :
2550 : // When changing how bbox is calculated, don't forget to update the centerX and rightX methods too.
2551 701 : Shape shape;
2552 701 : shape.add(RectF(x, y, w, h), item);
2553 :
2554 701 : ldata->gridHeight = h;
2555 :
2556 701 : ldata->fingeringItems.clear();
2557 701 : if (item->showFingering()) {
2558 0 : const double padding = 0.2 * spatium;
2559 0 : Font fingeringFont = item->fingeringFont();
2560 0 : FontMetrics fontMetrics(fingeringFont);
2561 0 : double fontHeight = fontMetrics.capHeight();
2562 0 : for (size_t i = 0; i < item->fingering().size(); ++i) {
2563 0 : int finger = item->fingering()[i];
2564 0 : if (finger == 0) {
2565 0 : continue;
2566 : }
2567 0 : String fingerS = String::number(finger);
2568 0 : double width = fontMetrics.width(fingerS);
2569 0 : double digitHeight = fontMetrics.tightBoundingRect(fingerS).height();
2570 0 : double xOff = -0.5 * width;
2571 0 : double fingerX = isVertical ? (item->strings() - i - 1) * ldata->stringDist + xOff : x + w + padding;
2572 0 : double fingerY = isVertical ? y + h + fontHeight + padding : i * ldata->stringDist + 0.5 * digitHeight;
2573 0 : ldata->fingeringItems.push_back(FretDiagram::FingeringItem(fingerS, PointF(fingerX, fingerY)));
2574 0 : shape.add(ShapeElement(fontMetrics.tightBoundingRect(fingerS).translated(PointF(fingerX, fingerY)), item));
2575 0 : }
2576 0 : }
2577 :
2578 701 : ldata->setShape(shape);
2579 :
2580 701 : if (item->ownershipParent()->isSegment()) {
2581 : // We need to get the width of the notehead/rest in order to position the fret diagram correctly
2582 631 : Segment* pSeg = item->segment();
2583 631 : double noteheadWidth = 0.0;
2584 631 : if (pSeg->isChordRestType()) {
2585 631 : staff_idx_t idx = item->staff()->idx();
2586 631 : for (EngravingItem* e = pSeg->firstElementOfSegment(idx); e; e = pSeg->nextElementOfSegment(e, idx)) {
2587 497 : if (e->isRest()) {
2588 284 : const Rest* r = toRest(e);
2589 284 : LD_CONDITION(r->ldata()->sym.has_value());
2590 284 : noteheadWidth = item->symWidth(r->ldata()->sym());
2591 284 : break;
2592 213 : } else if (e->isNote()) {
2593 213 : Note* n = toNote(e);
2594 213 : noteheadWidth = n->headWidth();
2595 213 : break;
2596 : }
2597 : }
2598 : }
2599 631 : if (RealIsNull(noteheadWidth)) {
2600 : // If note or rest not found, use standard notehead width
2601 134 : noteheadWidth = item->symWidth(SymId::noteheadBlack);
2602 : }
2603 :
2604 631 : ldata->setPos((noteheadWidth - item->mainWidth()) / 2, -(ldata->shape().bottom() + item->styleP(Sid::fretY)));
2605 : }
2606 :
2607 701 : Harmony* harmony = item->harmony();
2608 701 : if (harmony) {
2609 530 : TLayout::layoutHarmony(harmony, harmony->mutldata(), ctx);
2610 530 : if (item->visible() && harmony->autoplace()) {
2611 530 : double vertDist = ldata->bbox().top() - harmony->ldata()->bbox().translated(harmony->pos()).bottom();
2612 530 : double diff = vertDist - harmony->minDistance().val() * item->spatium();
2613 530 : if (diff < 0) {
2614 509 : harmony->mutldata()->moveY(diff);
2615 : }
2616 : }
2617 : }
2618 :
2619 701 : ldata->slurPaths.clear();
2620 906 : for (auto i : item->barres()) {
2621 205 : FretItem::Barre barre = i.second;
2622 205 : if (!barre.exists()) {
2623 0 : continue;
2624 : }
2625 205 : int startString = barre.startString;
2626 205 : int endString = barre.endString != -1 ? barre.endString : item->strings() - 1;
2627 205 : int fret = i.first;
2628 205 : bool slurStyleBarre = ctx.conf().styleB(Sid::barreAppearanceSlur);
2629 1344 : for (int string = startString; string <= endString; ++string) {
2630 1139 : if (slurStyleBarre) {
2631 0 : const_cast<FretDiagram*>(item)->addDotForDotStyleBarre(string, fret);
2632 : } else {
2633 1139 : const_cast<FretDiagram*>(item)->removeDotForDotStyleBarre(string, fret);
2634 : }
2635 : }
2636 205 : int length = endString - startString;
2637 205 : double insetX = (length < 2 ? 1.25 : 2) * ldata->stringLineWidth;
2638 205 : double insetY = fret == 1 ? ldata->nutLineWidth + ldata->stringLineWidth : length < 2 ? 2 * ldata->stringLineWidth : insetX;
2639 205 : double startX = startString * ldata->stringDist + insetX;
2640 205 : double endX = (endString) * ldata->stringDist - insetX;
2641 205 : double shoulderXoffset = 0.2 * (endX - startX);
2642 205 : double startEndY = (fret - 1) * ldata->fretDist - insetY;
2643 205 : double shoulderY = startEndY - (length < 2 ? 0.3 : length < 3 ? 0.4 : 0.5) * ldata->fretDist;
2644 205 : double slurThickness = (length < 2 ? 0.08 : 0.1) * item->spatium() * item->userMag();
2645 205 : double shoulderYfor = shoulderY - slurThickness;
2646 205 : double shoulderYback = shoulderY + slurThickness;
2647 205 : PointF bezier1for = PointF(startX + shoulderXoffset, shoulderYfor);
2648 205 : PointF bezier2for = PointF(endX - shoulderXoffset, shoulderYfor);
2649 205 : PointF bezier1back = PointF(startX + shoulderXoffset, shoulderYback);
2650 205 : PointF bezier2back = PointF(endX - shoulderXoffset, shoulderYback);
2651 205 : PainterPath slurPath = PainterPath();
2652 205 : slurPath.moveTo(startX, startEndY);
2653 205 : slurPath.cubicTo(bezier1for, bezier2for, PointF(endX, startEndY));
2654 205 : slurPath.cubicTo(bezier2back, bezier1back, PointF(startX, startEndY));
2655 205 : ldata->slurPaths.push_back(slurPath);
2656 205 : }
2657 701 : }
2658 :
2659 454 : void TLayout::layoutGlissando(Glissando* item, LayoutContext& ctx)
2660 : {
2661 : LAYOUT_CALL_ITEM(item);
2662 :
2663 454 : if (item->startElement()) {
2664 454 : item->setTick(item->startElement()->tick());
2665 : }
2666 :
2667 454 : if (item->endElement()) {
2668 454 : item->setTick2(item->endElement()->tick());
2669 : }
2670 :
2671 454 : TLayout::layoutLine(const_cast<Glissando*>(item), ctx);
2672 :
2673 454 : if (item->spannerSegments().empty()) {
2674 0 : LOGD("no segments");
2675 0 : return;
2676 : }
2677 454 : Glissando::LayoutData* ldata = item->mutldata();
2678 454 : ldata->setPos(0.0, 0.0);
2679 :
2680 454 : String instrId = item->staff()->part()->instrumentId(item->tick());
2681 454 : bool harpStaff = instrId == "harp";
2682 454 : if (!item->isHarpGliss().has_value()) {
2683 68 : item->setIsHarpGliss(harpStaff);
2684 : } else {
2685 386 : if (harpStaff != item->isHarpGliss().value()) {
2686 : // Preserve whether this gliss has its default playback style
2687 0 : bool defaultStyle = false;
2688 0 : if (item->isStyled(Pid::GLISS_STYLE)) {
2689 0 : defaultStyle = true;
2690 : }
2691 0 : item->setIsHarpGliss(harpStaff);
2692 0 : if (defaultStyle) {
2693 0 : item->resetProperty(Pid::GLISS_STYLE);
2694 : }
2695 :
2696 : // Make sure harp glisses can only be diatonic and chromatic
2697 0 : GlissandoStyle glissStyle = item->glissandoStyle();
2698 0 : if (item->isHarpGliss().value()
2699 0 : && (glissStyle != GlissandoStyle::DIATONIC
2700 0 : && glissStyle != GlissandoStyle::CHROMATIC)) {
2701 0 : item->setGlissandoStyle(GlissandoStyle::DIATONIC);
2702 : }
2703 : }
2704 : }
2705 :
2706 454 : layoutNoteAnchoredLine(item, ldata, ctx);
2707 :
2708 454 : ChordLayout::addLineAttachPoints(item);
2709 454 : }
2710 :
2711 910 : void TLayout::layoutGlissandoSegment(GlissandoSegment* item, LayoutContext&)
2712 : {
2713 : LAYOUT_CALL_ITEM(item);
2714 :
2715 910 : GlissandoSegment::LayoutData* ldata = item->mutldata();
2716 910 : if (item->pos2().x() <= 0) {
2717 287 : ldata->setBbox(RectF());
2718 287 : return;
2719 : }
2720 :
2721 623 : if (item->staff()) {
2722 623 : ldata->setMag(item->staff()->staffMag(item->tick()));
2723 : }
2724 623 : RectF r = RectF(0.0, 0.0, item->pos2().x(), item->pos2().y()).normalized();
2725 623 : double lw = item->absoluteFromSpatium(item->lineWidth()) * .5;
2726 623 : item->setbbox(r.adjusted(-lw, -lw, lw, lw));
2727 : }
2728 :
2729 1028 : void TLayout::layoutGraceNotesGroup(GraceNotesGroup* item, LayoutContext& ctx)
2730 : {
2731 1028 : const StaffType* staffType = item->parent()->staffType();
2732 1028 : if (staffType->isTabStaff() && !item->empty()) {
2733 45 : const Staff* staff = item->parent()->staff();
2734 45 : if (staff->links()) {
2735 33 : for (EngravingObject* linkedSt : *staff->links()) {
2736 33 : Staff* linkedStaff = toStaff(linkedSt);
2737 66 : if (linkedStaff->score() == staff->score() && !linkedStaff->staffType()->isTabStaff()
2738 66 : && linkedStaff->idx() < staff->idx()) {
2739 : // HACK: align grace notes in TAB to their linked notation staff.
2740 : // CAUTION: Works only if notation staff is above the TAB.
2741 : // TODO: refactor grace notes to allow doing this properly.
2742 33 : alignTabGraceNotesToMainStave(item, linkedStaff);
2743 33 : return;
2744 : }
2745 : }
2746 : }
2747 : }
2748 :
2749 : LAYOUT_CALL_ITEM(item);
2750 995 : Shape _shape;
2751 2322 : for (size_t i = item->size() - 1; i != muse::nidx; --i) {
2752 1327 : Chord* grace = item->at(i);
2753 1327 : Shape graceShape = grace->shape();
2754 1327 : Shape groupShape = _shape;
2755 1327 : groupShape.remove_if([grace](ShapeElement& s) {
2756 1695 : if (!s.item() || (s.item()->isStem() && s.item()->vStaffIdx() != grace->vStaffIdx())) {
2757 0 : return true;
2758 : }
2759 1695 : return false;
2760 : });
2761 : double offset;
2762 1327 : offset = -std::max(HorizontalSpacing::minHorizontalDistance(graceShape, groupShape, grace->spatium()), 0.0);
2763 : // Adjust spacing for cross-beam situations
2764 1327 : if (i < item->size() - 1 && grace->stem()) {
2765 268 : Chord* prevGrace = item->at(i + 1);
2766 268 : if (prevGrace->up() != grace->up()) {
2767 0 : double crossCorrection = grace->notes().front()->headWidth() - grace->stem()->width();
2768 0 : if (prevGrace->up() && !grace->up()) {
2769 0 : offset += crossCorrection;
2770 : } else {
2771 0 : offset -= crossCorrection;
2772 : }
2773 : }
2774 : }
2775 1327 : _shape.add(graceShape.translate(PointF(offset, 0.0)));
2776 1327 : double xpos = offset - item->parent()->rxoffset() - item->parent()->ldata()->pos().x();
2777 1327 : grace->setPos(xpos, 0.0);
2778 1327 : }
2779 :
2780 995 : const Segment* appendedSeg = item->appendedSegment();
2781 995 : Chord* parentChord = toChord(item->parent());
2782 995 : Shape staffShape = appendedSeg->staffShape(parentChord->staffIdx());
2783 995 : bool isTabStaff = parentChord->staffType() && parentChord->staffType()->isTabStaff();
2784 995 : if (isTabStaff) {
2785 12 : staffShape.remove_if([](ShapeElement& el) {
2786 : // Ignore stems on tab staves
2787 18 : return el.item() && el.item()->isStem();
2788 : });
2789 : }
2790 995 : double _shapeSpatium = HorizontalSpacing::shapeSpatium(_shape);
2791 995 : double xPos = -HorizontalSpacing::minHorizontalDistance(_shape, staffShape, _shapeSpatium);
2792 :
2793 : // If the parent chord is cross-staff, also check against shape in the other staff and take the minimum
2794 995 : if (item->parent()->staffMove() != 0) {
2795 0 : double xPosCross = -HorizontalSpacing::minHorizontalDistance(_shape,
2796 0 : appendedSeg->staffShape(item->parent()->vStaffIdx()),
2797 0 : _shapeSpatium);
2798 0 : xPos = std::min(xPos, xPosCross);
2799 : }
2800 : // Same if the grace note itself is cross-staff
2801 995 : Chord* firstGN = item->back();
2802 995 : if (firstGN->staffMove() != 0) {
2803 0 : double xPosCross = -HorizontalSpacing::minHorizontalDistance(_shape,
2804 : appendedSeg->staffShape(firstGN->vStaffIdx()),
2805 0 : _shapeSpatium);
2806 0 : xPos = std::min(xPos, xPosCross);
2807 : }
2808 : // Safety net in case the shape checks don't succeed
2809 995 : xPos = std::min(xPos, -double(ctx.conf().styleAbsolute(Sid::graceToMainNoteDist) + firstGN->notes().front()->headWidth() / 2));
2810 :
2811 995 : item->setPos(xPos, 0.0);
2812 :
2813 995 : if (isTabStaff) {
2814 12 : ChordLayout::layoutStem(parentChord, ctx);
2815 : }
2816 995 : }
2817 :
2818 96438 : void TLayout::layoutGraceNotesGroup2(const GraceNotesGroup* item, GraceNotesGroup::LayoutData* ldata)
2819 : {
2820 : LAYOUT_CALL_ITEM(item);
2821 : //! NOTE A lot of spam
2822 : // LD_CONDITION(ldata->isSetPos());
2823 :
2824 96438 : Shape shape(Shape::Type::Composite);
2825 97343 : for (const Chord* grace : *item) {
2826 905 : LD_CONDITION(grace->ldata()->isSetShape());
2827 905 : LD_CONDITION(grace->ldata()->isSetPos());
2828 :
2829 905 : shape.add(grace->shape(LD_ACCESS::PASS).translate(grace->pos() - item->pos()));
2830 : }
2831 96438 : ldata->setShape(shape);
2832 96438 : }
2833 :
2834 33 : void TLayout::alignTabGraceNotesToMainStave(GraceNotesGroup* graceNotes, const Staff* notationStaff)
2835 : {
2836 : // HACK: align grace notes in TAB to their linked notation staff.
2837 : // CAUTION: Works only if notation staff is above the TAB.
2838 : // TODO: refactor grace notes to allow doing this properly.
2839 :
2840 70 : for (Chord* grace : *graceNotes) {
2841 37 : Chord* linkedGrace = toChord(grace->findLinkedInStaff(notationStaff));
2842 37 : if (!linkedGrace) {
2843 0 : continue;
2844 : }
2845 37 : PointF linkedGracePos = linkedGrace->ldata()->pos();
2846 37 : grace->setPos(linkedGracePos);
2847 : }
2848 33 : }
2849 :
2850 22 : void TLayout::manageTempoChangeSnapping(GradualTempoChangeSegment* item, LayoutContext& ctx)
2851 : {
2852 22 : GradualTempoChangeSegment::LayoutData* ldata = item->mutldata();
2853 :
2854 22 : ldata->disconnectSnappedItems();
2855 22 : GradualTempoChangeSegment* tempoChangeSegmentSnappedBefore = item->findElementToSnapBefore();
2856 22 : if (tempoChangeSegmentSnappedBefore) {
2857 0 : ldata->connectItemSnappedBefore(tempoChangeSegmentSnappedBefore);
2858 0 : doLayoutGradualTempoChangeSegment(tempoChangeSegmentSnappedBefore, ctx);
2859 : }
2860 :
2861 22 : TempoText* tempoTextSnappedAfter = item->findElementToSnapAfter();
2862 22 : if (tempoTextSnappedAfter) {
2863 4 : ldata->connectItemSnappedAfter(tempoTextSnappedAfter);
2864 : }
2865 22 : }
2866 :
2867 22 : void TLayout::doLayoutGradualTempoChangeSegment(GradualTempoChangeSegment* item, LayoutContext& ctx)
2868 : {
2869 22 : GradualTempoChangeSegment::LayoutData* ldata = item->mutldata();
2870 22 : ldata->setPosY(0.0);
2871 :
2872 4 : auto extendLineToSnappedItemAfter = [item](EngravingItem* itemAfter) {
2873 4 : assert(itemAfter->isGradualTempoChangeSegment() || itemAfter->isTempoText());
2874 4 : if (item->tempoChange()->adjustForRehearsalMark(false)
2875 4 : || itemAfter->findAncestor(ElementType::SYSTEM) != item->system()) {
2876 0 : return;
2877 : }
2878 :
2879 4 : double xItemPos = itemAfter->pageX() - item->system()->pageX();
2880 4 : double itemLeftEdge = xItemPos + itemAfter->ldata()->bbox().left();
2881 :
2882 4 : double padding = item->spatium();
2883 4 : if (itemAfter->isTempoText()) {
2884 4 : const double fontSizeScaleFactor = toTempoText(itemAfter)->size() / 10.0;
2885 4 : padding = item->spatium() * fontSizeScaleFactor;
2886 0 : } else if (itemAfter->isGradualTempoChangeSegment()) {
2887 0 : Text* startText = toGradualTempoChangeSegment(itemAfter)->text();
2888 0 : if (startText) {
2889 0 : const double fontSizeScaleFactor = startText->size() / 10.0;
2890 0 : padding = item->spatium() * fontSizeScaleFactor;
2891 : }
2892 : }
2893 :
2894 4 : double maxTempoLineEnd = itemLeftEdge - padding;
2895 4 : double xEndDiff = maxTempoLineEnd - (item->pos().x() + item->pos2().x());
2896 4 : item->rxpos2() += xEndDiff;
2897 22 : };
2898 :
2899 22 : if (ldata->itemSnappedAfter() && ldata->itemSnappedAfter()->isTempoText()) {
2900 4 : TempoText* tempoTextSnappedAfter = toTempoText(ldata->itemSnappedAfter());
2901 4 : extendLineToSnappedItemAfter(tempoTextSnappedAfter);
2902 18 : } else if (ldata->itemSnappedAfter() && ldata->itemSnappedAfter()->isGradualTempoChangeSegment()) {
2903 0 : GradualTempoChangeSegment* tempoChangeSegmentSnappedAfter = toGradualTempoChangeSegment(ldata->itemSnappedAfter());
2904 0 : extendLineToSnappedItemAfter(tempoChangeSegmentSnappedAfter);
2905 : }
2906 :
2907 22 : layoutTextLineBaseSegment(item, ctx);
2908 :
2909 22 : Autoplace::autoplaceSpannerSegment(item, ldata);
2910 22 : }
2911 :
2912 22 : void TLayout::layoutGradualTempoChangeSegment(GradualTempoChangeSegment* item, LayoutContext& ctx)
2913 : {
2914 : LAYOUT_CALL_ITEM(item);
2915 :
2916 22 : manageTempoChangeSnapping(item, ctx);
2917 :
2918 22 : doLayoutGradualTempoChangeSegment(item, ctx);
2919 22 : }
2920 :
2921 0 : void TLayout::layoutGradualTempoChange(GradualTempoChange* item, LayoutContext& ctx)
2922 : {
2923 : LAYOUT_CALL_ITEM(item);
2924 0 : layoutLine(item, ctx);
2925 0 : }
2926 :
2927 1250 : void TLayout::layoutGuitarBend(GuitarBend* item, LayoutContext& ctx, System* system)
2928 : {
2929 : LAYOUT_CALL_ITEM(item);
2930 1250 : item->computeBendAmount();
2931 :
2932 1250 : GuitarBendLayout::updateSegmentsAndLayout(item, ctx, system);
2933 :
2934 1250 : item->updateHoldLine();
2935 1250 : if (item->holdLine()) {
2936 6 : GuitarBendLayout::updateSegmentsAndLayout(item->holdLine(), ctx, system);
2937 : }
2938 1250 : }
2939 :
2940 1250 : void TLayout::layoutGuitarBendSegment(GuitarBendSegment* item, LayoutContext& ctx)
2941 : {
2942 : LAYOUT_CALL_ITEM(item);
2943 1250 : bool tabStaff = item->staffType()->isTabStaff();
2944 1250 : if (tabStaff) {
2945 94 : GuitarBendLayout::layoutTabStaff(item, ctx);
2946 : } else {
2947 1156 : GuitarBendLayout::layoutStandardStaff(item, ctx);
2948 : }
2949 :
2950 1250 : SysStaff* staff = item->system()->staff(item->staffIdx());
2951 1250 : Skyline& skyline = staff->skyline();
2952 1250 : SkylineLine& skylineLine = tabStaff ? skyline.north() : (item->guitarBend()->ldata()->up() ? skyline.north() : skyline.south());
2953 1250 : skylineLine.add(item->shape().translate(item->pos()));
2954 :
2955 1250 : fillGuitarBendSegmentShape(item, item->mutldata());
2956 1250 : }
2957 :
2958 2573 : void TLayout::fillGuitarBendSegmentShape(const GuitarBendSegment* item, GuitarBendSegment::LayoutData* ldata)
2959 : {
2960 : LAYOUT_CALL_ITEM(item);
2961 2573 : Shape shape;
2962 2573 : shape.add(ldata->bbox(), item);
2963 2573 : if (!item->bendText()->empty()) {
2964 204 : shape.add(item->bendText()->shape().translate(item->bendText()->pos()));
2965 : }
2966 2573 : ldata->setShape(shape);
2967 2573 : }
2968 :
2969 472 : void TLayout::layoutHairpinSegment(HairpinSegment* item, LayoutContext& ctx)
2970 : {
2971 : LAYOUT_CALL_ITEM(item);
2972 472 : HairpinSegment::LayoutData* ldata = item->mutldata();
2973 :
2974 472 : const StaffType* stType = item->staffType();
2975 :
2976 472 : if (stType && stType->isHiddenElementOnTab(Sid::hairpinShowTabCommon, Sid::hairpinShowTabSimple)) {
2977 0 : ldata->setIsSkipDraw(true);
2978 0 : return;
2979 : }
2980 472 : ldata->setIsSkipDraw(false);
2981 :
2982 472 : const double _spatium = item->spatium();
2983 :
2984 472 : ldata->disconnectSnappedItems();
2985 :
2986 472 : manageHairpinSnapping(item, ctx);
2987 :
2988 472 : HairpinType type = item->hairpin()->hairpinType();
2989 472 : if (item->hairpin()->isLineType()) {
2990 26 : item->setTwoLines(false);
2991 26 : layoutTextLineBaseSegment(item, ctx);
2992 26 : item->setDrawCircledTip(false);
2993 26 : item->setCircledTipRadius(0.0);
2994 : } else {
2995 446 : item->setTwoLines(true);
2996 :
2997 446 : item->hairpin()->setBeginTextAlign({ AlignH::LEFT, AlignV::VCENTER });
2998 446 : item->hairpin()->setEndTextAlign({ AlignH::RIGHT, AlignV::VCENTER });
2999 446 : item->hairpin()->setBeginTextPosition(AlignH::LEFT);
3000 446 : item->hairpin()->setEndTextPosition(AlignH::RIGHT);
3001 :
3002 446 : double x1 = 0.0;
3003 446 : layoutTextLineBaseSegment(item, ctx);
3004 446 : if (!item->text()->empty()) {
3005 42 : x1 = item->text()->width() + _spatium * .5;
3006 : }
3007 :
3008 446 : Transform t;
3009 446 : double h1 = item->hairpin()->hairpinHeight().val() * _spatium * .5;
3010 446 : double h2 = item->hairpin()->hairpinContHeight().val() * _spatium * .5;
3011 :
3012 446 : double x = item->pos2().x();
3013 446 : if (!item->endText()->empty()) {
3014 4 : x -= (item->endText()->width() + _spatium * .5); // 0.5 spatium distance
3015 : }
3016 446 : if (x < _spatium) { // minimum size of hairpin
3017 4 : x = _spatium;
3018 : }
3019 446 : double y = item->pos2().y();
3020 446 : double len = sqrt(x * x + y * y);
3021 446 : t.rotateRadians(asin(y / len));
3022 :
3023 446 : item->setDrawCircledTip(item->hairpin()->hairpinCircledTip());
3024 446 : item->setCircledTipRadius(item->drawCircledTip() ? 0.6 * _spatium * .5 : 0.0);
3025 :
3026 446 : LineF l1, l2;
3027 :
3028 446 : switch (type) {
3029 358 : case HairpinType::CRESC_HAIRPIN: {
3030 358 : switch (item->spannerSegmentType()) {
3031 356 : case SpannerSegmentType::SINGLE:
3032 : case SpannerSegmentType::BEGIN: {
3033 356 : l1.setLine(x1 + item->circledTipRadius() * 2.0, 0.0, len, h1);
3034 356 : l2.setLine(x1 + item->circledTipRadius() * 2.0, 0.0, len, -h1);
3035 356 : PointF circledTip;
3036 356 : circledTip.setX(x1 + item->circledTipRadius());
3037 356 : circledTip.setY(0.0);
3038 356 : item->setCircledTip(circledTip);
3039 356 : } break;
3040 :
3041 2 : case SpannerSegmentType::MIDDLE:
3042 : case SpannerSegmentType::END:
3043 2 : item->setDrawCircledTip(false);
3044 2 : l1.setLine(x1, h2, len, h1);
3045 2 : l2.setLine(x1, -h2, len, -h1);
3046 2 : break;
3047 : }
3048 : }
3049 358 : break;
3050 88 : case HairpinType::DIM_HAIRPIN: {
3051 88 : switch (item->spannerSegmentType()) {
3052 88 : case SpannerSegmentType::SINGLE:
3053 : case SpannerSegmentType::END: {
3054 88 : l1.setLine(x1, h1, len - item->circledTipRadius() * 2, 0.0);
3055 88 : l2.setLine(x1, -h1, len - item->circledTipRadius() * 2, 0.0);
3056 88 : PointF circledTip;
3057 88 : circledTip.setX(len - item->circledTipRadius());
3058 88 : circledTip.setY(0.0);
3059 88 : item->setCircledTip(circledTip);
3060 88 : } break;
3061 0 : case SpannerSegmentType::BEGIN:
3062 : case SpannerSegmentType::MIDDLE:
3063 0 : item->setDrawCircledTip(false);
3064 0 : l1.setLine(x1, h1, len, +h2);
3065 0 : l2.setLine(x1, -h1, len, -h2);
3066 0 : break;
3067 : }
3068 : }
3069 88 : break;
3070 0 : default:
3071 0 : break;
3072 : }
3073 :
3074 : // Do Coord rotation
3075 446 : l1 = t.map(l1);
3076 446 : l2 = t.map(l2);
3077 446 : if (item->drawCircledTip()) {
3078 3 : item->setCircledTip(t.map(item->circledTip()));
3079 : }
3080 :
3081 446 : ldata->points[0] = l1.p1();
3082 446 : ldata->points[1] = l1.p2();
3083 446 : ldata->points[2] = l2.p1();
3084 446 : ldata->points[3] = l2.p2();
3085 446 : ldata->npoints = 4;
3086 :
3087 446 : ldata->joinedHairpin.clear();
3088 446 : if (item->spannerSegmentType() != SpannerSegmentType::MIDDLE) {
3089 445 : if (type == HairpinType::DIM_HAIRPIN && item->spannerSegmentType() != SpannerSegmentType::BEGIN) {
3090 88 : ldata->joinedHairpin << ldata->points[0] << ldata->points[1] << ldata->points[2]; // [top-left, joint, bottom-left]
3091 357 : } else if (type == HairpinType::CRESC_HAIRPIN && item->spannerSegmentType() != SpannerSegmentType::END) {
3092 356 : ldata->joinedHairpin << ldata->points[1] << ldata->points[0] << ldata->points[3]; // [top-right, joint, bottom-right]
3093 : }
3094 : }
3095 :
3096 446 : RectF r = RectF(l1.p1(), l1.p2()).normalized().united(RectF(l2.p1(), l2.p2()).normalized());
3097 446 : if (!item->text()->empty()) {
3098 42 : r.unite(item->text()->ldata()->bbox());
3099 : }
3100 446 : if (!item->endText()->empty()) {
3101 4 : r.unite(item->endText()->ldata()->bbox().translated(x + item->endText()->ldata()->bbox().width(), 0.0));
3102 : }
3103 446 : double w = item->absoluteFromSpatium(ctx.conf().styleS(Sid::hairpinLineWidth));
3104 446 : item->setbbox(r.adjusted(-w * .5, -w * .5, w, w));
3105 : }
3106 :
3107 472 : if (!item->ownershipParent()) {
3108 0 : item->setPos(PointF());
3109 0 : item->roffset() = PointF();
3110 0 : return;
3111 : }
3112 :
3113 472 : if (item->autoplace()) {
3114 472 : Autoplace::autoplaceSpannerSegment(item, ldata);
3115 : }
3116 : }
3117 :
3118 472 : void TLayout::manageHairpinSnapping(HairpinSegment* item, LayoutContext& ctx)
3119 : {
3120 472 : HairpinSegment::LayoutData* ldata = item->mutldata();
3121 :
3122 472 : EngravingItem* possibleSnapBeforeElement = nullptr;
3123 472 : EngravingItem* possibleSnapAfterElement = nullptr;
3124 472 : if (item->isSingleBeginType()) {
3125 467 : possibleSnapBeforeElement = item->findElementToSnapBefore();
3126 : }
3127 472 : if (item->isSingleEndType() && item->hairpin()->ticks().isNotZero()) {
3128 463 : possibleSnapAfterElement = item->findElementToSnapAfter();
3129 : }
3130 :
3131 472 : bool doSnapBefore = possibleSnapBeforeElement && item->hairpin()->snapToItemBefore();
3132 472 : if (doSnapBefore && possibleSnapBeforeElement->isHairpinSegment()) {
3133 58 : doSnapBefore = doSnapBefore && toHairpinSegment(possibleSnapBeforeElement)->hairpin()->snapToItemAfter()
3134 58 : && possibleSnapBeforeElement->ldata()->itemSnappedBefore() != item;
3135 : }
3136 :
3137 472 : if (doSnapBefore) {
3138 135 : ldata->connectItemSnappedBefore(possibleSnapBeforeElement);
3139 : }
3140 :
3141 472 : bool doSnapAfter = possibleSnapAfterElement && item->hairpin()->snapToItemAfter();
3142 472 : if (doSnapAfter && possibleSnapAfterElement->isHairpinSegment()) {
3143 0 : doSnapAfter = doSnapAfter && toHairpinSegment(possibleSnapAfterElement)->hairpin()->snapToItemBefore()
3144 0 : && possibleSnapAfterElement->ldata()->itemSnappedAfter() != item;
3145 : }
3146 :
3147 472 : if (doSnapAfter) {
3148 69 : ldata->connectItemSnappedAfter(possibleSnapAfterElement);
3149 : }
3150 :
3151 : // In case of dynamics/expressions before or after, make space for them horizontally
3152 472 : double mag = item->staff()->staffMag(item);
3153 472 : double hairpinDistToDynOrExpr = ctx.conf().style().styleAbsolute(Sid::autoplaceHairpinDynamicsDistance) * mag;
3154 :
3155 135 : bool makeSpaceBefore = (doSnapBefore && possibleSnapBeforeElement->isTextBase())
3156 607 : || (possibleSnapBeforeElement && possibleSnapBeforeElement->isDynamic());
3157 :
3158 472 : if (makeSpaceBefore && possibleSnapBeforeElement->findAncestor(ElementType::SYSTEM) == item->system()) {
3159 106 : double xItemPos = possibleSnapBeforeElement->pageX() - item->system()->pageX();
3160 106 : double itemRightEdge = xItemPos + possibleSnapBeforeElement->ldata()->bbox().right();
3161 106 : double xMinHairpinStart = itemRightEdge + hairpinDistToDynOrExpr;
3162 106 : double xStartDiff = ldata->pos().x() - xMinHairpinStart;
3163 106 : if (xStartDiff < 0) {
3164 106 : ldata->setPosX(xMinHairpinStart);
3165 106 : item->rxpos2() += xStartDiff;
3166 : }
3167 : }
3168 :
3169 69 : bool makeSpaceAfter = (doSnapAfter && possibleSnapAfterElement->isTextBase())
3170 541 : || (possibleSnapAfterElement && possibleSnapAfterElement->isDynamic());
3171 :
3172 472 : if (makeSpaceAfter && possibleSnapAfterElement->findAncestor(ElementType::SYSTEM) == item->system()) {
3173 69 : double xItemPos = possibleSnapAfterElement->pageX() - item->system()->pageX();
3174 69 : double itemLeftEdge = xItemPos + possibleSnapAfterElement->ldata()->bbox().left();
3175 69 : double xMaxHairpinEnd = itemLeftEdge - hairpinDistToDynOrExpr;
3176 69 : double xEndDiff = xMaxHairpinEnd - (item->pos().x() + item->pos2().x());
3177 69 : const double EXTEND_THRESHOLD = 3.0 * item->spatium();
3178 69 : if (xEndDiff < 0) {
3179 27 : item->rxpos2() += xEndDiff;
3180 42 : } else if (item->hairpin()->snapToItemAfter() && xEndDiff > EXTEND_THRESHOLD) {
3181 0 : item->rxpos2() += xEndDiff;
3182 : }
3183 : }
3184 472 : }
3185 :
3186 0 : void TLayout::layoutHairpin(Hairpin* item, LayoutContext& ctx)
3187 : {
3188 : LAYOUT_CALL_ITEM(item);
3189 0 : item->setPos(0.0, 0.0);
3190 0 : layoutTextLineBase(item, ctx);
3191 0 : }
3192 :
3193 0 : void TLayout::layoutHammerOnPullOff(HammerOnPullOff* item, LayoutContext& ctx)
3194 : {
3195 0 : layoutSlur(toSlur(item), ctx);
3196 0 : }
3197 :
3198 61 : void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutContext& ctx)
3199 : {
3200 : // The layout of the slur has already been done. Here we layout the H/P letters.
3201 61 : item->updateHopoText();
3202 :
3203 61 : System* system = item->system();
3204 61 : Fraction systemEndTick = system->endTick();
3205 61 : Skyline& sk = system->staff(item->staffIdx())->skyline();
3206 :
3207 136 : for (HammerOnPullOffText* hopoText : item->hopoText()) {
3208 75 : bool above = hopoText->placeAbove();
3209 :
3210 75 : Align align;
3211 75 : align.vertical = AlignV::BASELINE;
3212 75 : align.horizontal = AlignH::HCENTER;
3213 75 : hopoText->setPosition(AlignH::HCENTER);
3214 75 : hopoText->setAlign(align);
3215 75 : layoutItem(hopoText, ctx);
3216 :
3217 75 : const Chord* startChord = hopoText->startChord();
3218 75 : const Chord* endChord = hopoText->endChord();
3219 75 : double startX = startChord->systemPos().x() + startChord->upNote()->headWidth();
3220 75 : double endX = startX;
3221 75 : if (endChord->tick() < systemEndTick) {
3222 75 : endX = endChord->systemPos().x();
3223 : } else {
3224 : // The last endChord of this segment is in next system. Use end barline instead.
3225 0 : Segment* endSeg = system->lastMeasure()->last(SegmentType::BarLineTypes);
3226 0 : endX = endSeg ? endSeg->systemPos().x() : endX;
3227 : }
3228 75 : if (startChord->stem() && endChord->stem() && startChord->up() == above && endChord->up() == above) {
3229 : // Mid-way between centered on the notes and centered on the stems
3230 19 : endX += (above ? 0.5 : -0.5) * endChord->upNote()->headWidth();
3231 : }
3232 75 : double centerX = 0.5 * (startX + endX);
3233 :
3234 75 : double vertPadding = 0.5 * item->spatium();
3235 75 : Shape hopoTextShape = hopoText->ldata()->shape().translated(PointF(centerX, 0.0));
3236 75 : SkylineLine& skl = above ? sk.north() : sk.south();
3237 150 : SkylineLine skyline = skl.getFilteredCopy([](const ShapeElement& el) {
3238 2499 : return el.item() && el.item()->isArticulationFamily();
3239 75 : });
3240 :
3241 75 : double y = above ? -skyline.minDistanceToShapeAbove(hopoTextShape) : skyline.minDistanceToShapeBelow(hopoTextShape);
3242 75 : y += above ? -vertPadding : vertPadding;
3243 75 : y = above ? std::min(y, -vertPadding) : std::max(y, item->staff()->staffHeight(item->tick()) + vertPadding);
3244 :
3245 75 : Note* startNote = above ? startChord->upNote() : startChord->downNote();
3246 75 : Note* endNote = above ? endChord->upNote() : endChord->downNote();
3247 75 : double yNoteLimit = above ? std::min(startNote->y(), endNote->y()) - 2 * vertPadding
3248 0 : : std::max(startNote->y(), endNote->y()) + 2 * vertPadding;
3249 75 : y = above ? std::min(y, yNoteLimit) : std::max(y, yNoteLimit);
3250 :
3251 75 : hopoText->mutldata()->setPos(centerX, y);
3252 :
3253 75 : hopoTextShape.translateY(y);
3254 75 : skl.add(hopoTextShape);
3255 75 : }
3256 :
3257 61 : Shape hopoSegmentShape = item->mutldata()->shape();
3258 136 : for (HammerOnPullOffText* hopoText : item->hopoText()) {
3259 75 : hopoSegmentShape.add(hopoText->ldata()->shape().translated(hopoText->pos()));
3260 : }
3261 :
3262 61 : item->mutldata()->setShape(hopoSegmentShape);
3263 61 : }
3264 :
3265 75 : void TLayout::layoutHammerOnPullOffText(HammerOnPullOffText* item, LayoutContext& ctx)
3266 : {
3267 75 : TextLayout::layoutBaseTextBase(item, ctx);
3268 75 : }
3269 :
3270 32 : void TLayout::layoutHarpPedalDiagram(const HarpPedalDiagram* item, HarpPedalDiagram::LayoutData* ldata)
3271 : {
3272 : LAYOUT_CALL_ITEM(item);
3273 32 : const_cast<HarpPedalDiagram*>(item)->updateDiagramText();
3274 :
3275 32 : TextLayout::layoutBaseTextBase(item, ldata);
3276 :
3277 32 : if (item->autoplace()) {
3278 32 : const Segment* s = toSegment(item->ownershipParent());
3279 32 : const Measure* m = s->measure();
3280 :
3281 32 : LD_CONDITION(ldata->isSetPos());
3282 32 : LD_CONDITION(m->ldata()->isSetPos());
3283 32 : LD_CONDITION(s->ldata()->isSetPos());
3284 : }
3285 :
3286 32 : Autoplace::autoplaceSegmentElement(item, ldata);
3287 32 : }
3288 :
3289 29 : void TLayout::layoutHarmonicMarkSegment(HarmonicMarkSegment* item, LayoutContext& ctx)
3290 : {
3291 : LAYOUT_CALL_ITEM(item);
3292 29 : HarmonicMarkSegment::LayoutData* ldata = item->mutldata();
3293 29 : const StaffType* stType = item->staffType();
3294 29 : if (stType
3295 29 : && (!stType->isTabStaff()
3296 0 : || stType->isHiddenElementOnTab(Sid::harmonicMarkShowTabCommon, Sid::harmonicMarkShowTabSimple))) {
3297 29 : ldata->setIsSkipDraw(true);
3298 29 : return;
3299 : }
3300 0 : ldata->setIsSkipDraw(false);
3301 :
3302 0 : layoutTextLineBaseSegment(item, ctx);
3303 :
3304 0 : Autoplace::autoplaceSpannerSegment(item, ldata);
3305 : }
3306 :
3307 3654 : void TLayout::layoutHarmony(Harmony* item, Harmony::LayoutData* ldata, const LayoutContext& ctx)
3308 : {
3309 : LAYOUT_CALL_ITEM(item);
3310 : LD_INDEPENDENT;
3311 :
3312 3654 : HarmonyLayout::layoutHarmony(item, ldata, ctx);
3313 3654 : }
3314 :
3315 5787 : void TLayout::layoutHook(const Hook* item, Hook::LayoutData* ldata)
3316 : {
3317 : LAYOUT_CALL_ITEM(item);
3318 : LD_INDEPENDENT;
3319 :
3320 5787 : if (ldata->isValid()) {
3321 3495 : return;
3322 : }
3323 :
3324 2292 : ldata->setBbox(item->symBbox(item->sym()));
3325 : }
3326 :
3327 60 : void TLayout::layoutImage(const Image* item, Image::LayoutData* ldata)
3328 : {
3329 : LAYOUT_CALL_ITEM(item);
3330 60 : IF_ASSERT_FAILED(item->layoutParent()) {
3331 0 : return;
3332 : }
3333 :
3334 60 : ldata->setPos(0.0, 0.0);
3335 60 : const_cast<Image*>(item)->init();
3336 :
3337 60 : SizeF imageSize = item->size();
3338 :
3339 : // if autoscale && inside a box, scale to box relevant size
3340 60 : if (item->autoScale()
3341 60 : && ((item->layoutParent()->isHBox() || item->layoutParent()->isVBox()))) {
3342 0 : const EngravingItem::LayoutData* parentLD = item->layoutParent()->ldata();
3343 :
3344 0 : LD_CONDITION(parentLD->isSetBbox());
3345 :
3346 0 : if (item->lockAspectRatio()) {
3347 0 : double f = item->sizeIsSpatium() ? item->spatium() : DPMM;
3348 0 : SizeF size(item->imageSize());
3349 0 : double ratio = size.width() / size.height();
3350 0 : double w = parentLD->bbox().width();
3351 0 : double h = parentLD->bbox().height();
3352 0 : if ((w / h) < ratio) {
3353 0 : imageSize.setWidth(w / f);
3354 0 : imageSize.setHeight((w / ratio) / f);
3355 : } else {
3356 0 : imageSize.setHeight(h / f);
3357 0 : imageSize.setWidth(h * ratio / f);
3358 : }
3359 : } else {
3360 0 : imageSize = item->pixel2size(parentLD->bbox().size());
3361 : }
3362 : }
3363 :
3364 60 : const_cast<Image*>(item)->setSize(imageSize);
3365 :
3366 : // in any case, adjust position relative to parent
3367 60 : ldata->setBbox(RectF(PointF(), item->size2pixel(imageSize)));
3368 : }
3369 :
3370 91 : void TLayout::layoutInstrumentChange(const InstrumentChange* item, InstrumentChange::LayoutData* ldata)
3371 : {
3372 : LAYOUT_CALL_ITEM(item);
3373 91 : TextLayout::layoutBaseTextBase(item, ldata);
3374 :
3375 91 : if (item->autoplace()) {
3376 91 : const Segment* s = toSegment(item->ownershipParent());
3377 91 : const Measure* m = s->measure();
3378 91 : LD_CONDITION(ldata->isSetPos());
3379 91 : LD_CONDITION(m->ldata()->isSetPos());
3380 91 : LD_CONDITION(s->ldata()->isSetPos());
3381 : }
3382 :
3383 91 : Autoplace::autoplaceSegmentElement(item, ldata);
3384 91 : }
3385 :
3386 7975 : void TLayout::layoutInstrumentName(const InstrumentName* item, InstrumentName::LayoutData* ldata)
3387 : {
3388 : LAYOUT_CALL_ITEM(item);
3389 : LD_INDEPENDENT;
3390 : //! NOTE There are problems, an investigation is required
3391 : // if (ldata->isValid()) {
3392 : // return;
3393 : // }
3394 :
3395 7975 : TextLayout::layoutBaseTextBase(item, ldata);
3396 7975 : }
3397 :
3398 212 : void TLayout::layoutJump(const Jump* item, Jump::LayoutData* ldata)
3399 : {
3400 : LAYOUT_CALL_ITEM(item);
3401 212 : LD_CONDITION(item->layoutParent()->ldata()->isSetBbox());
3402 :
3403 212 : TextLayout::layoutBaseTextBase(item, ldata);
3404 :
3405 212 : AlignH position = item->position();
3406 :
3407 : // ignore courtesy keysig, timesig, but fall back if needed
3408 212 : Measure* measure = toMeasure(item->parent());
3409 212 : const BarLine* bl = measure->endBarLine();
3410 212 : double xAdj = bl ? bl->segment()->x() + bl->ldata()->bbox().width() : measure->width();
3411 :
3412 212 : bool avoidBarline = item->staffIdx() != 0 && position != AlignH::HCENTER;
3413 212 : if (avoidBarline) {
3414 1 : bool startRepeat = measure->repeatStart();
3415 1 : bool endRepeat = measure->repeatEnd();
3416 1 : staff_idx_t blAboveIdx = item->staffIdx() - 1;
3417 :
3418 1 : const double fontSizeScaleFactor = item->size() / 10.0;
3419 1 : double padding = (startRepeat || endRepeat) ? 0.0 : 0.5 * item->spatium() * fontSizeScaleFactor;
3420 :
3421 1 : if (position == AlignH::LEFT) {
3422 0 : const BarLine* blAbove = startRepeat || !measure->prevMeasure()
3423 0 : ? measure->startBarLine(blAboveIdx) : measure->prevMeasure()->endBarLine(blAboveIdx);
3424 0 : double blWidth = (startRepeat && blAbove) ? blAbove->width() : 0.0;
3425 0 : xAdj += padding + blWidth;
3426 1 : } else if (position == AlignH::RIGHT) {
3427 1 : const BarLine* blAbove = measure->endBarLine(blAboveIdx);
3428 1 : double blWidth = blAbove ? blAbove->width() : 0.0;
3429 1 : xAdj -= blWidth + padding;
3430 : }
3431 : }
3432 212 : ldata->moveX(xAdj);
3433 :
3434 212 : if (item->autoplace()) {
3435 212 : LD_CONDITION(ldata->isSetPos());
3436 212 : LD_CONDITION(ldata->isSetBbox());
3437 212 : LD_CONDITION(measure->ldata()->isSetPos());
3438 : }
3439 :
3440 212 : Autoplace::autoplaceMeasureElement(item, ldata);
3441 212 : }
3442 :
3443 8376 : static void keySigAddLayout(const KeySig* item, const LayoutConfiguration& conf, SymId sym, int line, KeySig::LayoutData* ldata)
3444 : {
3445 8376 : double _spatium = item->spatium();
3446 8376 : double step = _spatium * (item->staff() ? item->staff()->staffTypeForElement(item)->lineDistance().val() * 0.5 : 0.5);
3447 8376 : KeySym ks;
3448 8376 : ks.sym = sym;
3449 8376 : Spatium x = 0.0_sp;
3450 8376 : if (!ldata->keySymbols.empty()) {
3451 5224 : const KeySym& previous = ldata->keySymbols.back();
3452 5224 : Spatium accidentalGap = conf.styleS(Sid::keysigAccidentalDistance);
3453 5224 : if (previous.sym != sym) {
3454 2 : accidentalGap *= 2;
3455 5222 : } else if (previous.sym == SymId::accidentalNatural && sym == SymId::accidentalNatural) {
3456 248 : accidentalGap = conf.styleS(Sid::keysigNaturalDistance);
3457 : }
3458 5224 : Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(previous.sym), _spatium);
3459 5224 : x = previous.xPos + previousWidth + accidentalGap;
3460 5224 : bool isAscending = line < previous.line;
3461 5224 : SmuflAnchorId currentCutout = isAscending ? SmuflAnchorId::cutOutSW : SmuflAnchorId::cutOutNW;
3462 5224 : SmuflAnchorId previousCutout = isAscending ? SmuflAnchorId::cutOutNE : SmuflAnchorId::cutOutSE;
3463 5224 : PointF cutout = item->symSmuflAnchor(sym, currentCutout);
3464 5224 : double currentCutoutY = line * step + cutout.y();
3465 5224 : double previousCutoutY = previous.line * step + item->symSmuflAnchor(previous.sym, previousCutout).y();
3466 5224 : if ((isAscending && currentCutoutY < previousCutoutY) || (!isAscending && currentCutoutY > previousCutoutY)) {
3467 5136 : x -= Spatium::fromAbsolute(cutout.x(), _spatium);
3468 : }
3469 : }
3470 8376 : ks.xPos = x;
3471 8376 : ks.line = line;
3472 8376 : ldata->keySymbols.push_back(ks);
3473 8376 : }
3474 :
3475 23295 : void TLayout::layoutKeySig(const KeySig* item, KeySig::LayoutData* ldata, const LayoutConfiguration& conf)
3476 : {
3477 : LAYOUT_CALL_ITEM(item);
3478 : LD_INDEPENDENT;
3479 :
3480 : //! NOTE There are problems, an investigation is required
3481 : // if (ldata->isValid()) {
3482 : // return;
3483 : // }
3484 :
3485 23295 : ldata->setBbox(RectF());
3486 23295 : ldata->keySymbols.clear();
3487 :
3488 23295 : const Staff* staff = item->staff();
3489 23295 : const StaffType* stVisibility = staff ? staff->staffType(item->tick()) : nullptr;
3490 23295 : if (stVisibility && !stVisibility->genKeysig()) {
3491 216 : return;
3492 : }
3493 :
3494 23079 : const StaffType* st = item->staffType();
3495 23079 : const Segment* s = item->segment();
3496 23079 : track_idx_t track = item->track();
3497 23079 : double spatium = item->spatium();
3498 23079 : double step = spatium * (st ? st->lineDistance().val() * 0.5 : 0.5);
3499 :
3500 : // determine current clef for this staff
3501 23079 : ClefType clef = ClefType::G;
3502 23079 : if (item->staff()) {
3503 : // Look for a clef before the key signature at the same tick
3504 23079 : Clef* c = nullptr;
3505 23079 : if (s) {
3506 46169 : for (Segment* seg = s->prev1(); !c && seg && seg->tick() == item->tick(); seg = seg->prev1()) {
3507 46027 : const bool isClefSeg = seg->isClefType() || seg->isHeaderClefType()
3508 46027 : || (seg->isClefRepeatAnnounceType() && s->isKeySigRepeatAnnounceType());
3509 23090 : if (seg->enabled() && isClefSeg) {
3510 21022 : c = toClef(seg->element(track));
3511 : }
3512 : }
3513 : }
3514 : // If no clef found, get the clef type from the clefs list (using the previous tick)
3515 23079 : clef = c ? c->clefType() : item->staff()->clef(item->tick() - Fraction::eps());
3516 : }
3517 :
3518 23079 : int t1 = int(item->key());
3519 23079 : const signed char* lines = ClefInfo::lines(clef);
3520 :
3521 23079 : if (item->isCustom() && !item->isAtonal()) {
3522 50 : Spatium accidentalGap = conf.styleS(Sid::keysigAccidentalDistance);
3523 : // add standard key accidentals first, if necessary
3524 50 : for (int i = 1; i <= std::abs(t1) && std::abs(t1) <= 7; ++i) {
3525 0 : bool drop = false;
3526 0 : for (const CustDef& cd: item->customKeyDefs()) {
3527 0 : int degree = item->degInKey(cd.degree);
3528 : // if custom keysig accidental takes place, don't create tonal accidental
3529 0 : if ((degree * 2 + 2) % 7 == (t1 < 0 ? 8 - i : i) % 7) {
3530 0 : drop = true;
3531 0 : break;
3532 : }
3533 : }
3534 0 : if (!drop) {
3535 0 : KeySym ks;
3536 0 : int lineIndexOffset = t1 > 0 ? -1 : 6;
3537 0 : ks.sym = t1 > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
3538 0 : ks.line = lines[lineIndexOffset + i];
3539 0 : if (!ldata->keySymbols.empty()) {
3540 0 : const KeySym& previous = ldata->keySymbols.back();
3541 0 : Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(previous.sym), spatium);
3542 0 : ks.xPos = previous.xPos + previousWidth + accidentalGap;
3543 : } else {
3544 0 : ks.xPos = 0_sp;
3545 : }
3546 : // TODO octave metters?
3547 0 : ldata->keySymbols.push_back(ks);
3548 : }
3549 : }
3550 148 : for (const CustDef& cd : item->customKeyDefs()) {
3551 98 : SymId sym = item->symInKey(cd.sym, cd.degree);
3552 98 : int degree = item->degInKey(cd.degree);
3553 98 : bool flat = std::string(SymNames::nameForSymId(sym).ascii()).find("Flat") != std::string::npos;
3554 98 : int accIdx = (degree * 2 + 1) % 7; // C D E F ... index to F C G D index
3555 98 : accIdx = flat ? 13 - accIdx : accIdx;
3556 98 : int line = lines[accIdx] + cd.octAlt * 7;
3557 98 : Spatium xpos = cd.xAlt;
3558 98 : if (!ldata->keySymbols.empty()) {
3559 48 : const KeySym& previous = ldata->keySymbols.back();
3560 48 : Spatium previousWidth = Spatium::fromAbsolute(item->symWidth(previous.sym), spatium);
3561 48 : xpos += previous.xPos + previousWidth + accidentalGap;
3562 : }
3563 : // if translated symbol if out of range, add key accidental followed by untranslated symbol
3564 98 : if (sym == SymId::noSym) {
3565 0 : KeySym ks;
3566 0 : ks.line = line;
3567 0 : ks.xPos = xpos;
3568 : // for quadruple sharp use two double sharps
3569 0 : if (cd.sym == SymId::accidentalTripleSharp) {
3570 0 : ks.sym = SymId::accidentalDoubleSharp;
3571 0 : sym = SymId::accidentalDoubleSharp;
3572 : } else {
3573 0 : ks.sym = t1 > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
3574 0 : sym = cd.sym;
3575 : }
3576 0 : ldata->keySymbols.push_back(ks);
3577 0 : xpos += Spatium(t1 < 0 ? 0.7 : 1); // flats closer
3578 : }
3579 : // create symbol; natural only if is user defined
3580 98 : if (sym != SymId::accidentalNatural || sym == cd.sym) {
3581 98 : KeySym ks;
3582 98 : ks.sym = sym;
3583 98 : ks.line = line;
3584 98 : ks.xPos = xpos;
3585 98 : ldata->keySymbols.push_back(ks);
3586 : }
3587 : }
3588 : } else {
3589 23029 : auto layoutSharpsFlats = [&]() {
3590 23029 : if (std::abs(t1) > 7) {
3591 0 : LOGD("illegal t1 key %d", t1);
3592 0 : return;
3593 : }
3594 23029 : SymId symbol = t1 > 0 ? SymId::accidentalSharp : SymId::accidentalFlat;
3595 23029 : int lineIndexOffset = t1 > 0 ? 0 : 7;
3596 31091 : for (int i = 0; i < std::abs(t1); ++i) {
3597 8062 : keySigAddLayout(item, conf, symbol, lines[lineIndexOffset + i], ldata);
3598 : }
3599 23029 : };
3600 :
3601 : // Naturals are shown if:
3602 : // Key signature is courtesy, mid-measure or prev. measure has no section break and no courtesy keysig.
3603 : // AND we're not force hiding naturals (continuous mode)
3604 : // AND key sig is CMaj/Amin OR style says they are on
3605 23029 : const Measure* pm = item->measure() ? item->measure()->prevMeasureMM() : nullptr;
3606 23029 : const bool isCourtesy = s && (s->isType(SegmentType::CourtesyKeySigTypes) || !s->rtick().isZero());
3607 23029 : const bool prevTrailerCourtesy = pm && !pm->sectionBreak() && (!pm->trailer() || !pm->hasCourtesyKeySig());
3608 46058 : if (!item->hideNaturals() && track != muse::nidx
3609 23029 : && (conf.styleI(Sid::keySigNaturals) != int(KeySigNatural::NONE) || (t1 == 0))
3610 46058 : && (isCourtesy || prevTrailerCourtesy)) {
3611 7963 : KeySigEvent prevKsEvent = item->staff() ? item->staff()->keySigEvent(item->tick() - Fraction::eps()) : KeySigEvent();
3612 7963 : int t2 = int(prevKsEvent.key());
3613 :
3614 : // Handle naturals in continuation courtesy
3615 7963 : if (pm && s && s->isType(SegmentType::KeySigStartRepeatAnnounce)) {
3616 0 : Segment* prevCourtesySeg = pm->findSegmentR(SegmentType::KeySigRepeatAnnounce, pm->ticks());
3617 0 : if (prevCourtesySeg && prevCourtesySeg->element(track)) {
3618 0 : t2 = int(toKeySig(prevCourtesySeg->element(track))->key());
3619 : }
3620 : }
3621 : // Don't show naturals when going from sharps to flats, if style says so
3622 7963 : const bool sameAccidentals = t1 * t2 >= 0;
3623 7963 : if (t2 != 0 && (sameAccidentals || conf.styleB(Sid::keySigShowNaturalsChangingSharpsFlats))) {
3624 110 : auto key2accidentals = [](int key) -> int {
3625 110 : switch (std::abs(key)) {
3626 28 : case 7: return 0x7f;
3627 0 : case 6: return 0x3f;
3628 8 : case 5: return 0x1f;
3629 8 : case 4: return 0xf;
3630 12 : case 3: return 0x7;
3631 18 : case 2: return 0x3;
3632 4 : case 1: return 0x1;
3633 32 : case 0: return 0;
3634 0 : default:
3635 0 : LOGD("illegal key %d", key);
3636 0 : return 0;
3637 : }
3638 : };
3639 :
3640 72 : int naturals = key2accidentals(t2);
3641 : // remove redundant naturals
3642 72 : if (!((t1 > 0) ^ (t2 > 0))) {
3643 38 : naturals &= ~key2accidentals(t1);
3644 : }
3645 72 : auto layoutNaturals = [&]() {
3646 72 : int lineIndexOffset = t2 > 0 ? 0 : 7;
3647 576 : for (int i = 0; i < 7; ++i) {
3648 504 : if (naturals & (1 << i)) {
3649 314 : keySigAddLayout(item, conf, SymId::accidentalNatural, lines[i + lineIndexOffset], ldata);
3650 : }
3651 : }
3652 144 : };
3653 : // Naturals should go BEFORE accidentals if style says so
3654 : // or going from sharps to flats or vice versa (i.e. t1 & t2 have opposite signs)
3655 72 : if (conf.styleI(Sid::keySigNaturals) == int(KeySigNatural::BEFORE) || !sameAccidentals) {
3656 8 : layoutNaturals();
3657 8 : layoutSharpsFlats();
3658 : } else {
3659 64 : layoutSharpsFlats();
3660 64 : layoutNaturals();
3661 : }
3662 7891 : } else if (prevKsEvent.custom()) {
3663 0 : Fraction prevKeyTick = Fraction::fromTicks(item->staff()->keyList()->currentKeyTick(
3664 0 : (item->tick() - Fraction::eps()).ticks()));
3665 0 : Segment* prevKsSeg = item->score()->tick2segment(prevKeyTick, true, SegmentType::KeySig);
3666 0 : if (prevKsSeg) {
3667 0 : KeySig* prevCustomKeySig = toKeySig(prevKsSeg->element(item->track()));
3668 0 : if (prevCustomKeySig) {
3669 0 : for (KeySym keySym : prevCustomKeySig->ldata()->keySymbols) {
3670 0 : if (keySym.sym != SymId::accidentalNatural) {
3671 0 : keySigAddLayout(item, conf, SymId::accidentalNatural, keySym.line, ldata);
3672 : }
3673 : }
3674 : }
3675 : }
3676 : }
3677 7963 : }
3678 :
3679 : // No naturals were added, so just create a regular keysig
3680 23029 : if (ldata->keySymbols.empty()) {
3681 22957 : layoutSharpsFlats();
3682 : }
3683 : }
3684 :
3685 23079 : ldata->setPosY((st ? step * st->stepOffset() : 0.0) + item->staffOffsetY());
3686 :
3687 23079 : Shape keySigShape;
3688 31553 : for (const KeySym& ks : ldata->keySymbols) {
3689 8474 : double x = ks.xPos.toAbsolute(spatium);
3690 8474 : double y = ks.line * step;
3691 8474 : Shape ksShape = item->symShapeWithCutouts(ks.sym);
3692 38665 : for (const ShapeElement& ksElem : ksShape.elements()) {
3693 30191 : keySigShape.add(ksElem.translated(x, y), item);
3694 : }
3695 8474 : }
3696 23079 : ldata->setShape(keySigShape);
3697 23079 : }
3698 :
3699 0 : void TLayout::layoutLaissezVib(LaissezVib* item)
3700 : {
3701 : UNUSED(item);
3702 0 : }
3703 :
3704 2168 : void TLayout::layoutLayoutBreak(const LayoutBreak* item, LayoutBreak::LayoutData* ldata)
3705 : {
3706 : LAYOUT_CALL_ITEM(item);
3707 : LD_INDEPENDENT;
3708 :
3709 2168 : if (ldata->isValid() || !item->configuration()->canLayoutIcons()) {
3710 2168 : return;
3711 : }
3712 :
3713 0 : FontMetrics metrics(item->font());
3714 0 : RectF bbox = metrics.boundingRect(item->iconCode());
3715 0 : ldata->setShape(Shape(bbox, item));
3716 0 : }
3717 :
3718 147 : void TLayout::layoutIndicatorIcon(const IndicatorIcon* item, IndicatorIcon::LayoutData* ldata)
3719 : {
3720 147 : if (!item->configuration()->canLayoutIcons()) {
3721 147 : return;
3722 : }
3723 :
3724 0 : const MeasureBase* endMB = nullptr;
3725 :
3726 0 : Shape shape;
3727 :
3728 0 : const FontMetrics metrics(item->font());
3729 0 : const RectF iconBox = metrics.boundingRect(item->iconCode());
3730 0 : shape.add(iconBox, item);
3731 :
3732 0 : if (item->isSystemLockIndicator()) {
3733 0 : const SystemLockIndicator* sli = toSystemLockIndicator(item);
3734 0 : endMB = sli->systemLock()->endMB();
3735 0 : if (item->selected()) {
3736 : // Draw the range rect...
3737 0 : const RangeLock* lock = sli->systemLock();
3738 :
3739 0 : double xStart = lock->startMB()->x();
3740 0 : double xEnd = lock->endMB()->x() + lock->endMB()->width();
3741 :
3742 0 : double width = xEnd - xStart;
3743 0 : double height = iconBox.height() + sli->spatium();
3744 :
3745 0 : double y = iconBox.top() - 0.5 * sli->spatium();
3746 :
3747 0 : ldata->rangeRect = RectF(xStart, y, width, height).translated(-sli->x(), 0.0);
3748 0 : shape.add(ldata->rangeRect);
3749 : }
3750 : } else {
3751 0 : endMB = item->system()->last();
3752 : }
3753 :
3754 0 : ldata->setShape(shape);
3755 :
3756 0 : const double spatium = item->spatium();
3757 :
3758 0 : double x = endMB->x() + endMB->width();
3759 0 : x -= iconBox.right() + 0.5 * spatium;
3760 :
3761 0 : double xOffset = endMB->x() + endMB->width();
3762 0 : for (EngravingItem* el : endMB->el()) {
3763 0 : if (el->isLayoutBreak()) {
3764 0 : xOffset = std::min(xOffset, endMB->x() + el->x() + el->ldata()->bbox().left() - spatium);
3765 : }
3766 : }
3767 :
3768 0 : for (const SystemLockIndicator* sli : item->system()->systemLockIndicators()) {
3769 0 : if (sli != item && sli->systemLock()->endMB() == endMB) {
3770 0 : const double sliWidth = FontMetrics(sli->font()).boundingRect(sli->iconCode()).width();
3771 : // TODO: Rough spacing here
3772 0 : xOffset -= (sliWidth * 2) - spatium;
3773 : }
3774 0 : }
3775 :
3776 0 : x = std::min(x, xOffset - iconBox.right());
3777 :
3778 0 : ldata->setPos(PointF(x, -2.5 * spatium));
3779 :
3780 : // Ensure it goes behind notation and LayoutBreak
3781 0 : const_cast<IndicatorIcon*>(item)->setZ(-100);
3782 0 : }
3783 :
3784 206540 : static void _layoutLedgerLine(const LedgerLine* item, const LayoutContext& ctx, LedgerLine::LayoutData* ldata)
3785 : {
3786 206540 : double chordMag = item->chord()->mag();
3787 206540 : ldata->setMag(chordMag);
3788 206540 : ldata->lineWidth = ctx.conf().styleAbsolute(Sid::ledgerLineWidth) * chordMag;
3789 206540 : if (item->staff()) {
3790 206540 : const_cast<LedgerLine*>(item)->setColor(item->staff()->staffType(item->tick())->color());
3791 : }
3792 206540 : double w2 = ldata->lineWidth * .5;
3793 :
3794 206540 : if (item->vertical()) {
3795 0 : ldata->setBbox(-w2, 0, w2, item->len());
3796 : } else {
3797 206540 : ldata->setBbox(0, -w2, item->len(), 2 * w2);
3798 : }
3799 206540 : }
3800 :
3801 206540 : void TLayout::layoutLedgerLine(LedgerLine* item, LayoutContext& ctx)
3802 : {
3803 : LAYOUT_CALL_ITEM(item);
3804 206540 : _layoutLedgerLine(item, ctx, item->mutldata());
3805 206540 : }
3806 :
3807 0 : void TLayout::layoutLetRing(LetRing* item, LayoutContext& ctx)
3808 : {
3809 : LAYOUT_CALL_ITEM(item);
3810 0 : layoutLine(item, ctx);
3811 0 : }
3812 :
3813 31 : void TLayout::layoutLetRingSegment(LetRingSegment* item, LayoutContext& ctx)
3814 : {
3815 : LAYOUT_CALL_ITEM(item);
3816 31 : HarmonicMarkSegment::LayoutData* ldata = item->mutldata();
3817 :
3818 31 : const StaffType* stType = item->staffType();
3819 :
3820 31 : if (stType && stType->isHiddenElementOnTab(Sid::letRingShowTabCommon, Sid::letRingShowTabSimple)) {
3821 0 : ldata->setIsSkipDraw(true);
3822 0 : return;
3823 : }
3824 31 : ldata->setIsSkipDraw(false);
3825 :
3826 31 : layoutTextLineBaseSegment(item, ctx);
3827 :
3828 31 : Autoplace::autoplaceSpannerSegment(item, ldata);
3829 : }
3830 :
3831 3055 : void TLayout::layoutLineSegment(LineSegment* item, LayoutContext& ctx)
3832 : {
3833 : LAYOUT_CALL_ITEM(item);
3834 3055 : layoutItem(item, ctx);
3835 3055 : }
3836 :
3837 2180 : void TLayout::layoutLyrics(Lyrics* item, LayoutContext& ctx)
3838 : {
3839 : LAYOUT_CALL_ITEM(item);
3840 2180 : LyricsLayout::layout(item, ctx);
3841 2180 : }
3842 :
3843 328 : void TLayout::layoutLyricsLine(LyricsLine* item)
3844 : {
3845 : LAYOUT_CALL_ITEM(item);
3846 328 : LyricsLayout::layout(item);
3847 328 : }
3848 :
3849 348 : void TLayout::layoutLyricsLineSegment(LyricsLineSegment* item, LayoutContext& ctx)
3850 : {
3851 : LAYOUT_CALL_ITEM(item);
3852 348 : LyricsLayout::layout(item, ctx);
3853 348 : }
3854 :
3855 309 : void TLayout::layoutMarker(Marker* item, Marker::LayoutData* ldata, LayoutContext& ctx)
3856 : {
3857 : LAYOUT_CALL_ITEM(item);
3858 :
3859 309 : MarkerLayout::layoutMarker(item, ldata, ctx);
3860 309 : }
3861 :
3862 147 : void TLayout::layoutMeasureBase(MeasureBase* item, LayoutContext& ctx)
3863 : {
3864 147 : layoutItem(item, ctx);
3865 147 : }
3866 :
3867 36303 : void TLayout::layoutBaseMeasureBase(const MeasureBase* item, MeasureBase::LayoutData* ldata, const LayoutContext& ctx)
3868 : {
3869 36303 : LD_CONDITION(ldata->isSetBbox());
3870 :
3871 36303 : double x = ldata->bbox().width();
3872 43016 : for (EngravingItem* e : item->el()) {
3873 6713 : if (e->isLayoutBreak()) {
3874 2162 : TLayout::layoutItem(e, const_cast<LayoutContext&>(ctx));
3875 2162 : EngravingItem::LayoutData* eldata = e->mutldata();
3876 2162 : double spatium = item->spatium();
3877 2162 : double y = 0.0;
3878 2162 : if (toLayoutBreak(e)->isNoBreak()) {
3879 83 : x -= eldata->bbox().right() * .5;
3880 : } else {
3881 2079 : x -= eldata->bbox().right() + 0.5 * spatium;
3882 : }
3883 2162 : y = -2.5 * spatium;
3884 2162 : eldata->setPos(x, y);
3885 2162 : x += eldata->bbox().left() - 0.5 * spatium;
3886 4551 : } else if (e->isMarker() || e->isJump()) {
3887 : } else {
3888 4018 : layoutItem(e, const_cast<LayoutContext&>(ctx));
3889 : }
3890 : }
3891 36303 : }
3892 :
3893 3851 : void TLayout::layoutMeasureNumber(MeasureNumber* item, MeasureNumber::LayoutData* ldata, const LayoutContext& ctx)
3894 : {
3895 3851 : MeasureNumberLayout::layoutMeasureNumber(item, ldata, ctx);
3896 3851 : }
3897 :
3898 365 : void TLayout::layoutMeasureRepeat(const MeasureRepeat* item, MeasureRepeat::LayoutData* ldata, const LayoutContext& ctx)
3899 : {
3900 : LAYOUT_CALL_ITEM(item);
3901 :
3902 : //! NOTE The types are listed here explicitly to show what types there are (see Rest::add method)
3903 : //! and accordingly show what depends on.
3904 365 : for (EngravingItem* e : item->el()) {
3905 0 : switch (e->type()) {
3906 0 : case ElementType::DEAD_SLAPPED: {
3907 0 : DeadSlapped* ds = item_cast<DeadSlapped*>(e);
3908 : LD_INDEPENDENT;
3909 0 : layoutDeadSlapped(ds, ds->mutldata());
3910 0 : } break;
3911 0 : case ElementType::SYMBOL: {
3912 0 : Symbol* s = item_cast<Symbol*>(e);
3913 : // LD_X not clear yet
3914 0 : layoutSymbol(s, s->mutldata(), ctx);
3915 0 : } break;
3916 0 : case ElementType::IMAGE: {
3917 0 : Image* im = item_cast<Image*>(e);
3918 : LD_INDEPENDENT;
3919 0 : layoutImage(im, im->mutldata());
3920 0 : } break;
3921 0 : default:
3922 0 : UNREACHABLE;
3923 : }
3924 : }
3925 :
3926 365 : switch (item->numMeasures()) {
3927 276 : case 1:
3928 : {
3929 276 : ldata->setSymId(SymId::repeat1Bar);
3930 276 : if (ctx.conf().styleB(Sid::mrNumberSeries) && item->track() != muse::nidx) {
3931 0 : int placeInSeries = 2; // "1" would be the measure actually being repeated
3932 0 : staff_idx_t staffIdx = item->staffIdx();
3933 0 : const Measure* m = item->measure();
3934 0 : while (m && m->isOneMeasureRepeat(staffIdx) && m->prevIsOneMeasureRepeat(staffIdx)) {
3935 0 : placeInSeries++;
3936 0 : m = m->prevMeasure();
3937 : }
3938 0 : if (placeInSeries % ctx.conf().styleI(Sid::mrNumberEveryXMeasures) == 0) {
3939 0 : if (ctx.conf().styleB(Sid::mrNumberSeriesWithParentheses)) {
3940 0 : ldata->setNumberSym(String(u"(%1)").arg(placeInSeries));
3941 : } else {
3942 0 : ldata->setNumberSym(placeInSeries);
3943 : }
3944 : } else {
3945 0 : ldata->clearNumberSym();
3946 : }
3947 276 : } else if (ctx.conf().styleB(Sid::oneMeasureRepeatShow1)) {
3948 0 : ldata->setNumberSym(1);
3949 : } else {
3950 276 : ldata->clearNumberSym();
3951 : }
3952 276 : break;
3953 : }
3954 46 : case 2:
3955 46 : ldata->setSymId(SymId::repeat2Bars);
3956 46 : ldata->setNumberSym(item->numMeasures());
3957 46 : break;
3958 43 : case 4:
3959 43 : ldata->setSymId(SymId::repeat4Bars);
3960 43 : ldata->setNumberSym(item->numMeasures());
3961 43 : break;
3962 0 : default:
3963 0 : ldata->setSymId(SymId::noSym); // should never happen
3964 0 : ldata->clearNumberSym();
3965 0 : break;
3966 : }
3967 :
3968 365 : RectF bbox = item->symBbox(ldata->symId);
3969 :
3970 365 : if (item->track() != muse::nidx) { // if this is in score rather than a palette cell
3971 : // For unknown reasons, the symbol has some offset in almost all SMuFL fonts
3972 : // We compensate for it, to make sure the symbol is visually centered around the staff line
3973 365 : double offset = (-bbox.top() - bbox.bottom()) / 2.0;
3974 :
3975 365 : const StaffType* staffType = item->staffType();
3976 :
3977 : // Only need to set y position here; x position is handled in MeasureLayout::layoutMeasureElements()
3978 365 : ldata->setPos(0, std::floor(staffType->middleLine() / 2.0) * staffType->lineDistance().val() * item->spatium() + offset);
3979 : }
3980 :
3981 365 : ChordLayout::fillShape(item, ldata, ctx.conf());
3982 365 : }
3983 :
3984 0 : void TLayout::layoutMeasureRepeatExtender(const MeasureRepeat* item, MeasureRepeat::LayoutData* ldata, const LayoutContext& ctx)
3985 : {
3986 0 : ldata->extenderLineLeft = LineF();
3987 0 : ldata->extenderLineRight = LineF();
3988 :
3989 0 : Measure* parentMeasure = item->measure();
3990 0 : if (!parentMeasure) {
3991 0 : return;
3992 : }
3993 :
3994 0 : Measure* startMeasure = parentMeasure->prevMeasure();
3995 0 : if (!startMeasure || startMeasure->system() != parentMeasure->system()) {
3996 0 : return;
3997 : }
3998 :
3999 0 : Measure* endMeasure = parentMeasure->nextMeasure();
4000 0 : if (endMeasure) {
4001 0 : endMeasure = endMeasure->nextMeasure();
4002 : }
4003 0 : if (!endMeasure || endMeasure->system() != parentMeasure->system()) {
4004 0 : return;
4005 : }
4006 :
4007 : // Reuse the function that calculates start and end pos for mmRests,
4008 : // except start and end point are on different measures
4009 0 : staff_idx_t staffIdx = item->staffIdx();
4010 0 : double xStart = MeasureLayout::getMeasureStartEndPos(startMeasure, startMeasure->first(SegmentType::ChordRest), staffIdx,
4011 0 : /*needHeaderException*/ startMeasure->header(), /*modernMMrest*/ true, ctx).x1;
4012 0 : double xEnd = MeasureLayout::getMeasureStartEndPos(endMeasure, endMeasure->first(SegmentType::ChordRest), staffIdx,
4013 0 : /*needHeaderException*/ false, /*modernMRest*/ true, ctx).x2;
4014 :
4015 0 : double margin = ctx.conf().styleAbsolute(Sid::multiMeasureRestMargin);
4016 0 : xStart += margin;
4017 0 : xEnd -= margin;
4018 :
4019 0 : double xItemInSysCoords = item->x() + item->segment()->x() + parentMeasure->x();
4020 :
4021 0 : PointF startPoint(xStart + startMeasure->x() - xItemInSysCoords, 0.0);
4022 0 : PointF midPointL(-0.5 * item->spatium(), 0.0);
4023 0 : ldata->extenderLineLeft = LineF(startPoint, midPointL);
4024 :
4025 0 : PointF midPointR(item->symWidth(ldata->symId) + 0.5 * item->spatium(), 0.0);
4026 0 : PointF endPoint(xEnd + endMeasure->x() - xItemInSysCoords, 0.0);
4027 0 : ldata->extenderLineRight = LineF(midPointR, endPoint);
4028 : }
4029 :
4030 356 : void TLayout::layoutMMRest(const MMRest* item, MMRest::LayoutData* ldata, const LayoutContext& ctx)
4031 : {
4032 : LAYOUT_CALL_ITEM(item);
4033 :
4034 : //! NOTE The types are listed here explicitly to show what types there are (see Rest::add method)
4035 : //! and accordingly show what depends on.
4036 356 : for (EngravingItem* e : item->el()) {
4037 0 : switch (e->type()) {
4038 0 : case ElementType::DEAD_SLAPPED: {
4039 0 : DeadSlapped* ds = item_cast<DeadSlapped*>(e);
4040 : LD_INDEPENDENT;
4041 0 : layoutDeadSlapped(ds, ds->mutldata());
4042 0 : } break;
4043 0 : case ElementType::SYMBOL: {
4044 0 : Symbol* s = item_cast<Symbol*>(e);
4045 : // LD_X not clear yet
4046 0 : layoutSymbol(s, s->mutldata(), ctx);
4047 0 : } break;
4048 0 : case ElementType::IMAGE: {
4049 0 : Image* im = item_cast<Image*>(e);
4050 : LD_INDEPENDENT;
4051 0 : layoutImage(im, im->mutldata());
4052 0 : } break;
4053 0 : default:
4054 0 : UNREACHABLE;
4055 : }
4056 : }
4057 :
4058 356 : LD_CONDITION(ldata->restWidth.has_value());
4059 :
4060 : //! NOTE This is not look like layout data, perhaps this is should be set not here
4061 356 : ldata->number = item->measure()->mmRestCount();
4062 356 : ldata->setNumberSym(ldata->number);
4063 356 : ldata->yNumberPos = ctx.conf().styleAbsolute(Sid::mmRestNumberPos);
4064 :
4065 356 : const Staff* staff = item->staff();
4066 356 : if (staff->lines(item->tick()) == 1) {
4067 0 : ldata->yNumberPos = std::min(ldata->yNumberPos, -item->spatium());
4068 : }
4069 :
4070 356 : if (item->isOldStyle()) {
4071 66 : SymIdList restSyms;
4072 66 : double symsWidth = 0.0;
4073 :
4074 66 : int remaining = ldata->number;
4075 66 : double spacing = ctx.conf().styleAbsolute(Sid::mmRestOldStyleSpacing);
4076 : SymId sym;
4077 :
4078 132 : while (remaining > 0) {
4079 66 : if (remaining >= 4) {
4080 0 : sym = SymId::restLonga;
4081 0 : remaining -= 4;
4082 66 : } else if (remaining >= 2) {
4083 0 : sym = SymId::restDoubleWhole;
4084 0 : remaining -= 2;
4085 : } else {
4086 66 : sym = SymId::restWhole;
4087 66 : remaining -= 1;
4088 : }
4089 :
4090 66 : restSyms.push_back(sym);
4091 66 : symsWidth += item->symBbox(sym).width();
4092 :
4093 66 : if (remaining > 0) { // do not add spacing after last symbol
4094 0 : symsWidth += spacing;
4095 : }
4096 : }
4097 :
4098 66 : ldata->restSyms = restSyms;
4099 66 : ldata->symsWidth = symsWidth;
4100 66 : }
4101 :
4102 : // Only need to set y position here; x position is handled in MeasureLayout::layoutMeasureElements()
4103 356 : const StaffType* staffType = item->staffType();
4104 356 : double midline = staffType->middleLine() / 2.0;
4105 356 : if (item->isOldStyle()) {
4106 66 : midline = std::max(1.0, midline);
4107 : }
4108 356 : ldata->setPos(0, midline * staffType->lineDistance().val() * item->spatium());
4109 :
4110 356 : ChordLayout::fillShape(item, ldata, ctx.conf());
4111 356 : }
4112 :
4113 0 : void TLayout::layoutMMRestRange(MMRestRange* item, MMRestRange::LayoutData* ldata, const LayoutContext& ctx)
4114 : {
4115 0 : MeasureNumberLayout::layoutMMRestRange(item, ldata, ctx);
4116 0 : }
4117 :
4118 60053 : void TLayout::layoutNote(const Note* item, Note::LayoutData* ldata)
4119 : {
4120 : LAYOUT_CALL_ITEM(item);
4121 : LD_INDEPENDENT;
4122 :
4123 60053 : if (!ldata->isSetPos()) {
4124 734 : ldata->setPos(PointF());
4125 : }
4126 :
4127 60053 : bool useTablature = item->staff() && item->staff()->isTabStaff(item->chord()->tick());
4128 60053 : ldata->useTablature.set_value(useTablature);
4129 :
4130 60053 : RectF noteBBox;
4131 60053 : if (useTablature) {
4132 1355 : if (item->displayFret() == Note::DisplayFretOption::Hide) {
4133 0 : return;
4134 : }
4135 :
4136 1355 : const Staff* st = item->staff();
4137 1355 : const StaffType* tab = st->staffTypeForElement(item);
4138 : // not complete but we need systems to be laid out to add parenthesis
4139 1355 : if (item->fixed()) {
4140 0 : const_cast<Note*>(item)->setFretString(u"/");
4141 : } else {
4142 1355 : const_cast<Note*>(item)->setFretString(tab->fretString(std::fabs(item->fret()), item->string(), item->deadNote()));
4143 :
4144 1355 : if (item->negativeFretUsed() && !item->deadNote()) {
4145 40 : const_cast<Note*>(item)->setFretString(u"-" + item->fretString());
4146 : }
4147 :
4148 1355 : if (item->displayFret() == Note::DisplayFretOption::ArtificialHarmonic) {
4149 0 : const_cast<Note*>(item)->setFretString(String(u"%1 <%2>").arg(item->fretString(), String::number(item->harmonicFret())));
4150 1355 : } else if (item->displayFret() == Note::DisplayFretOption::NaturalHarmonic) {
4151 0 : const_cast<Note*>(item)->setFretString(String(u"<%1>").arg(String::number(item->harmonicFret())));
4152 : }
4153 : }
4154 :
4155 1355 : double w = item->tabHeadWidth(tab);
4156 1355 : double mags = item->magS();
4157 1355 : double y = item->deadNote() ? tab->deadFretBoxY() : tab->fretBoxY();
4158 1355 : double height = item->deadNote() ? tab->deadFretBoxH() : tab->fretBoxH();
4159 :
4160 1355 : noteBBox = RectF(0, y * mags, w, height * mags);
4161 : } else {
4162 58698 : if (item->deadNote()) {
4163 218 : const_cast<Note*>(item)->setHeadGroup(NoteHeadGroup::HEAD_CROSS);
4164 58480 : } else if (item->harmonic()) {
4165 74 : const_cast<Note*>(item)->setHeadGroup(NoteHeadGroup::HEAD_DIAMOND);
4166 : }
4167 :
4168 58698 : SymId nh = item->noteHead();
4169 58698 : ldata->cachedNoteheadSym.set_value(nh);
4170 :
4171 58698 : if (item->isNoteName()) {
4172 137 : ldata->cachedSymNull.set_value(SymId::noteEmptyBlack);
4173 137 : NoteHeadType ht = item->headType() == NoteHeadType::HEAD_AUTO ? item->chord()->durationType().headType() : item->headType();
4174 137 : if (ht == NoteHeadType::HEAD_WHOLE) {
4175 8 : ldata->cachedSymNull.set_value(SymId::noteEmptyWhole);
4176 129 : } else if (ht == NoteHeadType::HEAD_HALF) {
4177 0 : ldata->cachedSymNull.set_value(SymId::noteEmptyHalf);
4178 : }
4179 : } else {
4180 58561 : ldata->cachedSymNull.set_value(SymId::noSym);
4181 : }
4182 58698 : noteBBox = item->symBbox(nh);
4183 : }
4184 :
4185 60053 : ldata->setBbox(noteBBox);
4186 :
4187 60053 : fillNoteShape(item, ldata);
4188 : }
4189 :
4190 121109 : void TLayout::fillNoteShape(const Note* item, Note::LayoutData* ldata)
4191 : {
4192 : LAYOUT_CALL_ITEM(item);
4193 121109 : RectF noteBBox = ldata->bbox();
4194 :
4195 121109 : Shape shape(Shape::Type::Composite);
4196 121109 : shape.add(noteBBox, item);
4197 :
4198 123216 : for (const NoteDot* dot : item->dots()) {
4199 2107 : if (!dot->addToSkyline()) {
4200 4 : continue;
4201 : }
4202 2103 : shape.add(item->symBbox(SymId::augmentationDot).translated(dot->pos()), dot);
4203 : }
4204 :
4205 121109 : Accidental* acc = item->accidental();
4206 121109 : if (acc && acc->addToSkyline()) {
4207 8151 : shape.add(acc->ldata()->shape().translated(acc->pos()));
4208 : }
4209 121109 : const LaissezVib* lv = item->laissezVib();
4210 121109 : const LaissezVibSegment* lvSeg = lv && !lv->segmentsEmpty() ? lv->frontSegment() : nullptr;
4211 121109 : if (lvSeg && lvSeg->addToSkyline()) {
4212 108 : const PointF cornerClosestToNote = lv->up() ? lvSeg->shape().bbox().bottomLeft() : lvSeg->shape().bbox().topLeft();
4213 108 : const PointF pos = lvSeg->ldata()->posRelativeToNote.value() - cornerClosestToNote;
4214 108 : shape.add(lvSeg->shape().translate(pos));
4215 : }
4216 122040 : for (auto e : item->el()) {
4217 931 : if (e->addToSkyline()) {
4218 896 : if (e->isFingering() && toFingering(e)->layoutType() != ElementType::NOTE) {
4219 783 : continue;
4220 : }
4221 113 : shape.add(e->ldata()->bbox().translated(e->pos()), e);
4222 : }
4223 : }
4224 :
4225 : // This method is also called from SingleLayout, where `part` may be nullptr
4226 121109 : Part* part = item->part();
4227 121109 : if (part && part->instrument()->hasStrings()) {
4228 14459 : GuitarBend* bend = item->bendFor();
4229 14459 : if (bend && bend->addToSkyline() && bend->bendType() == GuitarBendType::SLIGHT_BEND && !bend->segmentsEmpty()) {
4230 70 : GuitarBendSegment* bendSeg = toGuitarBendSegment(bend->frontSegment());
4231 : // Semi-hack: the relative position of note and bend
4232 : // isn't fully known yet, so we use an approximation
4233 70 : double sp = item->spatium();
4234 70 : PointF approxRelPos(noteBBox.width() + 0.25 * sp, -0.25 * sp);
4235 70 : shape.add(bendSeg->shape().translate(approxRelPos));
4236 : }
4237 14459 : GuitarBend* bendBack = item->bendBack();
4238 1228 : if (bendBack && bendBack->bendType() == GuitarBendType::SCOOP && !bendBack->segmentsEmpty()
4239 15687 : && item->findAncestor(ElementType::SYSTEM)) {
4240 3 : GuitarBendSegment* seg = toGuitarBendSegment(bendBack->frontSegment());
4241 3 : shape.add(seg->shape().translated(seg->pos() - item->systemPos()));
4242 : }
4243 : }
4244 :
4245 121109 : ldata->setShape(shape);
4246 121109 : }
4247 :
4248 1215 : void TLayout::layoutNoteDot(const NoteDot* item, NoteDot::LayoutData* ldata)
4249 : {
4250 : LAYOUT_CALL_ITEM(item);
4251 : LD_INDEPENDENT;
4252 :
4253 1215 : if (ldata->isValid()) {
4254 59 : return;
4255 : }
4256 :
4257 1156 : ldata->setBbox(item->symBbox(SymId::augmentationDot));
4258 : }
4259 :
4260 751 : void TLayout::layoutOrnament(const Ornament* item, Ornament::LayoutData* ldata, const LayoutConfiguration& conf)
4261 : {
4262 : LAYOUT_CALL_ITEM(item);
4263 751 : LD_CONDITION(ldata->symId.has_value());
4264 :
4265 751 : ldata->setShape(Shape());
4266 :
4267 751 : layoutArticulation(const_cast<Ornament*>(item), ldata);
4268 :
4269 751 : double _spatium = item->spatium();
4270 751 : double vertMargin = 0.35 * _spatium;
4271 : static constexpr double ornamentAccidentalMag = 0.6; // TODO: style?
4272 :
4273 4506 : for (size_t i = 0; i < item->accidentalsAboveAndBelow().size(); ++i) {
4274 1502 : bool above = (i == 0);
4275 1502 : Accidental* accidental = item->accidentalsAboveAndBelow()[i];
4276 1502 : if (!accidental) {
4277 1434 : continue;
4278 : }
4279 68 : Accidental::LayoutData* accLData = accidental->mutldata();
4280 :
4281 68 : LD_CONDITION(accLData->isSetBbox());
4282 :
4283 68 : accidental->computeMag();
4284 :
4285 68 : accLData->setMag(accLData->mag() * ornamentAccidentalMag);
4286 68 : accLData->syms.clear(); // Invalidate
4287 68 : layoutAccidental(accidental, accLData, conf);
4288 68 : Shape accidentalShape = accidental->shape();
4289 : double minVertDist = above
4290 108 : ? accidentalShape.minVerticalDistance(Shape(ldata->bbox()))
4291 68 : : Shape(ldata->bbox()).minVerticalDistance(accidentalShape);
4292 :
4293 108 : accLData->setPos(-0.5 * accLData->bbox().width() + 0.5 * ldata->bbox().width(),
4294 40 : above ? (-minVertDist - vertMargin) : (minVertDist + vertMargin));
4295 68 : }
4296 :
4297 751 : Shape sh(Shape::Type::Composite);
4298 751 : sh.add(ldata->bbox(), item); // from Articulation
4299 2253 : for (const Accidental* accidental : item->accidentalsAboveAndBelow()) {
4300 1502 : if (accidental && accidental->visible()) {
4301 68 : LD_CONDITION(accidental->ldata()->isSetShape());
4302 68 : LD_CONDITION(accidental->ldata()->isSetPos());
4303 68 : sh.add(accidental->shape().translate(accidental->pos()));
4304 : }
4305 : }
4306 :
4307 751 : ldata->setShape(sh);
4308 751 : }
4309 :
4310 16 : void TLayout::layoutOrnamentCueNote(Ornament* item, LayoutContext& ctx)
4311 : {
4312 : LAYOUT_CALL_ITEM(item);
4313 16 : if (!item->ownershipParent()) {
4314 0 : return;
4315 : }
4316 :
4317 16 : Chord* parentChord = toChord(item->chordRest());
4318 16 : Chord* cueNoteChord = item->cueNoteChord();
4319 :
4320 16 : if (!cueNoteChord) {
4321 0 : return;
4322 : }
4323 :
4324 16 : const std::vector<Note*>& notes = cueNoteChord->notes();
4325 16 : Note* cueNote = notes.empty() ? nullptr : notes.front();
4326 :
4327 16 : if (!cueNote) {
4328 0 : return;
4329 : }
4330 :
4331 64 : ChordLayout::layoutChords3({ cueNoteChord }, { cueNote }, item->staff(), ctx);
4332 32 : ChordLayout::layoutLedgerLines({ cueNoteChord }, ctx);
4333 32 : AccidentalsLayout::layoutAccidentals({ cueNoteChord }, ctx);
4334 16 : layoutChord(cueNoteChord, ctx);
4335 :
4336 16 : Shape noteShape = cueNoteChord->shape();
4337 16 : Shape parentChordShape = parentChord->shape();
4338 16 : double minDist = HorizontalSpacing::minHorizontalDistance(parentChordShape, noteShape, parentChord->spatium());
4339 : // Check for possible other chords in same segment
4340 16 : staff_idx_t startStaff = staff2track(parentChord->staffIdx());
4341 80 : for (staff_idx_t staff = startStaff; staff < startStaff + VOICES; ++staff) {
4342 64 : Segment* segment = parentChord->segment();
4343 64 : ChordRest* cr = segment->element(staff) ? toChordRest(segment->element(staff)) : nullptr;
4344 64 : if (cr) {
4345 16 : minDist = std::max(minDist, HorizontalSpacing::minHorizontalDistance(cr->shape(), noteShape, cr->spatium()));
4346 : }
4347 : }
4348 16 : cueNoteChord->mutldata()->setPosX(minDist);
4349 16 : }
4350 :
4351 0 : void TLayout::layoutOttava(Ottava* item, LayoutContext& ctx)
4352 : {
4353 : LAYOUT_CALL_ITEM(item);
4354 0 : layoutLine(item, ctx);
4355 0 : }
4356 :
4357 161 : void TLayout::layoutOttavaSegment(OttavaSegment* item, LayoutContext& ctx)
4358 : {
4359 : LAYOUT_CALL_ITEM(item);
4360 161 : OttavaSegment::LayoutData* ldata = item->mutldata();
4361 :
4362 161 : layoutTextLineBaseSegment(item, ctx);
4363 :
4364 161 : Autoplace::autoplaceSpannerSegment(item, ldata);
4365 161 : }
4366 :
4367 56 : void TLayout::layoutPageLockIndicator(const PageLockIndicator* item, PageLockIndicator::LayoutData* ldata)
4368 : {
4369 56 : if (!item->configuration()->canLayoutIcons()) {
4370 56 : return;
4371 : }
4372 0 : const Page* page = item->page();
4373 0 : const System* sys = item->system();
4374 :
4375 0 : IF_ASSERT_FAILED(page && sys) {
4376 0 : return;
4377 : }
4378 :
4379 0 : Shape shape;
4380 :
4381 0 : const double spatium = item->spatium();
4382 :
4383 0 : const FontMetrics metrics(item->font());
4384 0 : const RectF iconBox = metrics.boundingRect(item->iconCode());
4385 0 : shape.add(iconBox, item);
4386 :
4387 0 : const double borderWidth = iconBox.height() + spatium;
4388 0 : const double borderPadding = 2 * spatium;
4389 0 : const double iconInset = (borderWidth - iconBox.width()) / 2;
4390 0 : PointF posOnPage = PointF(page->width() - borderPadding - iconBox.width() - iconBox.left() - iconInset,
4391 0 : page->height() - borderPadding - iconInset);
4392 0 : PointF posOnSystem = posOnPage - sys->pos();
4393 0 : ldata->setPos(posOnSystem);
4394 :
4395 0 : if (item->selected()) {
4396 : // Outer range rectangle
4397 0 : RectF rangeRect = page->shape().bbox();
4398 0 : PointF rectPagePos = rangeRect.topLeft() - ldata->pos() - sys->pos();
4399 0 : rangeRect.translate(rectPagePos);
4400 0 : rangeRect.adjust(borderPadding, borderPadding, -borderPadding, -borderPadding);
4401 0 : ldata->rangeRect = rangeRect;
4402 :
4403 : // Inner rectangle cutout
4404 : // Leaves border with rounded corners when painted
4405 0 : RectF innerRect = rangeRect.adjusted(borderWidth, borderWidth, -borderWidth, -borderWidth);
4406 0 : ldata->innerRangeRect = innerRect;
4407 0 : shape.add(RectF(PointF(rangeRect.left(), rangeRect.top()), PointF(rangeRect.right(), innerRect.top()))); // top
4408 0 : shape.add(RectF(PointF(rangeRect.left(), innerRect.bottom()), PointF(rangeRect.right(), rangeRect.bottom()))); // bottom
4409 0 : shape.add(RectF(PointF(rangeRect.left(), innerRect.top()), PointF(innerRect.left(), innerRect.bottom()))); // left
4410 0 : shape.add(RectF(PointF(innerRect.right(), innerRect.top()), PointF(rangeRect.right(), innerRect.bottom()))); // right
4411 : }
4412 :
4413 0 : ldata->setShape(shape);
4414 :
4415 : // Ensure it goes behind notation and LayoutBreak
4416 0 : const_cast<PageLockIndicator*>(item)->setZ(-100);
4417 0 : }
4418 :
4419 0 : void TLayout::layoutPalmMute(PalmMute* item, LayoutContext& ctx)
4420 : {
4421 : LAYOUT_CALL_ITEM(item);
4422 0 : layoutLine(item, ctx);
4423 0 : }
4424 :
4425 14 : void TLayout::layoutPalmMuteSegment(PalmMuteSegment* item, LayoutContext& ctx)
4426 : {
4427 : LAYOUT_CALL_ITEM(item);
4428 14 : PalmMuteSegment::LayoutData* ldata = item->mutldata();
4429 :
4430 14 : const StaffType* stType = item->staffType();
4431 :
4432 14 : if (stType && stType->isHiddenElementOnTab(Sid::palmMuteShowTabCommon, Sid::palmMuteShowTabSimple)) {
4433 0 : ldata->setIsSkipDraw(true);
4434 0 : return;
4435 : }
4436 14 : ldata->setIsSkipDraw(false);
4437 :
4438 14 : layoutTextLineBaseSegment(item, ctx);
4439 :
4440 14 : Autoplace::autoplaceSpannerSegment(item, ldata);
4441 : }
4442 :
4443 0 : void TLayout::layoutParenthesis(Parenthesis* item, Parenthesis::LayoutData* ldata, const LayoutContext& ctx)
4444 : {
4445 0 : ParenthesisLayout::layoutParenthesis(item, ldata, ctx);
4446 0 : }
4447 :
4448 0 : void TLayout::layoutPedal(Pedal* item, LayoutContext& ctx)
4449 : {
4450 : LAYOUT_CALL_ITEM(item);
4451 0 : layoutLine(item, ctx);
4452 0 : }
4453 :
4454 119 : void TLayout::layoutPedalSegment(PedalSegment* item, LayoutContext& ctx)
4455 : {
4456 : LAYOUT_CALL_ITEM(item);
4457 119 : PedalSegment::LayoutData* ldata = item->mutldata();
4458 :
4459 119 : layoutTextLineBaseSegment(item, ctx);
4460 :
4461 119 : Text* endText = item->endText();
4462 119 : if (endText && !endText->empty() && ldata->npoints > 0) { // Rosette
4463 11 : PointF endPoint = item->pos2();
4464 11 : double endTextWidth = endText->ldata()->bbox().width();
4465 11 : double xEndText = endPoint.x() - endTextWidth;
4466 11 : if (const Text* startText = item->text()) {
4467 11 : xEndText = std::max(xEndText, startText->ldata()->bbox().width());
4468 : }
4469 11 : endText->mutldata()->setPosX(xEndText);
4470 :
4471 11 : double lineTextGap = item->absoluteFromSpatium(item->getProperty(Pid::GAP_BETWEEN_TEXT_AND_LINE).value<Spatium>());
4472 11 : PointF& endOfLine = ldata->points.at(ldata->npoints - 1);
4473 11 : endOfLine.setX(xEndText - lineTextGap);
4474 :
4475 : // Recalculate shape after moving end text
4476 11 : Shape sh = recalculateTextLineBaseSegmentShape(item);
4477 11 : ldata->setShape(sh);
4478 11 : }
4479 :
4480 119 : Autoplace::autoplaceSpannerSegment(item, ldata);
4481 119 : }
4482 :
4483 2 : void TLayout::layoutPickScrapeSegment(PickScrapeSegment* item, LayoutContext& ctx)
4484 : {
4485 : LAYOUT_CALL_ITEM(item);
4486 2 : PickScrapeSegment::LayoutData* ldata = item->mutldata();
4487 2 : layoutTextLineBaseSegment(item, ctx);
4488 2 : Autoplace::autoplaceSpannerSegment(item, ldata);
4489 2 : }
4490 :
4491 6 : void TLayout::layoutPlayCountText(PlayCountText* item, TextBase::LayoutData* ldata)
4492 : {
4493 : LAYOUT_CALL_ITEM(item);
4494 6 : BarLine* bl = item->barline();
4495 6 : Segment* seg = item->segment();
4496 :
4497 6 : TextLayout::layoutBaseTextBase(item, ldata);
4498 :
4499 : // Avoid incoming barlines from above
4500 6 : double xAdj = 0.0;
4501 6 : BarLine* blAbove = item->staffIdx() != 0 ? toBarLine(seg->element(staff2track(item->staffIdx() - 1))) : nullptr;
4502 6 : if (blAbove && blAbove->spanStaff()) {
4503 0 : xAdj = blAbove->width();
4504 : }
4505 :
4506 6 : const double barlineWidth = bl->width();
4507 6 : const double diff = item->width() - barlineWidth + xAdj;
4508 6 : ldata->moveX(-diff);
4509 6 : }
4510 :
4511 87 : void TLayout::layoutPlayTechAnnotation(const PlayTechAnnotation* item, PlayTechAnnotation::LayoutData* ldata)
4512 : {
4513 : LAYOUT_CALL_ITEM(item);
4514 87 : TextLayout::layoutBaseTextBase(item, ldata);
4515 :
4516 87 : if (item->isHandbellsSymbol()) {
4517 25 : Chord* chord = nullptr;
4518 25 : Segment* seg = item->segment();
4519 25 : track_idx_t sTrack = staff2track(item->staffIdx());
4520 25 : track_idx_t eTrack = sTrack + VOICES;
4521 25 : for (track_idx_t track = sTrack; track < eTrack; ++track) {
4522 25 : if (seg->element(track) && seg->element(track)->isChord()) {
4523 25 : chord = toChord(seg->element(track));
4524 25 : break;
4525 : }
4526 : }
4527 25 : double center = 0.0;
4528 25 : if (chord) {
4529 25 : Note* refNote = item->placeAbove() ? chord->upNote() : chord->downNote();
4530 25 : center = 0.5 * refNote->width() + refNote->x() + chord->x();
4531 : } else {
4532 0 : center = 0.5 * item->score()->noteHeadWidth();
4533 : }
4534 25 : ldata->setPosX(center - 0.5 * (ldata->bbox().right() - ldata->bbox().left()));
4535 : }
4536 :
4537 87 : if (item->autoplace()) {
4538 87 : const Segment* s = toSegment(item->ownershipParent());
4539 87 : const Measure* m = s->measure();
4540 87 : LD_CONDITION(ldata->isSetPos());
4541 87 : LD_CONDITION(m->ldata()->isSetPos());
4542 87 : LD_CONDITION(s->ldata()->isSetPos());
4543 : }
4544 :
4545 87 : Autoplace::autoplaceSegmentElement(item, ldata);
4546 87 : }
4547 :
4548 4 : void TLayout::layoutRasgueadoSegment(RasgueadoSegment* item, LayoutContext& ctx)
4549 : {
4550 : LAYOUT_CALL_ITEM(item);
4551 4 : RasgueadoSegment::LayoutData* ldata = item->mutldata();
4552 4 : const StaffType* stType = item->staffType();
4553 :
4554 4 : if (stType && stType->isHiddenElementOnTab(Sid::rasgueadoShowTabCommon, Sid::rasgueadoShowTabSimple)) {
4555 0 : ldata->setIsSkipDraw(true);
4556 0 : return;
4557 : }
4558 4 : ldata->setIsSkipDraw(false);
4559 :
4560 4 : layoutTextLineBaseSegment(item, ctx);
4561 :
4562 4 : Autoplace::autoplaceSpannerSegment(item, ldata);
4563 : }
4564 :
4565 127 : void TLayout::layoutRehearsalMark(const RehearsalMark* item, RehearsalMark::LayoutData* ldata)
4566 : {
4567 : LAYOUT_CALL_ITEM(item);
4568 127 : TextLayout::layoutBaseTextBase(item, ldata);
4569 :
4570 127 : const Segment* s = item->segment();
4571 :
4572 127 : LD_CONDITION(s->ldata()->isSetPos());
4573 :
4574 127 : if (!s->rtick().isZero()) {
4575 27 : return;
4576 : }
4577 :
4578 : // first CR of measure, alignment is hcenter or right (the usual cases)
4579 : // align with barline, point just after header, or start of measure depending on context
4580 100 : const Measure* m = s->measure();
4581 100 : const Segment* header = s->prev(); // possibly just a start repeat
4582 100 : while (header && header->isType(SegmentType::Duration)) {
4583 0 : header = header->prev();
4584 : }
4585 100 : double measureX = -s->x();
4586 100 : const Segment* repeat = m->findSegmentR(SegmentType::StartRepeatBarLine, Fraction(0, 1));
4587 :
4588 100 : if (repeat) {
4589 0 : LD_CONDITION(repeat->ldata()->isSetPos());
4590 : }
4591 :
4592 100 : double barlineX = repeat ? repeat->x() - s->x() : measureX;
4593 100 : const System* sys = m->system();
4594 100 : bool systemFirst = (sys && m->isFirstInSystem());
4595 :
4596 100 : if (!header || repeat || !systemFirst) {
4597 : // no header, or header with repeat, or header mid-system - align with barline
4598 32 : ldata->setPosX(barlineX);
4599 32 : checkRehearsalMarkVSBigTimeSig(item, ldata);
4600 32 : return;
4601 : }
4602 :
4603 : // header at start of system
4604 : // align to a point just after the header
4605 68 : EngravingItem* e = header->element(item->track());
4606 :
4607 68 : if (e) {
4608 68 : LD_CONDITION(e->ldata()->isSetBbox());
4609 : }
4610 :
4611 68 : LD_CONDITION(header->ldata()->isSetBbox());
4612 :
4613 68 : double w = e ? e->ldata()->bbox().width() : header->ldata()->bbox().width();
4614 68 : ldata->setPosX(header->x() + w - s->x());
4615 :
4616 : // special case for right aligned rehearsal marks at start of system
4617 : // left align with start of measure if that is further left
4618 68 : if (item->position() == AlignH::RIGHT) {
4619 0 : ldata->setPosX(std::min(ldata->pos().x(), measureX + ldata->bbox().width()));
4620 : }
4621 :
4622 68 : checkRehearsalMarkVSBigTimeSig(item, ldata);
4623 : }
4624 :
4625 100 : void TLayout::checkRehearsalMarkVSBigTimeSig(const RehearsalMark* item, TextBase::LayoutData* ldata)
4626 : {
4627 100 : if (item->score()->style().styleV(Sid::timeSigPlacement).value<TimeSigPlacement>() != TimeSigPlacement::ABOVE_STAVES) {
4628 100 : return;
4629 : }
4630 :
4631 0 : const Segment* s = item->segment();
4632 0 : TimeSig* bigTimeSig = nullptr;
4633 0 : for (const Segment* segment = s; segment && segment->tick() == s->tick(); segment = segment->prevActive()) {
4634 0 : if (!segment->isType(SegmentType::TimeSigTypes)) {
4635 0 : continue;
4636 : }
4637 0 : TimeSig* timeSig = toTimeSig(segment->element(item->track()));
4638 0 : if (timeSig && timeSig->visible()) {
4639 0 : bigTimeSig = timeSig;
4640 0 : break;
4641 : }
4642 : }
4643 :
4644 0 : if (!bigTimeSig) {
4645 0 : return;
4646 : }
4647 :
4648 0 : double timeSigRightEdge = bigTimeSig->ldata()->bbox().translated(bigTimeSig->ldata()->pos()).right()
4649 0 : + bigTimeSig->segment()->x() - s->x();
4650 0 : double rehMarkLeftEdge = ldata->bbox().translated(item->ldata()->pos()).left();
4651 0 : double distance = rehMarkLeftEdge - timeSigRightEdge;
4652 0 : const double margin = 0.5 * item->fontMetrics().xHeight();
4653 0 : if (distance < margin) {
4654 0 : ldata->moveX(margin - distance);
4655 : }
4656 : }
4657 :
4658 66760 : void TLayout::layoutRest(const Rest* item, Rest::LayoutData* ldata, const LayoutContext& ctx)
4659 : {
4660 66760 : RestLayout::layoutRest(item, ldata, ctx);
4661 66760 : }
4662 :
4663 0 : void TLayout::layoutShadowNote(ShadowNote* item, LayoutContext& ctx)
4664 : {
4665 : LAYOUT_CALL_ITEM(item);
4666 0 : if (!item->isValid()) {
4667 0 : item->setbbox(RectF());
4668 0 : return;
4669 : }
4670 :
4671 0 : double _spatium = item->spatium();
4672 0 : double mag = item->mag();
4673 0 : RectF noteheadBbox = item->symBbox(item->noteheadSymbol());
4674 0 : bool up = item->computeUp();
4675 :
4676 : // Layout dots
4677 0 : double dotWidth = 0.0;
4678 0 : if (item->duration().dots() > 0) {
4679 0 : double noteheadWidth = noteheadBbox.width();
4680 0 : double d = ctx.conf().styleAbsolute(Sid::dotNoteDistance) * mag;
4681 0 : double dd = ctx.conf().styleAbsolute(Sid::dotDotDistance) * mag;
4682 0 : dotWidth = (noteheadWidth + d);
4683 0 : if (item->hasFlag() && up) {
4684 0 : dotWidth = std::max(dotWidth, noteheadWidth + item->symBbox(item->flagSym()).right());
4685 : }
4686 0 : for (int i = 0; i < item->duration().dots(); i++) {
4687 0 : dotWidth += dd * i;
4688 : }
4689 : }
4690 :
4691 0 : RectF newBbox;
4692 0 : newBbox.setRect(noteheadBbox.x(), noteheadBbox.y(), noteheadBbox.width() + dotWidth, noteheadBbox.height());
4693 :
4694 : // Layout stem and flag
4695 0 : if (item->hasStem()) {
4696 0 : double x = noteheadBbox.x();
4697 0 : double w = noteheadBbox.width();
4698 :
4699 0 : double stemWidth = ctx.conf().styleAbsolute(Sid::stemWidth);
4700 0 : double stemLength = (up ? -3.5 : 3.5) * _spatium;
4701 0 : double stemAnchor = item->symSmuflAnchor(item->noteheadSymbol(), up ? SmuflAnchorId::stemUpSE : SmuflAnchorId::stemDownNW).y();
4702 :
4703 0 : newBbox |= RectF(up ? x + w - stemWidth : x,
4704 : stemAnchor,
4705 : stemWidth,
4706 0 : stemLength - stemAnchor);
4707 :
4708 0 : if (item->hasFlag()) {
4709 0 : RectF flagBbox = item->symBbox(item->flagSym());
4710 0 : newBbox |= RectF(up ? x + w - stemWidth : x,
4711 0 : stemAnchor + stemLength + flagBbox.y(),
4712 : flagBbox.width(),
4713 0 : flagBbox.height());
4714 : }
4715 : }
4716 :
4717 0 : int lineIdx = item->lineIndex();
4718 :
4719 : // Layout ledger lines if needed
4720 0 : if (item->ledgerLinesVisible()) {
4721 0 : double extraLen = ctx.conf().styleAbsolute(Sid::ledgerLineLength) * mag;
4722 0 : double step = 0.5 * _spatium * item->staffType()->lineDistance().val();
4723 0 : double yOffset = item->staffOffsetY();
4724 0 : double x = noteheadBbox.x() - extraLen;
4725 0 : double w = noteheadBbox.width() + 2 * extraLen;
4726 :
4727 0 : double lw = ctx.conf().styleAbsolute(Sid::ledgerLineWidth);
4728 :
4729 0 : RectF r(x, -lw * .5, w, lw);
4730 0 : const int topLine = -2 + yOffset / step;
4731 0 : for (int i = topLine; i >= lineIdx; i -= 2) {
4732 0 : newBbox |= r.translated(PointF(0, step * (i - lineIdx)));
4733 : }
4734 0 : int l = item->staffType()->lines() * 2 + yOffset / step; // first ledger line below staff
4735 0 : for (int i = l; i <= lineIdx; i += 2) {
4736 0 : newBbox |= r.translated(PointF(0, step * (i - lineIdx)));
4737 : }
4738 : }
4739 :
4740 : // Layout accidental
4741 0 : SymId acc = Accidental::subtype2symbol(item->accidentalType());
4742 0 : if (acc != SymId::noSym) {
4743 0 : RectF symRect = item->symBbox(acc);
4744 0 : double accWidth = symRect.width() + ctx.conf().styleAbsolute(Sid::accidentalNoteDistance) * mag;
4745 0 : double dh = 0.0;
4746 :
4747 0 : if (symRect.y() < newBbox.y()) {
4748 0 : dh = symRect.height() - (newBbox.y() - symRect.y());
4749 0 : newBbox.setY(symRect.y());
4750 0 : } else if (symRect.y() > newBbox.y()) {
4751 0 : dh = newBbox.height() - (symRect.y() - newBbox.y());
4752 : }
4753 :
4754 0 : newBbox.setX(newBbox.x() - accWidth);
4755 0 : newBbox.setWidth(newBbox.width() + accWidth);
4756 0 : newBbox.setHeight(newBbox.height() + dh);
4757 : }
4758 :
4759 0 : const std::set<SymId>& articulationIds = item->articulationIds();
4760 0 : if (articulationIds.empty()) {
4761 0 : item->setbbox(newBbox);
4762 0 : return;
4763 : }
4764 :
4765 : // Layout articulations
4766 0 : bool distantArticulations = false;
4767 0 : double minDist = 0.4 * _spatium;
4768 0 : if ((!up && lineIdx > 1) || (up && lineIdx < item->staffType()->bottomLine() - 1)) {
4769 0 : minDist = _spatium;
4770 : }
4771 :
4772 : // Extend bounding box for nearby articulations
4773 0 : for (const SymId& artic: item->articulationIds()) {
4774 0 : String articName = Articulation::symId2ArticulationName(artic);
4775 0 : const double symH = item->symHeight(artic);
4776 0 : if (articName == u"staccato" || articName == u"tenuto") {
4777 0 : if (!up) {
4778 0 : newBbox.setTop(newBbox.y() - minDist - symH);
4779 : } else {
4780 0 : newBbox.setBottom(newBbox.bottom() + minDist + symH);
4781 : }
4782 : } else {
4783 0 : distantArticulations = true;
4784 : }
4785 0 : }
4786 :
4787 : // If there are any other articulations, extend bounding box to include whole staff
4788 : // And extend further to include those articulations
4789 0 : if (distantArticulations) {
4790 0 : minDist = 0.4 * _spatium;
4791 0 : const double staffTop = -_spatium * .5 * lineIdx;
4792 0 : const double staffBottom = -_spatium * .5 * (item->staffType()->bottomLine() - lineIdx);
4793 0 : if (newBbox.y() > staffTop) {
4794 0 : newBbox.setTop(staffTop);
4795 : }
4796 0 : if (newBbox.bottom() < staffBottom) {
4797 0 : newBbox.setBottom(staffBottom);
4798 : }
4799 :
4800 0 : for (const SymId& artic: item->articulationIds()) {
4801 0 : String articName = Articulation::symId2ArticulationName(artic);
4802 0 : const double symH = item->symHeight(artic);
4803 0 : bool isMarcato = Articulation::symId2ArticulationName(artic).contains(u"marcato");
4804 0 : bool isLv = artic == SymId::articLaissezVibrerAbove || artic == SymId::articLaissezVibrerBelow;
4805 0 : if (articName == u"staccato" || articName == u"tenuto") {
4806 0 : continue;
4807 : } else {
4808 0 : if (!up || isMarcato) {
4809 0 : newBbox.setTop(newBbox.y() - minDist - symH);
4810 : } else {
4811 0 : newBbox.setBottom(newBbox.bottom() + minDist + symH);
4812 : }
4813 : }
4814 0 : if (isLv) {
4815 0 : const double width = item->symWidth(artic) - item->symWidth(item->noteheadSymbol()) / 2;
4816 0 : newBbox.setWidth(newBbox.width() + width);
4817 : }
4818 0 : }
4819 : }
4820 :
4821 0 : item->setbbox(newBbox);
4822 : }
4823 :
4824 460 : void TLayout::layoutLine(SLine* item, LayoutContext& ctx)
4825 : {
4826 : LAYOUT_CALL_ITEM(item);
4827 460 : item->computeStartElement();
4828 460 : item->computeEndElement();
4829 :
4830 : System* s1;
4831 : System* s2;
4832 460 : PointF p1(item->linePos(Grip::START, &s1));
4833 460 : PointF p2(item->linePos(Grip::END, &s2));
4834 :
4835 460 : const std::vector<System*>& systems = ctx.dom().systems();
4836 460 : system_idx_t sysIdx1 = muse::indexOf(systems, s1);
4837 460 : system_idx_t sysIdx2 = muse::indexOf(systems, s2);
4838 460 : int segmentsNeeded = 0;
4839 :
4840 460 : if (sysIdx1 == muse::nidx || sysIdx2 == muse::nidx) {
4841 0 : return;
4842 : }
4843 :
4844 921 : for (system_idx_t i = sysIdx1; i <= sysIdx2; ++i) {
4845 461 : if (systems.at(i)->vbox()) {
4846 0 : continue;
4847 : }
4848 461 : ++segmentsNeeded;
4849 : }
4850 :
4851 460 : int segCount = int(item->spannerSegments().size());
4852 :
4853 460 : if (segmentsNeeded != segCount) {
4854 319 : item->fixupSegments(segmentsNeeded, [item]() { return item->createLineSegment(); });
4855 159 : if (segmentsNeeded > segCount) {
4856 319 : for (int i = segCount; i < segmentsNeeded; ++i) {
4857 160 : LineSegment* lineSegm = item->segmentAt(i);
4858 : // set user offset to previous segment's offset
4859 160 : if (segCount > 0) {
4860 0 : lineSegm->setOffset(PointF(0, item->segmentAt(i - 1)->offset().y()));
4861 : } else {
4862 160 : lineSegm->setOffset(PointF(0, item->offset().y()));
4863 : }
4864 : }
4865 : }
4866 : }
4867 :
4868 460 : int segIdx = 0;
4869 921 : for (system_idx_t i = sysIdx1; i <= sysIdx2; ++i) {
4870 461 : System* system = systems.at(i);
4871 461 : if (system->vbox()) {
4872 0 : continue;
4873 : }
4874 461 : LineSegment* lineSegm = item->segmentAt(segIdx++);
4875 461 : lineSegm->setTrack(item->track()); // DEBUG
4876 461 : lineSegm->moveToSystem(system);
4877 :
4878 461 : if (sysIdx1 == sysIdx2) {
4879 : // single segment
4880 459 : lineSegm->setSpannerSegmentType(SpannerSegmentType::SINGLE);
4881 459 : double len = p2.x() - p1.x();
4882 : // enforcing a minimum length would be possible but inadvisable
4883 : // the line length calculations are tuned well enough that this should not be needed
4884 : //if (anchor() == Anchor::SEGMENT && !isPedal())
4885 : // len = std::max(1.0 * spatium(), len);
4886 459 : lineSegm->setPos(p1);
4887 459 : lineSegm->setPos2(PointF(len, p2.y() - p1.y()));
4888 2 : } else if (i == sysIdx1) {
4889 : // start segment
4890 1 : lineSegm->setSpannerSegmentType(SpannerSegmentType::BEGIN);
4891 1 : lineSegm->setPos(p1);
4892 1 : double x2 = system->endingXForOpenEndedLines();
4893 1 : lineSegm->setPos2(PointF(x2 - p1.x(), 0.0));
4894 1 : } else if (i > 0 && i != sysIdx2) {
4895 : // middle segment
4896 0 : lineSegm->setSpannerSegmentType(SpannerSegmentType::MIDDLE);
4897 0 : double x1 = system->firstNoteRestSegmentX(true);
4898 0 : double x2 = system->endingXForOpenEndedLines();
4899 0 : lineSegm->setPos(PointF(x1, p1.y()));
4900 0 : lineSegm->setPos2(PointF(x2 - x1, 0.0));
4901 1 : } else if (i == sysIdx2) {
4902 : // end segment
4903 1 : double minLen = 0.0;
4904 1 : double x1 = system->firstNoteRestSegmentX(true);
4905 1 : double len = std::max(minLen, p2.x() - x1);
4906 1 : lineSegm->setSpannerSegmentType(SpannerSegmentType::END);
4907 1 : lineSegm->setPos(PointF(p2.x() - len, p2.y()));
4908 1 : lineSegm->setPos2(PointF(len, 0.0));
4909 : }
4910 461 : layoutLineSegment(lineSegm, ctx);
4911 : }
4912 : }
4913 :
4914 460 : void TLayout::layoutNoteAnchoredLine(SLine* item, EngravingItem::LayoutData* ldata, LayoutContext& ctx)
4915 : {
4916 460 : double _spatium = item->spatium();
4917 460 : Note* startAnchor = toNote(item->startElement());
4918 460 : Note* endAnchor = toNote(item->endElement());
4919 460 : Chord* startChord = startAnchor->chord();
4920 460 : Chord* endChord = endAnchor->chord();
4921 460 : LineSegment* startSeg = toLineSegment(item->frontSegment());
4922 460 : LineSegment* endSeg = toLineSegment(item->backSegment());
4923 :
4924 : // Note: line segments are defined by
4925 : // initial point: ipos() (relative to system origin)
4926 : // ending point: pos2() (relative to initial point)
4927 :
4928 : // LINE ENDING POINTS TO NOTEHEAD CENTRES
4929 :
4930 : // assume line goes from centre of initial note centre to centre of ending note:
4931 : // move first segment origin and last segment ending point from notehead origin to notehead centre
4932 : // For TAB: begin at the right-edge of initial note rather than centre
4933 460 : PointF startOffset = (startChord->staff()->isTabStaff(startChord->tick()))
4934 460 : ? PointF(startAnchor->ldata()->bbox().right(), 0.0)
4935 460 : : PointF(startAnchor->headWidth() * 0.5, 0.0);
4936 :
4937 460 : PointF endOffset = PointF(endAnchor->headWidth() * 0.5, 0.0);
4938 :
4939 : // AVOID HORIZONTAL LINES
4940 :
4941 : // for microtonality read tuning, or check note accidental
4942 460 : double startTune = startAnchor->tuning();
4943 460 : double endTune = endAnchor->tuning();
4944 460 : AccidentalType startAcc = startAnchor->accidentalType();
4945 460 : AccidentalType endAcc = endAnchor->accidentalType();
4946 460 : if (muse::RealIsNull(startTune) && Accidental::isMicrotonal(startAcc)) {
4947 0 : startTune = Accidental::subtype2centOffset(startAcc);
4948 : }
4949 460 : if (muse::RealIsNull(endTune) && Accidental::isMicrotonal(endAcc)) {
4950 0 : endTune = Accidental::subtype2centOffset(endAcc);
4951 : }
4952 :
4953 460 : int upDown = (0 < (endAnchor->ppitch() - startAnchor->ppitch())) - ((endAnchor->ppitch() - startAnchor->ppitch()) < 0);
4954 : // same note, so compare tunings
4955 460 : if (upDown == 0) {
4956 3 : upDown = (0 < (endTune - startTune)) - ((endTune - startTune) < 0);
4957 : }
4958 :
4959 : // on TAB's, glissando are by necessity on the same string, this gives an horizontal glissando line;
4960 : // make bottom end point lower and top ending point higher
4961 460 : if (startChord->staff()->isTabStaff(startChord->tick())) {
4962 15 : double yOff = startChord->staff()->lineDistance(startChord->tick()) * 0.4 * _spatium;
4963 15 : startOffset.ry() += yOff * upDown;
4964 15 : endOffset.ry() -= yOff * upDown;
4965 : }
4966 : // if not TAB, angle glissando between notes on the same line
4967 : else {
4968 445 : if (startAnchor->line() == endAnchor->line()) {
4969 3 : startOffset.ry() += _spatium * 0.25 * upDown;
4970 3 : endOffset.ry() -= _spatium * 0.25 * upDown;
4971 : }
4972 : }
4973 :
4974 : // move initial point of first segment and adjust its length accordingly
4975 460 : startSeg->setPos(startSeg->ldata()->pos() + startOffset);
4976 460 : startSeg->setPos2(startSeg->ipos2() - startOffset);
4977 : // adjust ending point of last segment
4978 460 : endSeg->setPos2(endSeg->ipos2() + endOffset);
4979 :
4980 : // INTERPOLATION OF INTERMEDIATE POINTS
4981 :
4982 : // get total x-width and total y-height of all segments
4983 460 : double xTot = 0.0;
4984 921 : for (SpannerSegment* segm : item->spannerSegments()) {
4985 461 : xTot += segm->ipos2().x();
4986 : }
4987 460 : double startY = startSeg->ldata()->pos().y();
4988 460 : double yTot = endSeg->ldata()->pos().y() + endSeg->ipos2().y() - startY;
4989 460 : yTot -= yStaffDifference(endSeg->system(), startSeg->system(), track2staff(item->track2()));
4990 460 : double ratio = muse::divide(yTot, xTot, 1.0);
4991 : // interpolate y-coord of intermediate points across total width and height
4992 460 : double xCurr = 0.0;
4993 : double yCurr;
4994 461 : for (unsigned i = 0; i + 1 < item->spannerSegments().size(); i++) {
4995 1 : SpannerSegment* segm = item->segmentAt(i);
4996 1 : xCurr += segm->ipos2().x();
4997 1 : yCurr = startY + ratio * xCurr;
4998 1 : segm->rypos2() = yCurr - segm->ldata()->pos().y(); // position segm. end point at yCurr
4999 : // next segment shall start where this segment stopped
5000 1 : SpannerSegment* nextSeg = item->segmentAt(i + 1);
5001 1 : yCurr += yStaffDifference(nextSeg->system(), segm->system(), track2staff(item->track2()));
5002 1 : segm = nextSeg;
5003 1 : segm->rypos2() += segm->ldata()->pos().y() - yCurr; // adjust next segm. vertical length
5004 1 : segm->mutldata()->setPosY(yCurr); // position next segm. start point at yCurr
5005 : }
5006 :
5007 : // KEEP CLEAR OF ALL ELEMENTS OF THE CHORD
5008 : // Remove offset already applied
5009 460 : startOffset *= -1.0;
5010 460 : endOffset *= -1.0;
5011 : // Look at chord shapes (but don't consider lyrics)
5012 460 : Shape startCRShape = startChord->shape();
5013 460 : startCRShape.remove_if([](ShapeElement& s) {
5014 1099 : if (!s.item() || s.item()->isLyrics()) {
5015 6 : return true;
5016 : } else {
5017 1093 : return false;
5018 : }
5019 : });
5020 :
5021 460 : double startYAbove = startAnchor->ldata()->pos().y() + startAnchor->ldata()->bbox().topRight().y();
5022 460 : double startYBelow = startYAbove + startAnchor->ldata()->bbox().height();
5023 460 : startOffset.rx() += startCRShape.rightMostEdgeAtHeight(startYAbove, startYBelow) - startAnchor->pos().x();
5024 460 : if (!endChord->staff()->isTabStaff(endChord->tick())) {
5025 445 : double endYAbove = endAnchor->ldata()->pos().y() + endAnchor->ldata()->bbox().topLeft().y();
5026 445 : double endYBelow = endYAbove + endAnchor->ldata()->bbox().height();
5027 445 : double noteMiddle = endYAbove + endAnchor->ldata()->bbox().height() / 2;
5028 445 : if (upDown != 0) {
5029 442 : int llWidth = ctx.conf().styleS(Sid::ledgerLineWidth).val() * _spatium;
5030 : // Only check top/bottom half of note depending on line approach direction
5031 : // to avoid clearing acidentals the line won't collide with
5032 442 : endYAbove = upDown == 1 ? noteMiddle - llWidth : endYAbove;
5033 442 : endYBelow = upDown == 1 ? endYBelow : noteMiddle + llWidth;
5034 : }
5035 :
5036 445 : endOffset.rx() -= endAnchor->pos().x() - endChord->shape().leftMostEdgeAtHeight(endYAbove, endYBelow);
5037 : }
5038 : // Add note distance
5039 460 : const double lineNoteDist = 0.25 * item->spatium(); // TODO: style
5040 460 : startOffset.rx() += lineNoteDist;
5041 460 : endOffset.rx() -= lineNoteDist;
5042 :
5043 : // apply offsets: shorten first segment by x1 (and proportionally y) and adjust its length accordingly
5044 460 : startOffset.ry() = startSeg->ipos2().y() * muse::divide(startOffset.x(), startSeg->ipos2().x(), 1.0);
5045 460 : startSeg->setPos(startSeg->ldata()->pos() + startOffset);
5046 460 : startSeg->setPos2(startSeg->ipos2() - startOffset);
5047 : // adjust last segment length by x2 (and proportionally y)
5048 460 : endOffset.ry() = endSeg->ipos2().y() * muse::divide(endOffset.x(), endSeg->ipos2().x(), 1.0);
5049 460 : endSeg->setPos2(endSeg->ipos2() + endOffset);
5050 :
5051 921 : for (SpannerSegment* segm : item->spannerSegments()) {
5052 461 : TLayout::layoutItem(segm, ctx);
5053 : }
5054 :
5055 : // compute line bbox as the bbox of the last segment, relative to the end anchor note
5056 460 : PointF endAnchorPagePos = endAnchor->pagePos();
5057 460 : PointF endSystemPagePos;
5058 460 : IF_ASSERT_FAILED(endChord->segment()->system()) {
5059 0 : endSystemPagePos = endSeg->pos();
5060 : } else {
5061 460 : endSystemPagePos = endChord->segment()->system()->pagePos();
5062 : }
5063 :
5064 460 : PointF endAnchorSystPos = endAnchorPagePos - endSystemPagePos;
5065 460 : RectF r = RectF(endAnchorSystPos - endSeg->pos(), endAnchorSystPos - endSeg->pos() - endSeg->pos2()).normalized();
5066 460 : double lw = item->absoluteFromSpatium(item->lineWidth()) * .5;
5067 460 : ldata->setBbox(r.adjusted(-lw, -lw, lw, lw));
5068 460 : }
5069 :
5070 6 : void TLayout::layoutNoteLine(NoteLine* item, LayoutContext& ctx)
5071 : {
5072 : LAYOUT_CALL_ITEM(item);
5073 :
5074 6 : if (item->startElement()) {
5075 6 : item->setTick(item->startElement()->tick());
5076 : }
5077 :
5078 6 : if (item->endElement()) {
5079 6 : item->setTick2(item->endElement()->tick());
5080 : }
5081 :
5082 6 : TLayout::layoutLine(item, ctx);
5083 :
5084 6 : if (item->lineEndPlacement() == NoteLineEndPlacement::OFFSET_ENDS) {
5085 6 : layoutNoteAnchoredLine(item, item->mutldata(), ctx);
5086 6 : ChordLayout::addLineAttachPoints(item);
5087 : }
5088 6 : }
5089 :
5090 12 : void TLayout::layoutNoteLineSegment(NoteLineSegment* item, LayoutContext& ctx)
5091 : {
5092 : LAYOUT_CALL_ITEM(item);
5093 :
5094 12 : NoteLineSegment::LayoutData* ldata = item->mutldata();
5095 12 : layoutTextLineBaseSegment(item, ctx);
5096 :
5097 12 : if (item->staff()) {
5098 12 : ldata->setMag(item->staff()->staffMag(item->tick()));
5099 : }
5100 12 : }
5101 :
5102 0 : void TLayout::layoutSlur(Slur* item, LayoutContext& ctx)
5103 : {
5104 0 : SlurTieLayout::createSlurSegments(item, ctx);
5105 0 : }
5106 :
5107 2 : void TLayout::layoutSpacer(Spacer* item, LayoutContext&)
5108 : {
5109 : LAYOUT_CALL_ITEM(item);
5110 2 : Spacer::LayoutData* ldata = item->mutldata();
5111 :
5112 2 : double spatium = item->spatium();
5113 :
5114 2 : PainterPath path = PainterPath();
5115 2 : double w = spatium;
5116 2 : double b = w * .5;
5117 2 : double h = item->ownershipParent() ? item->absoluteGap() : item->absoluteFromSpatium(std::min(item->gap(), 4.0_sp)); // limit length for palette
5118 :
5119 2 : switch (item->spacerType()) {
5120 0 : case SpacerType::DOWN:
5121 0 : path.lineTo(w, 0.0);
5122 0 : path.moveTo(b, 0.0);
5123 0 : path.lineTo(b, h);
5124 0 : path.lineTo(0.0, h - b);
5125 0 : path.moveTo(b, h);
5126 0 : path.lineTo(w, h - b);
5127 0 : break;
5128 0 : case SpacerType::UP:
5129 0 : path.moveTo(b, 0.0);
5130 0 : path.lineTo(0.0, b);
5131 0 : path.moveTo(b, 0.0);
5132 0 : path.lineTo(w, b);
5133 0 : path.moveTo(b, 0.0);
5134 0 : path.lineTo(b, h);
5135 0 : path.moveTo(0.0, h);
5136 0 : path.lineTo(w, h);
5137 0 : break;
5138 2 : case SpacerType::FIXED:
5139 2 : path.lineTo(w, 0.0);
5140 2 : path.moveTo(b, 0.0);
5141 2 : path.lineTo(b, h);
5142 2 : path.moveTo(0.0, h);
5143 2 : path.lineTo(w, h);
5144 2 : break;
5145 : }
5146 2 : ldata->path = path;
5147 2 : double lw = spatium * 0.4;
5148 2 : RectF bb(0, 0, w, h);
5149 2 : bb.adjust(-lw, -lw, lw, lw);
5150 2 : ldata->setBbox(bb);
5151 :
5152 2 : item->setZ(0.0);
5153 2 : }
5154 :
5155 646 : void TLayout::layoutSpanner(Spanner* item, LayoutContext& ctx)
5156 : {
5157 : LAYOUT_CALL_ITEM(item);
5158 646 : layoutItem(item, ctx);
5159 646 : }
5160 :
5161 44190 : void TLayout::layoutStaffLines(StaffLines* item, LayoutContext& ctx)
5162 : {
5163 : LAYOUT_CALL_ITEM(item);
5164 44190 : layoutForWidth(item, item->measure()->width(), ctx);
5165 44190 : }
5166 :
5167 44190 : void TLayout::layoutForWidth(StaffLines* item, double w, LayoutContext& ctx)
5168 : {
5169 : LAYOUT_CALL_ITEM(item);
5170 44190 : StaffLines::LayoutData* ldata = item->mutldata();
5171 44190 : const Staff* s = item->staff();
5172 44190 : double _spatium = item->spatium();
5173 44190 : double dist = _spatium;
5174 44190 : item->setPos(PointF(0.0, 0.0));
5175 : int _lines;
5176 44190 : if (s) {
5177 44190 : ldata->setMag(s->staffMag(item->measure()->tick()));
5178 44190 : item->setVisible(!s->isLinesInvisible(item->measure()->tick()));
5179 44190 : item->setColor(s->color(item->measure()->tick()));
5180 44190 : const StaffType* st = s->staffType(item->measure()->tick());
5181 44190 : dist *= st->lineDistance().val();
5182 44190 : _lines = st->lines();
5183 44190 : ldata->setPosY(st->yoffset().val() * _spatium);
5184 : // if (_lines == 1)
5185 : // rypos() = 2 * _spatium;
5186 : } else {
5187 0 : _lines = 5;
5188 0 : item->setColor(item->configuration()->defaultColor());
5189 : }
5190 44190 : item->setLw(ctx.conf().styleS(Sid::staffLineWidth).val() * _spatium);
5191 44190 : double x1 = item->pos().x();
5192 44190 : double x2 = x1 + w;
5193 44190 : double y = item->pos().y();
5194 44190 : ldata->setBbox(x1, -item->lw() * .5 + y, w, (_lines - 1) * dist + item->lw());
5195 :
5196 44190 : std::vector<LineF> ll;
5197 265722 : for (int i = 0; i < _lines; ++i) {
5198 221532 : ll.push_back(LineF(x1, y, x2, y));
5199 221532 : y += dist;
5200 : }
5201 44190 : item->setLines(ll);
5202 44190 : }
5203 :
5204 0 : void TLayout::layoutStaffState(const StaffState* item, StaffState::LayoutData* ldata)
5205 : {
5206 : LAYOUT_CALL_ITEM(item);
5207 : LD_INDEPENDENT;
5208 :
5209 0 : if (ldata->isValid()) {
5210 0 : return;
5211 : }
5212 :
5213 0 : double _spatium = item->spatium();
5214 0 : ldata->lw = (_spatium * 0.3);
5215 0 : double h = _spatium * 4;
5216 0 : double w = _spatium * 2.5;
5217 : // double w1 = w * .6;
5218 :
5219 0 : PainterPath path;
5220 0 : switch (item->staffStateType()) {
5221 0 : case StaffStateType::INSTRUMENT:
5222 0 : path.lineTo(w, 0.0);
5223 0 : path.lineTo(w, h);
5224 0 : path.lineTo(0.0, h);
5225 0 : path.lineTo(0.0, 0.0);
5226 0 : path.moveTo(w * .5, h - _spatium * .5);
5227 0 : path.lineTo(w * .5, _spatium * 2);
5228 0 : path.moveTo(w * .5, _spatium * .8);
5229 0 : path.lineTo(w * .5, _spatium * 1.0);
5230 0 : break;
5231 :
5232 0 : case StaffStateType::TYPE:
5233 0 : path.lineTo(w, 0.0);
5234 0 : path.lineTo(w, h);
5235 0 : path.lineTo(0.0, h);
5236 0 : path.lineTo(0.0, 0.0);
5237 0 : break;
5238 :
5239 0 : case StaffStateType::VISIBLE:
5240 0 : path.lineTo(w, 0.0);
5241 0 : path.lineTo(w, h);
5242 0 : path.lineTo(0.0, h);
5243 0 : path.lineTo(0.0, 0.0);
5244 0 : break;
5245 :
5246 0 : case StaffStateType::INVISIBLE:
5247 0 : path.lineTo(w, 0.0);
5248 0 : path.lineTo(w, h);
5249 0 : path.lineTo(0.0, h);
5250 0 : path.lineTo(0.0, 0.0);
5251 0 : break;
5252 :
5253 0 : default:
5254 0 : LOGD("unknown layout break symbol");
5255 0 : break;
5256 : }
5257 :
5258 0 : ldata->path = path;
5259 :
5260 0 : RectF bb(0, 0, w, h);
5261 0 : bb.adjust(-ldata->lw, -ldata->lw, ldata->lw, ldata->lw);
5262 0 : ldata->setBbox(bb);
5263 0 : ldata->setPos(0.0, _spatium * -6.0);
5264 0 : }
5265 :
5266 1133 : void TLayout::layoutStaffText(const StaffText* item, StaffText::LayoutData* ldata)
5267 : {
5268 : LAYOUT_CALL_ITEM(item);
5269 1133 : TextLayout::layoutBaseTextBase(item, ldata);
5270 :
5271 1133 : if (item->autoplace()) {
5272 1133 : const Segment* s = toSegment(item->ownershipParent());
5273 1133 : const Measure* m = s->measure();
5274 1133 : LD_CONDITION(ldata->isSetPos());
5275 1133 : LD_CONDITION(m->ldata()->isSetPos());
5276 1133 : LD_CONDITION(s->ldata()->isSetPos());
5277 : }
5278 :
5279 1133 : Autoplace::autoplaceSegmentElement(item, ldata);
5280 :
5281 1133 : if (SoundFlag* flag = item->soundFlag()) {
5282 9 : layoutSoundFlag(flag, flag->mutldata());
5283 : }
5284 1133 : }
5285 :
5286 6 : void TLayout::layoutStaveSharingLabel(const StaveSharingLabel* item, TextBase::LayoutData* ldata)
5287 : {
5288 : LAYOUT_CALL_ITEM(item);
5289 6 : TextLayout::layoutBaseTextBase(item, ldata);
5290 6 : Autoplace::autoplaceSegmentElement(item, ldata);
5291 6 : }
5292 :
5293 19 : void TLayout::layoutStaffTypeChange(const StaffTypeChange* item, StaffTypeChange::LayoutData* ldata, const LayoutConfiguration& conf)
5294 : {
5295 : LAYOUT_CALL_ITEM(item);
5296 : LD_INDEPENDENT;
5297 :
5298 19 : double spatium = conf.spatium();
5299 19 : ldata->setBbox(RectF(-item->lw() * .5, -item->lw() * .5, spatium * 2.5 + item->lw(), spatium * 2.5 + item->lw()));
5300 19 : if (item->measure()) {
5301 19 : double y = -1.5 * spatium - ldata->bbox().height() + item->measure()->system()->staff(item->staffIdx())->y();
5302 19 : ldata->setPos(spatium * .8, y);
5303 : } else {
5304 0 : ldata->setPos(0.0, 0.0);
5305 : }
5306 19 : }
5307 :
5308 216469 : void TLayout::layoutStem(const Stem* item, Stem::LayoutData* ldata, const LayoutConfiguration& conf)
5309 : {
5310 : LAYOUT_CALL_ITEM(item);
5311 :
5312 216469 : const bool up = item->up();
5313 216469 : const double _up = up ? -1.0 : 1.0;
5314 :
5315 : //Note* note = up ? item->chord()->downNote() : item->chord()->upNote();
5316 : //! NOTE A lot of spam
5317 : // LD_CONDITION(note->ldata()->mirror.has_value());
5318 :
5319 216469 : double y1 = 0.0; // vertical displacement to match note attach point
5320 216469 : double y2 = _up * (item->length());
5321 :
5322 216469 : bool isTabStaff = false;
5323 216469 : if (item->chord()) {
5324 216469 : ldata->setMag(item->chord()->mag());
5325 :
5326 216469 : const Staff* staff = item->staff();
5327 216469 : const StaffType* staffType = staff ? staff->staffTypeForElement(item->chord()) : nullptr;
5328 216469 : isTabStaff = staffType && staffType->isTabStaff();
5329 :
5330 216469 : if (isTabStaff) {
5331 493 : if (staffType->stemThrough()) {
5332 : // if stems through staves, gets Y pos. of stem-side note relative to chord other side
5333 105 : const double staffLinesDistance = staffType->lineDistance().val() * item->spatium();
5334 105 : y1 = (item->chord()->downString() - item->chord()->upString()) * _up * staffLinesDistance;
5335 :
5336 : // if fret marks above lines, raise stem beginning by 1/2 line distance
5337 105 : if (!staffType->onLines()) {
5338 0 : y1 -= staffLinesDistance * 0.5;
5339 : }
5340 :
5341 : // shorten stem by 1/2 lineDist to clear the note and a little more to keep 'air' between stem and note
5342 105 : y1 += _up * staffLinesDistance * 0.7;
5343 : }
5344 : // in other TAB types, no correction
5345 : } else { // non-TAB
5346 : // move stem start to note attach point
5347 215976 : Note* note = up ? item->chord()->downNote() : item->chord()->upNote();
5348 215976 : if ((up && !note->ldata()->mirror.value(LD_ACCESS::BAD)) || (!up && note->ldata()->mirror.value(LD_ACCESS::BAD))) {
5349 112753 : y1 = note->stemUpSE().y();
5350 : } else {
5351 103223 : y1 = note->stemDownNW().y();
5352 : }
5353 :
5354 215976 : ldata->setPosY(note->ldata()->pos().y());
5355 : }
5356 :
5357 216469 : const Hook* hook = item->chord()->hook();
5358 216469 : const Beam* beam = item->chord()->beam();
5359 216469 : if (hook && !beam) {
5360 6676 : y2 += hook->smuflAnchor().y();
5361 : }
5362 :
5363 216469 : if (beam) {
5364 115255 : y2 -= _up * beam->absoluteFromSpatium(conf.styleS(Sid::beamWidth)) * .5 * beam->mag();
5365 : }
5366 : }
5367 :
5368 216469 : double lineWidthCorrection = item->lineWidthMag() * 0.5;
5369 216469 : double lineX = isTabStaff ? 0.0 : _up * lineWidthCorrection;
5370 :
5371 216469 : LineF line = LineF(lineX, y1, lineX, y2);
5372 216469 : ldata->line = line;
5373 :
5374 : // compute line and bounding rectangle
5375 216469 : RectF rect(line.p1(), line.p2() + PointF(0.0, ldata->beamCorrection));
5376 216469 : ldata->setBbox(rect.normalized().adjusted(-lineWidthCorrection, 0, lineWidthCorrection, 0));
5377 216469 : }
5378 :
5379 841 : void TLayout::layoutStemSlash(const StemSlash* item, StemSlash::LayoutData* ldata, const LayoutConfiguration& conf)
5380 : {
5381 : LAYOUT_CALL_ITEM(item);
5382 841 : IF_ASSERT_FAILED(item->ownershipParent()) {
5383 0 : return;
5384 : }
5385 :
5386 841 : const Chord* c = item->chord();
5387 841 : const Stem* stem = c->stem();
5388 841 : const Hook* hook = c->hook();
5389 841 : const Beam* beam = c->beam();
5390 :
5391 841 : LD_CONDITION(stem->ldata()->isSetPos());
5392 841 : LD_CONDITION(stem->ldata()->isSetBbox());
5393 : // Notes without a stem (including whole notes) don't draw a slash.
5394 :
5395 : static constexpr double heightReduction = 0.66;
5396 : static constexpr double angleIncrease = 1.2;
5397 : static constexpr double lengthIncrease = 1.1;
5398 841 : const double mag = c->mag();
5399 :
5400 841 : double up = c->up() ? -1 : 1;
5401 841 : double stemTipY = c->up()
5402 841 : ? stem->ldata()->bbox().translated(stem->pos()).top()
5403 9 : : stem->ldata()->bbox().translated(stem->pos()).bottom();
5404 841 : double leftHang = conf.noteHeadWidth() * mag / 2;
5405 841 : double angle = conf.styleD(Sid::stemSlashAngle) * M_PI / 180; // converting to radians
5406 841 : bool straight = conf.styleB(Sid::useStraightNoteFlags);
5407 841 : double graceNoteMag = mag;
5408 :
5409 841 : double startX = stem->ldata()->bbox().translated(stem->pos()).right() - leftHang;
5410 841 : double startY = stemTipY - up * graceNoteMag * conf.styleAbsolute(Sid::stemSlashPosition)
5411 841 : * (straight || !hook ? heightReduction : 1);
5412 :
5413 841 : double endX = 0;
5414 841 : double endY = 0;
5415 :
5416 841 : if (hook) {
5417 472 : auto musicFont = conf.styleSt(Sid::musicalSymbolFont);
5418 : // HACK: adjust slash angle for fonts with "fat" hooks. In future, we must use smufl cutOut
5419 507 : if (c->beams() >= 2 && !straight
5420 507 : && (musicFont == "Bravura" || musicFont == "Finale Maestro" || musicFont == "Gonville")) {
5421 0 : angle *= angleIncrease;
5422 : }
5423 472 : endX = hook->ldata()->bbox().translated(hook->ldata()->pos()).right(); // always ends at the right bbox margin of the hook
5424 472 : endY = startY + up * (endX - startX) * tan(angle);
5425 841 : } else if (beam) {
5426 225 : PointF p1 = beam->startAnchor();
5427 225 : PointF p2 = beam->endAnchor();
5428 225 : double beamAngle = p2.x() > p1.x() ? atan((p2.y() - p1.y()) / (p2.x() - p1.x())) : 0;
5429 225 : angle += up * beamAngle / 2;
5430 225 : double length = 2 * item->spatium();
5431 225 : bool obtuseAngle = (c->up() && beamAngle < 0) || (!c->up() && beamAngle > 0);
5432 225 : if (obtuseAngle) {
5433 120 : length *= lengthIncrease; // needs to be slightly longer
5434 : }
5435 225 : endX = startX + length * cos(angle);
5436 225 : endY = startY + up * length * sin(angle);
5437 : } else {
5438 144 : double rightHang = (conf.noteHeadWidth() * mag / 2) - stem->width(); // subtract the stem width so the slash is optically centered on the stem
5439 144 : endX = stem->ldata()->bbox().translated(stem->pos()).right() + rightHang;
5440 144 : endY = startY + up * (endX - startX) * tan(angle);
5441 : }
5442 :
5443 841 : ldata->line = LineF(PointF(startX, startY), PointF(endX, endY));
5444 841 : ldata->stemWidth = conf.styleAbsolute(Sid::stemSlashThickness) * graceNoteMag;
5445 :
5446 841 : RectF bbox = RectF(ldata->line.p1(), ldata->line.p2()).normalized();
5447 841 : bbox = bbox.adjusted(-ldata->stemWidth / 2, -ldata->stemWidth / 2, ldata->stemWidth, ldata->stemWidth);
5448 841 : ldata->setBbox(bbox);
5449 : }
5450 :
5451 131 : void TLayout::layoutSticking(const Sticking* item, Sticking::LayoutData* ldata)
5452 : {
5453 : LAYOUT_CALL_ITEM(item);
5454 131 : TextLayout::layoutBaseTextBase(item, ldata);
5455 :
5456 131 : if (item->autoplace() && item->ownershipParent()) {
5457 131 : const Segment* s = toSegment(item->ownershipParent());
5458 131 : const Measure* m = s->measure();
5459 131 : LD_CONDITION(ldata->isSetPos());
5460 131 : LD_CONDITION(m->ldata()->isSetPos());
5461 131 : LD_CONDITION(s->ldata()->isSetPos());
5462 : }
5463 :
5464 131 : Autoplace::autoplaceSegmentElement(item, ldata);
5465 131 : }
5466 :
5467 19 : void TLayout::layoutStringTunings(StringTunings* item, LayoutContext& ctx)
5468 : {
5469 : LAYOUT_CALL_ITEM(item);
5470 19 : item->updateText();
5471 :
5472 19 : TextLayout::layoutBaseTextBase(item, ctx);
5473 :
5474 19 : double spatium = item->spatium();
5475 :
5476 19 : if (item->noStringVisible()) {
5477 19 : Font font(item->font());
5478 :
5479 19 : RectF rect;
5480 19 : rect.setTopLeft({ 0, item->ldata()->bbox().y() - 2.5 * spatium });
5481 19 : rect.setSize({ spatium, 2.5 * spatium });
5482 :
5483 19 : item->setbbox(rect);
5484 19 : }
5485 :
5486 38 : for (TextBlock& block : item->mutldata()->blocks) {
5487 19 : for (TextFragment& fragment : block.fragments()) {
5488 0 : Font font = fragment.font(item);
5489 0 : if (font.type() == Font::Type::MusicSymbol) {
5490 : // HACK: the music symbol doesn't have a good baseline
5491 : // to go with text so we correct it here
5492 0 : const double baselineAdjustment = 0.35 * spatium * item->symbolScale();
5493 0 : fragment.pos.setY(fragment.pos.y() + baselineAdjustment);
5494 : }
5495 0 : }
5496 : }
5497 :
5498 19 : double secondStringXAlign = 0.0;
5499 19 : for (const TextFragment& fragment : item->fragmentList()) {
5500 0 : if (fragment.font(item).type() == Font::Type::MusicSymbol) {
5501 0 : secondStringXAlign = std::max(secondStringXAlign, fragment.pos.x());
5502 : }
5503 19 : }
5504 :
5505 38 : for (TextBlock& block : item->mutldata()->blocks) {
5506 19 : double xMove = 0.0;
5507 19 : for (TextFragment& fragment : block.fragments()) {
5508 0 : if (block.fragments().front() == fragment) { // skip first
5509 0 : continue;
5510 : }
5511 :
5512 0 : if (fragment.font(item).type() == Font::Type::MusicSymbol) {
5513 0 : xMove = secondStringXAlign - fragment.pos.x();
5514 : }
5515 0 : fragment.pos.setX(fragment.pos.x() + xMove);
5516 : }
5517 : }
5518 :
5519 19 : Segment* parentSegment = item->segment();
5520 19 : item->move(PointF(-parentSegment->x() + item->spatium(), 0.0));
5521 :
5522 19 : Autoplace::autoplaceSegmentElement(item, item->mutldata());
5523 19 : }
5524 :
5525 9 : void TLayout::layoutSoundFlag(const SoundFlag* item, SoundFlag::LayoutData* ldata)
5526 : {
5527 : LAYOUT_CALL_ITEM(item);
5528 :
5529 9 : const Score* score = item->score();
5530 9 : if (score && !score->showSoundFlags()) {
5531 0 : return;
5532 : }
5533 :
5534 9 : const EngravingItem* parent = item->ownershipParentItem();
5535 9 : if (!parent) {
5536 0 : return;
5537 : }
5538 :
5539 9 : double spatium = item->spatium();
5540 9 : double iconHeight = spatium * 3.0;
5541 :
5542 9 : RectF parentBbox = parent->ldata()->bbox();
5543 9 : RectF iconBBox = RectF(parentBbox.x(), parentBbox.y(), iconHeight, iconHeight);
5544 :
5545 9 : iconBBox.moveCenter(parentBbox.center());
5546 :
5547 : // <icon><space><text>
5548 9 : double space = spatium / 2.0;
5549 9 : iconBBox.setX(parentBbox.x() - iconBBox.width() - space);
5550 :
5551 9 : ldata->setBbox(iconBBox);
5552 : }
5553 :
5554 72 : void TLayout::layoutSymbol(const Symbol* item, Symbol::LayoutData* ldata, const LayoutContext& ctx)
5555 : {
5556 : LAYOUT_CALL_ITEM(item);
5557 72 : IF_ASSERT_FAILED(item->ownershipParent()) {
5558 0 : return;
5559 : }
5560 :
5561 : LD_INDEPENDENT;
5562 :
5563 72 : double expectedMag = item->staff() ? item->staff()->staffMag(item->tick()) : ldata->mag();
5564 :
5565 72 : if (ldata->isValid() && RealIsEqual(ldata->mag(), expectedMag)) {
5566 0 : return;
5567 : }
5568 :
5569 72 : if (item->staff()) {
5570 50 : ldata->setMag(expectedMag);
5571 : }
5572 144 : ldata->setBbox(item->scoreFont()
5573 72 : ? item->scoreFont()->bbox(item->sym(), item->magS() * item->symbolsSize())
5574 31 : : item->symBbox(item->sym()));
5575 72 : double w = ldata->bbox().width();
5576 72 : PointF p;
5577 72 : if (item->align() == AlignV::BOTTOM) {
5578 0 : p.setY(-ldata->bbox().height());
5579 72 : } else if (item->align() == AlignV::VCENTER) {
5580 0 : p.setY((-ldata->bbox().height()) * .5);
5581 72 : } else if (item->align() == AlignV::BASELINE) {
5582 72 : p.setY(-item->baseLine());
5583 : }
5584 72 : if (item->align() == AlignH::RIGHT) {
5585 0 : p.setX(-w);
5586 72 : } else if (item->align() == AlignH::HCENTER) {
5587 0 : p.setX(-(w * .5));
5588 : }
5589 72 : ldata->setPos(p);
5590 :
5591 : // see BSymbol::add
5592 72 : for (EngravingItem* e : item->leafs()) {
5593 0 : switch (e->type()) {
5594 0 : case ElementType::SYMBOL: {
5595 0 : Symbol* s = item_cast<Symbol*>(e);
5596 0 : layoutSymbol(s, s->mutldata(), ctx);
5597 0 : } break;
5598 0 : case ElementType::IMAGE: {
5599 0 : Image* im = item_cast<Image*>(e);
5600 0 : layoutImage(im, im->mutldata());
5601 0 : } break;
5602 0 : default:
5603 0 : UNREACHABLE;
5604 : break;
5605 : }
5606 : }
5607 : }
5608 :
5609 0 : void TLayout::layoutFSymbol(const FSymbol* item, FSymbol::LayoutData* ldata)
5610 : {
5611 : LAYOUT_CALL_ITEM(item);
5612 : LD_INDEPENDENT;
5613 0 : if (ldata->isValid()) {
5614 0 : return;
5615 : }
5616 :
5617 0 : ldata->setBbox(FontMetrics::boundingRect(item->font(), item->toString()));
5618 : }
5619 :
5620 28 : void TLayout::layoutSystemDivider(const SystemDivider* item, SystemDivider::LayoutData* ldata, const LayoutContext& ctx)
5621 : {
5622 : LAYOUT_CALL_ITEM(item);
5623 : LD_INDEPENDENT;
5624 28 : if (ldata->isValid()) {
5625 6 : return;
5626 : }
5627 :
5628 22 : layoutSymbol(item, ldata, ctx);
5629 : }
5630 :
5631 136 : void TLayout::layoutSystemText(const SystemText* item, SystemText::LayoutData* ldata)
5632 : {
5633 : LAYOUT_CALL_ITEM(item);
5634 136 : TextLayout::layoutBaseTextBase(item, ldata);
5635 :
5636 136 : if (item->autoplace() && item->ownershipParent()) {
5637 136 : const Segment* s = toSegment(item->ownershipParent());
5638 136 : const Measure* m = s->measure();
5639 136 : LD_CONDITION(ldata->isSetPos());
5640 136 : LD_CONDITION(m->ldata()->isSetPos());
5641 136 : LD_CONDITION(s->ldata()->isSetPos());
5642 : }
5643 :
5644 136 : Autoplace::autoplaceSegmentElement(item, ldata);
5645 136 : }
5646 :
5647 24 : void TLayout::layoutTabDurationSymbol(const TabDurationSymbol* item, TabDurationSymbol::LayoutData* ldata)
5648 : {
5649 : LAYOUT_CALL_ITEM(item);
5650 : LD_INDEPENDENT;
5651 :
5652 : static constexpr Spatium TAB_RESTSYMBDISPL = 2.0_sp;
5653 :
5654 24 : if (!item->tab()) {
5655 0 : ldata->setBbox(RectF());
5656 0 : return;
5657 : }
5658 24 : double spatium = item->spatium();
5659 : double hbb, wbb, xbb, ybb; // bbox sizes
5660 : double xpos, ypos; // position coords
5661 :
5662 24 : ldata->beamGrid = TabBeamGrid::NONE;
5663 24 : ldata->beamLength = 0.0;
5664 24 : ldata->beamLevel = 0;
5665 24 : Chord* chord = item->ownershipParent() && item->ownershipParent()->isChord() ? toChord(item->ownershipParent()) : nullptr;
5666 : // A grid replaces the individual duration signs. Only connect equal, undotted
5667 : // values; otherwise retain the signs rather than lose their rhythm information.
5668 15 : auto connects = [](const ChordRest* left, const ChordRest* right) {
5669 15 : if (!left || !right || !left->isChord() || !right->isChord()) {
5670 4 : return false;
5671 : }
5672 22 : const bool regularChords = !left->isGrace() && !right->isGrace()
5673 22 : && !toChord(left)->noStem() && !toChord(right)->noStem();
5674 22 : const bool sameContext = left->measure() == right->measure() && left->staffType() == right->staffType()
5675 22 : && left->tuplet() == right->tuplet();
5676 11 : const bool equalUndottedDurations = left->durationType() == right->durationType() && !left->dots() && !right->dots();
5677 11 : const bool leftContinues = left->beamMode() == BeamMode::BEGIN || left->beamMode() == BeamMode::MID;
5678 11 : const bool rightConnects = right->beamMode() == BeamMode::MID || right->beamMode() == BeamMode::END;
5679 11 : return regularChords && sameContext && equalUndottedDurations && leftContinues && rightConnects;
5680 : };
5681 24 : const int beamLevel = chord ? static_cast<int>(chord->durationType().type())
5682 24 : - static_cast<int>(item->tab()->tabDurationFont().zeroBeamLevel) : 0;
5683 22 : const bool joinsPrevious = beamLevel > 0 && (chord->beamMode() == BeamMode::MID || chord->beamMode() == BeamMode::END)
5684 46 : && connects(Navigation::prevChordRest(chord), chord);
5685 22 : const bool joinsNext = beamLevel > 0 && (chord->beamMode() == BeamMode::BEGIN || chord->beamMode() == BeamMode::MID)
5686 46 : && connects(chord, Navigation::nextChordRest(chord));
5687 : // if no chord (shouldn't happens...) or not a special beam mode, layout regular symbol
5688 24 : if (beamLevel < 1 || (!joinsPrevious && !joinsNext)) {
5689 16 : FontMetrics fm(item->tab()->durationFont());
5690 16 : hbb = item->tab()->durationBoxH();
5691 16 : wbb = fm.width(item->text());
5692 16 : xbb = 0.0;
5693 16 : xpos = 0.0;
5694 16 : ypos = item->tab()->durationFontYOffset();
5695 16 : ybb = item->tab()->durationBoxY() - ypos;
5696 : // with rests, move symbol down by half its displacement from staff
5697 16 : if (item->ownershipParent() && item->ownershipParent()->isRest()) {
5698 0 : ybb += TAB_RESTSYMBDISPL.toAbsolute(spatium);
5699 0 : ypos += TAB_RESTSYMBDISPL.toAbsolute(spatium);
5700 : }
5701 16 : }
5702 : // if on a chord with special beam mode, layout an 'English'-style duration grid
5703 : else {
5704 8 : const TablatureDurationFont& font = item->tab()->tabDurationFont();
5705 8 : hbb = font.gridStemHeight.toAbsolute(spatium); // bbox height is stem height
5706 8 : wbb = font.gridStemWidth.toAbsolute(spatium); // bbox width is stem width
5707 8 : xbb = -wbb * 0.5; // bbox is half at left and half at right of stem centre
5708 8 : ybb = -hbb; // bbox top is at top of stem height
5709 8 : xpos = 0.75 * spatium; // conventional centring of stem on fret marks
5710 8 : ypos = item->tab()->durationGridYOffset(); // stem start is at bottom
5711 8 : ldata->beamLevel = beamLevel;
5712 8 : ldata->beamGrid = joinsPrevious ? TabBeamGrid::MEDIALFINAL : TabBeamGrid::INITIAL;
5713 : }
5714 : // set this' mag from parent chord mag (include staff mag)
5715 24 : double mag = chord != nullptr ? chord->mag() : 1.0;
5716 24 : ldata->setMag(mag);
5717 24 : mag = item->magS(); // local mag * score mag
5718 : // set magnified bbox and position
5719 24 : ldata->setBbox(xbb * mag, ybb * mag, wbb * mag, hbb * mag);
5720 24 : ldata->setPos(xpos * mag, ypos * mag);
5721 : }
5722 :
5723 24 : void TLayout::layoutTabDurationSymbol2(const TabDurationSymbol* item, TabDurationSymbol::LayoutData* ldata)
5724 : {
5725 24 : if (ldata->beamGrid != TabBeamGrid::MEDIALFINAL) {
5726 20 : return;
5727 : }
5728 4 : const ChordRest* previous = Navigation::prevChordRest(toChord(item->ownershipParent()));
5729 4 : if (!previous || !previous->tabDur()) {
5730 0 : return;
5731 : }
5732 : // Horizontal spacing is now final. Drawing applies the symbol magnification again.
5733 4 : const double length = previous->tabDur()->pagePos().x() - item->pagePos().x();
5734 4 : ldata->beamLength = length / item->magS();
5735 4 : RectF bbox = ldata->bbox();
5736 4 : bbox.setLeft(std::min(bbox.left(), length));
5737 4 : ldata->setBbox(bbox);
5738 : }
5739 :
5740 134 : void TLayout::layoutTapping(Tapping* item, Tapping::LayoutData* ldata, LayoutContext& ctx)
5741 : {
5742 134 : TappingLayout::layoutTapping(item, ldata, ctx);
5743 134 : }
5744 :
5745 0 : void TLayout::layoutTappingHalfSlur(TappingHalfSlur* item)
5746 : {
5747 : UNUSED(item)
5748 0 : }
5749 :
5750 1136 : void TLayout::layoutTempoText(const TempoText* item, TempoText::LayoutData* ldata)
5751 : {
5752 : LAYOUT_CALL_ITEM(item);
5753 1136 : IF_ASSERT_FAILED(item->ownershipParent()) {
5754 0 : return;
5755 : }
5756 :
5757 1136 : ldata->disconnectSnappedItems();
5758 :
5759 1136 : TextLayout::layoutBaseTextBase(item, ldata);
5760 :
5761 1136 : if (!item->autoplace()) {
5762 0 : return;
5763 : }
5764 :
5765 : // tempo text on first chordrest of measure should align over time sig if present, unless time sig is above staff
5766 1136 : Segment* s = item->segment();
5767 :
5768 1136 : RehearsalMark* rehearsalMark = toRehearsalMark(s->findAnnotation(ElementType::REHEARSAL_MARK, item->track(), item->track()));
5769 1136 : RectF rehearsalMarkBbox = rehearsalMark ? rehearsalMark->ldata()->bbox().translated(rehearsalMark->pos()) : RectF();
5770 1136 : RectF thisBbox = ldata->bbox().translated(item->pos());
5771 :
5772 1136 : if (s->rtick().isZero()) {
5773 1116 : Segment* p = item->segment()->prev(SegmentType::TimeSig);
5774 1116 : if (p && !p->allElementsInvisible()) {
5775 874 : ldata->moveX(-(s->x() - p->x()));
5776 874 : EngravingItem* e = p->element(item->staffIdx() * VOICES);
5777 874 : if (e) {
5778 874 : ldata->moveX(p->hasTimeSigAboveStaves() ? e->x() + e->width() + e->spatium() : e->x());
5779 : }
5780 : }
5781 : }
5782 :
5783 1136 : if (rehearsalMark) {
5784 10 : const bool sameSide = item->placeAbove() == rehearsalMark->placeAbove();
5785 : const bool collision
5786 10 : = item->placeAbove() ? muse::RealIsEqualOrMore(rehearsalMarkBbox.bottom(), thisBbox.top()) : muse::RealIsEqualOrLess(
5787 10 : rehearsalMarkBbox.top(), thisBbox.bottom());
5788 :
5789 10 : if (sameSide && collision && item->getProperty(Pid::TEMPO_ALIGN_RIGHT_OF_REHEARSAL_MARK).toBool()) {
5790 10 : double rightEdge = rehearsalMarkBbox.right();
5791 10 : const double fontSizeScaleFactor = item->size() / 10.0;
5792 10 : const double padding = 0.5 * item->spatium() * fontSizeScaleFactor;
5793 10 : double curX = ldata->pos().x();
5794 10 : ldata->setPosX(std::max(curX, rightEdge + padding));
5795 : }
5796 : }
5797 1136 : Autoplace::autoplaceSegmentElement(item, ldata);
5798 : }
5799 :
5800 11 : Shape TLayout::recalculateTextLineBaseSegmentShape(const TextLineBaseSegment* item)
5801 : {
5802 : LAYOUT_CALL_ITEM(item);
5803 11 : const TextLineBaseSegment::LayoutData* ldata = item->ldata();
5804 11 : Shape shape;
5805 11 : if (!item->text()->empty()) {
5806 11 : shape.add(item->text()->ldata()->bbox().translated(item->text()->pos()), item->text());
5807 : }
5808 11 : if (!item->endText()->empty()) {
5809 11 : shape.add(item->endText()->ldata()->bbox().translated(item->endText()->pos()), item->endText());
5810 : }
5811 11 : double lw2 = 0.5 * item->absoluteFromSpatium(item->lineWidth());
5812 11 : bool isDottedLine = item->textLineBase()->lineStyle() == LineType::DOTTED;
5813 11 : if (item->twoLines()) { // hairpins
5814 0 : shape.add(TextLineBaseSegment::boundingBoxOfLine(ldata->points[0], ldata->points[1], lw2, isDottedLine), item);
5815 0 : shape.add(TextLineBaseSegment::boundingBoxOfLine(ldata->points[2], ldata->points[3], lw2, isDottedLine), item);
5816 : } else {
5817 22 : for (int i = 0; i < ldata->npoints - 1; ++i) {
5818 11 : shape.add(TextLineBaseSegment::boundingBoxOfLine(ldata->points[i], ldata->points[i + 1], lw2, isDottedLine), item,
5819 11 : !item->textLineBase()->lineVisible());
5820 : }
5821 : }
5822 11 : return shape;
5823 0 : }
5824 :
5825 15377 : void TLayout::layoutText(const Text* item, Text::LayoutData* ldata)
5826 : {
5827 : LAYOUT_CALL_ITEM(item);
5828 15377 : if (item->layoutParent() && item->layoutToParentWidth()) {
5829 11152 : LD_CONDITION(item->layoutParent()->ldata()->isSetBbox());
5830 : }
5831 :
5832 15377 : TextLayout::layoutBaseTextBase(item, ldata);
5833 15377 : }
5834 :
5835 0 : void TLayout::layoutTextLine(TextLine* item, LayoutContext& ctx)
5836 : {
5837 : LAYOUT_CALL_ITEM(item);
5838 0 : layoutLine(item, ctx);
5839 0 : }
5840 :
5841 55 : void TLayout::layoutTextLineSegment(TextLineSegment* item, LayoutContext& ctx)
5842 : {
5843 : LAYOUT_CALL_ITEM(item);
5844 55 : TextLineSegment::LayoutData* ldata = item->mutldata();
5845 55 : layoutTextLineBaseSegment(item, ctx);
5846 :
5847 55 : Autoplace::autoplaceSpannerSegment(item, ldata);
5848 55 : }
5849 :
5850 : // Extends lines to fill the corner between them.
5851 : // Assumes that l1p2 == l2p1 is the intersection between the lines.
5852 : // If checkAngle is false, assumes that the lines are perpendicular,
5853 : // and some calculations are saved.
5854 147 : static inline void extendLines(const PointF& l1p1, PointF& l1p2, PointF& l2p1, const PointF& l2p2, double lineWidth, bool checkAngle)
5855 : {
5856 147 : PointF l1UnitVector = (l1p2 - l1p1).normalized();
5857 147 : PointF l2UnitVector = (l2p1 - l2p2).normalized();
5858 :
5859 147 : double addedLength = lineWidth * 0.5;
5860 :
5861 147 : if (checkAngle) {
5862 0 : double angle = M_PI - acos(PointF::dotProduct(l1UnitVector, l2UnitVector));
5863 :
5864 0 : if (angle <= M_PI_2) {
5865 0 : addedLength *= tan(0.5 * angle);
5866 : }
5867 : }
5868 :
5869 147 : l1p2 += l1UnitVector * addedLength;
5870 147 : l2p1 += l2UnitVector * addedLength;
5871 147 : }
5872 :
5873 0 : static PolygonF createArrow(bool start, bool filled, PointF& startPoint, PointF& endPoint, const TextLineBase* tl)
5874 : {
5875 0 : double arrowWidth = 0.0;
5876 0 : double arrowHeight = 0.0;
5877 0 : if (filled) {
5878 0 : arrowWidth = tl->absoluteFromSpatium(start ? tl->beginFilledArrowWidth() : tl->endFilledArrowWidth());
5879 0 : arrowHeight = tl->absoluteFromSpatium(start ? tl->beginFilledArrowHeight() : tl->endFilledArrowHeight());
5880 : } else {
5881 0 : arrowWidth = tl->absoluteFromSpatium(start ? tl->beginLineArrowWidth() : tl->endLineArrowWidth());
5882 0 : arrowHeight = tl->absoluteFromSpatium(start ? tl->beginLineArrowHeight() : tl->endLineArrowHeight());
5883 : }
5884 :
5885 0 : PolygonF arrow;
5886 0 : if (start) {
5887 0 : arrow << PointF(0.0, -arrowHeight / 2) << PointF(-arrowWidth, 0.0) << PointF(0.0, arrowHeight / 2); // left
5888 : } else {
5889 0 : arrow << PointF(0.0, -arrowHeight / 2) << PointF(arrowWidth, 0.0) << PointF(0.0, arrowHeight / 2); // right
5890 : }
5891 :
5892 0 : PointF arrowAdjust = PointF(arrowWidth, 0.0);
5893 0 : arrowAdjust = (start ? 1.0 : -1.0) * arrowAdjust;
5894 0 : arrow.translate(arrowAdjust);
5895 :
5896 0 : const double yDiff = endPoint.y() - startPoint.y();
5897 0 : const double xDiff = endPoint.x() - startPoint.x();
5898 0 : const double rotate = atan(yDiff / xDiff) + (endPoint.x() < startPoint.x() ? M_PI : 0.0);
5899 :
5900 0 : Transform t;
5901 0 : t.rotateRadians(rotate);
5902 :
5903 0 : for (PointF& p : arrow) {
5904 0 : p = t.map(p);
5905 : }
5906 :
5907 0 : if (start) {
5908 0 : arrow.translate(startPoint);
5909 : } else {
5910 0 : arrow.translate(endPoint);
5911 : }
5912 :
5913 0 : const PointF lineVector = endPoint - startPoint;
5914 0 : const PointF unitVector = lineVector.normalized();
5915 0 : const double reduction = filled ? arrowWidth : tl->absoluteFromSpatium(tl->lineWidth()) / 2;
5916 0 : if (start) {
5917 0 : startPoint += reduction * unitVector;
5918 : } else {
5919 0 : endPoint -= reduction * unitVector;
5920 : }
5921 :
5922 0 : return arrow;
5923 0 : }
5924 :
5925 0 : void TLayout::layoutTextLineBase(TextLineBase* item, LayoutContext& ctx)
5926 : {
5927 0 : layoutLine(item, ctx);
5928 0 : }
5929 :
5930 1269 : void TLayout::layoutTextLineBaseSegment(TextLineBaseSegment* item, LayoutContext& ctx)
5931 : {
5932 1269 : TextLineBaseSegment::LayoutData* ldata = item->mutldata();
5933 1269 : ldata->npoints = 0;
5934 1269 : const TextLineBase* tl = item->textLineBase();
5935 1269 : const double spatium = tl->spatium();
5936 1269 : const bool isSingleOrBegin = item->isSingleBeginType();
5937 1269 : const bool isSingleOrEnd = item->isSingleEndType();
5938 1269 : const bool isDottedLine = tl->lineStyle() == LineType::DOTTED;
5939 1269 : const double lineWidth = tl->absoluteFromSpatium(tl->lineWidth());
5940 :
5941 1269 : if (item->spanner()->placeBelow()) {
5942 594 : ldata->setPosY(item->staff() ? item->staff()->staffHeight(item->tick()) : 0.0);
5943 : }
5944 :
5945 : // adjust Y pos to staffType offset
5946 1269 : ldata->moveY(item->staffOffsetY());
5947 :
5948 1269 : if (!tl->diagonal()) {
5949 1252 : item->setUserYoffset2(0);
5950 : }
5951 :
5952 438 : auto alignBaseLine = [lineWidth](Text* text, PointF& pp1, PointF& pp2) {
5953 438 : PointF widthCorrection(0.0, lineWidth / 2);
5954 438 : switch (text->align().vertical) {
5955 117 : case AlignV::TOP:
5956 117 : pp1 += widthCorrection;
5957 117 : pp2 += widthCorrection;
5958 117 : break;
5959 136 : case AlignV::VCENTER:
5960 136 : break;
5961 0 : case AlignV::BOTTOM:
5962 0 : pp1 -= widthCorrection;
5963 0 : pp2 -= widthCorrection;
5964 0 : break;
5965 185 : case AlignV::BASELINE:
5966 185 : pp1 -= widthCorrection;
5967 185 : pp2 -= widthCorrection;
5968 185 : break;
5969 : }
5970 438 : };
5971 :
5972 1269 : if (isSingleOrBegin) {
5973 1246 : item->text()->setXmlText(tl->beginText());
5974 1246 : item->text()->setFamily(tl->beginFontFamily());
5975 1246 : item->text()->setSize(tl->beginFontSize());
5976 1246 : item->text()->setSymbolScale(tl->beginTextMusicalSymbolsScale());
5977 1246 : item->text()->setSymbolSize(tl->beginTextMusicSymbolsSize());
5978 1246 : item->text()->setOffset(tl->beginTextOffset() * item->mag());
5979 1246 : item->text()->setAlign(tl->beginTextAlign());
5980 1246 : item->text()->setPosition(tl->beginTextPosition());
5981 1246 : item->text()->setFontStyle(tl->beginFontStyle());
5982 : } else {
5983 23 : item->text()->setXmlText(tl->continueText());
5984 23 : item->text()->setFamily(tl->continueFontFamily());
5985 23 : item->text()->setSize(tl->continueFontSize());
5986 23 : item->text()->setSymbolScale(tl->continueTextMusicalSymbolsScale());
5987 23 : item->text()->setSymbolSize(tl->continueTextMusicSymbolsSize());
5988 23 : item->text()->setOffset(tl->continueTextOffset() * item->mag());
5989 23 : item->text()->setAlign(tl->continueTextAlign());
5990 23 : item->text()->setPosition(tl->continueTextPosition());
5991 23 : item->text()->setFontStyle(tl->continueFontStyle());
5992 : }
5993 1269 : item->text()->setPlacement(PlacementV::ABOVE);
5994 1269 : item->text()->setTrack(item->track());
5995 1269 : item->text()->setColor(tl->lineColor());
5996 1269 : layoutText(item->text(), item->text()->mutldata());
5997 :
5998 1269 : if (isSingleOrEnd) {
5999 1244 : item->endText()->setXmlText(tl->endText());
6000 1244 : item->endText()->setFamily(tl->endFontFamily());
6001 1244 : item->endText()->setSize(tl->endFontSize());
6002 1244 : item->endText()->setSymbolScale(tl->endTextMusicalSymbolsScale());
6003 1244 : item->endText()->setSymbolSize(tl->endTextMusicSymbolsSize());
6004 1244 : item->endText()->setOffset(tl->endTextOffset() * item->mag());
6005 1244 : item->endText()->setAlign(tl->endTextAlign());
6006 1244 : item->endText()->setPosition(tl->endTextPosition());
6007 1244 : item->endText()->setFontStyle(tl->endFontStyle());
6008 1244 : item->endText()->setPlacement(PlacementV::ABOVE);
6009 1244 : item->endText()->setTrack(item->track());
6010 1244 : item->endText()->setColor(tl->lineColor());
6011 1244 : layoutText(item->endText(), item->endText()->mutldata());
6012 : } else {
6013 25 : item->endText()->setXmlText(u"");
6014 : }
6015 :
6016 1269 : if (!item->textLineBase()->textSizeSpatiumDependent()) {
6017 0 : item->text()->setSize(item->text()->size() * item->defaultSpatium() / item->spatium());
6018 0 : item->endText()->setSize(item->endText()->size() * item->defaultSpatium() / item->spatium());
6019 : }
6020 :
6021 1269 : PointF defaultPos = tl->defaultPos();
6022 1269 : ldata->move(defaultPos);
6023 :
6024 1269 : PointF pp1;
6025 1269 : PointF pp2(item->pos2());
6026 :
6027 : // line with no text or hooks - just use the basic rectangle for line
6028 1763 : if (item->text()->empty() && item->endText()->empty()
6029 483 : && (!isSingleOrBegin || tl->beginHookType() == HookType::NONE)
6030 1763 : && (!isSingleOrEnd || tl->endHookType() == HookType::NONE)) {
6031 444 : ldata->npoints = 2;
6032 444 : ldata->points[0] = pp1;
6033 444 : ldata->points[1] = pp2;
6034 444 : ldata->lineLength = sqrt(PointF::dotProduct(pp2 - pp1, pp2 - pp1));
6035 :
6036 444 : item->setbbox(TextLineBaseSegment::boundingBoxOfLine(pp1, pp2, lineWidth / 2, isDottedLine));
6037 493 : return;
6038 : }
6039 :
6040 825 : double l1 = 0.0;
6041 825 : double l2 = 0.0;
6042 825 : const double gapBetweenTextAndLine = spatium * tl->gapBetweenTextAndLine().val();
6043 :
6044 825 : const bool alignBeginText = tl->beginTextPlace() == TextPlace::LEFT || tl->beginTextPlace() == TextPlace::AUTO;
6045 825 : const bool alignContinueText = tl->continueTextPlace() == TextPlace::LEFT || tl->continueTextPlace() == TextPlace::AUTO;
6046 825 : const bool alignEndText = tl->endTextPlace() == TextPlace::LEFT || tl->endTextPlace() == TextPlace::AUTO;
6047 825 : const bool hasBeginText = !item->text()->empty() && isSingleOrBegin;
6048 825 : const bool hasContinueText = !item->text()->empty() && !isSingleOrBegin;
6049 825 : const bool hasEndText = !item->endText()->empty() && isSingleOrEnd;
6050 :
6051 825 : Shape shape;
6052 :
6053 825 : if (!item->text()->empty()) {
6054 775 : if ((isSingleOrBegin && alignBeginText) || (!isSingleOrBegin && alignContinueText)) {
6055 427 : l1 = gapBetweenTextAndLine;
6056 427 : switch (item->text()->position()) {
6057 427 : case AlignH::LEFT:
6058 427 : l1 += item->text()->ldata()->bbox().width();
6059 427 : break;
6060 0 : case AlignH::HCENTER:
6061 0 : l1 += item->text()->ldata()->bbox().width() / 2;
6062 0 : break;
6063 0 : default:
6064 0 : break;
6065 : }
6066 : }
6067 775 : shape.add(item->text()->ldata()->bbox().translated(item->text()->pos()), item);
6068 : }
6069 : // set end text position and extend bbox
6070 825 : if (!item->endText()->empty()) {
6071 30 : if (alignEndText) {
6072 30 : l2 = gapBetweenTextAndLine;
6073 30 : switch (item->endText()->position()) {
6074 4 : case AlignH::RIGHT:
6075 4 : l2 += item->endText()->ldata()->bbox().width();
6076 4 : break;
6077 11 : case AlignH::HCENTER:
6078 11 : l2 += item->endText()->ldata()->bbox().width() / 2;
6079 11 : break;
6080 15 : default:
6081 15 : break;
6082 : }
6083 : }
6084 30 : const double endTextX = std::max(pp2.x(), shape.bbox().right() + l2); // prevent end text from overlapping begin text
6085 30 : item->endText()->mutldata()->setPosX(endTextX);
6086 30 : shape.add(item->endText()->ldata()->bbox().translated(item->endText()->pos()), item);
6087 : }
6088 :
6089 825 : bool beginHookDrawnSeparately = false;
6090 825 : bool endHookDrawnSeparately = false;
6091 :
6092 825 : if (tl->lineVisible() || !ctx.conf().isPrintingMode()) {
6093 825 : pp1.rx() += l1;
6094 825 : pp2.rx() -= l2;
6095 :
6096 : // Make sure baseline of text and line are properly aligned (accounting for line thickness)
6097 825 : if ((hasBeginText && alignBeginText) || (hasContinueText && alignContinueText)) {
6098 427 : alignBaseLine(item->text(), pp1, pp2);
6099 398 : } else if (hasEndText && alignEndText) {
6100 11 : alignBaseLine(item->endText(), pp1, pp2);
6101 : }
6102 :
6103 825 : const double beginHookHeight = (tl->placeBelow() ? -1.0 : 1.0) * tl->beginHookHeight().val() * spatium;
6104 825 : const double endHookHeight = (tl->placeBelow() ? -1.0 : 1.0) * tl->endHookHeight().val() * spatium;
6105 :
6106 825 : double beginHookWidth = 0.0;
6107 825 : double endHookWidth = 0.0;
6108 :
6109 825 : if (tl->beginHookType() == HookType::HOOK_45) {
6110 10 : beginHookWidth = std::fabs(beginHookHeight * .4);
6111 10 : pp1.rx() += beginHookWidth;
6112 : }
6113 :
6114 825 : if (tl->endHookType() == HookType::HOOK_45) {
6115 8 : endHookWidth = std::fabs(endHookHeight * .4);
6116 8 : pp2.rx() -= endHookWidth;
6117 : }
6118 :
6119 : // don't draw backwards lines (or hooks) if text is longer than nominal line length
6120 825 : if (!item->text()->empty() && pp1.x() > pp2.x() && !tl->diagonal()) {
6121 49 : ldata->setShape(shape);
6122 49 : return;
6123 : }
6124 :
6125 : const bool beginArrow = isSingleOrBegin
6126 776 : && (tl->beginHookType() == HookType::ARROW_FILLED || tl->beginHookType() == HookType::ARROW);
6127 776 : const bool endArrow = isSingleOrEnd && (tl->endHookType() == HookType::ARROW_FILLED || tl->endHookType() == HookType::ARROW);
6128 :
6129 776 : if (beginArrow) {
6130 0 : const bool filled = tl->beginHookType() == HookType::ARROW_FILLED;
6131 0 : ldata->beginArrow = createArrow(true, filled, pp1, pp2, tl);
6132 0 : shape.add(ldata->beginArrow.boundingRect(), item);
6133 : }
6134 776 : if (endArrow) {
6135 0 : const bool filled = tl->endHookType() == HookType::ARROW_FILLED;
6136 0 : ldata->endArrow = createArrow(false, filled, pp1, pp2, tl);
6137 0 : shape.add(ldata->endArrow.boundingRect(), item);
6138 : }
6139 :
6140 1511 : auto hasHook = [](HookType type) -> bool {
6141 1511 : switch (type) {
6142 897 : case HookType::HOOK_45:
6143 : case HookType::HOOK_90:
6144 : case HookType::HOOK_90T:
6145 897 : return true;
6146 614 : case HookType::NONE:
6147 : case HookType::ARROW:
6148 : case HookType::ARROW_FILLED:
6149 : case HookType::ROSETTE:
6150 614 : return false;
6151 0 : default:
6152 0 : break;
6153 : }
6154 0 : return false;
6155 : };
6156 :
6157 776 : const bool beginHook = isSingleOrBegin && hasHook(tl->beginHookType());
6158 776 : const bool endHook = isSingleOrEnd && hasHook(tl->endHookType());
6159 :
6160 776 : if (beginHook) {
6161 : // We use the term "endpoint" for the point that does not touch the main line.
6162 374 : const PointF& beginHookEndpoint = ldata->points[ldata->npoints++]
6163 374 : = PointF(pp1.x() - beginHookWidth, pp1.y() + beginHookHeight);
6164 374 : if (tl->beginHookType() == HookType::HOOK_90T) {
6165 : // A T-hook needs to be drawn separately, so we add an extra point
6166 0 : beginHookDrawnSeparately = true;
6167 0 : ldata->points[ldata->npoints++] = PointF(pp1.x() - beginHookWidth, pp1.y() - beginHookHeight);
6168 374 : } else if (tl->lineStyle() != LineType::SOLID) {
6169 : // For non-solid lines, we also draw the hook separately,
6170 : // so that we can distribute the dashes/dots for each linepiece individually
6171 2 : beginHookDrawnSeparately = true;
6172 2 : PointF& beginHookStartpoint = ldata->points[ldata->npoints++] = pp1;
6173 :
6174 2 : if (tl->lineStyle() == LineType::DASHED) {
6175 : // For dashes lines, we extend the lines somewhat,
6176 : // so that the corner between them gets filled
6177 1 : bool checkAngle = tl->beginHookType() == HookType::HOOK_45 || tl->diagonal();
6178 1 : extendLines(beginHookEndpoint, beginHookStartpoint, pp1, pp2, lineWidth, checkAngle);
6179 : }
6180 : }
6181 : }
6182 :
6183 776 : ldata->points[ldata->npoints++] = pp1;
6184 776 : PointF& pp22 = ldata->points[ldata->npoints++] = pp2; // Keep a reference so that we can modify later
6185 :
6186 776 : if (endHook) {
6187 523 : const PointF endHookEndpoint = PointF(pp2.x() + endHookWidth, pp2.y() + endHookHeight);
6188 523 : if (tl->endHookType() == HookType::HOOK_90T) {
6189 : // A T-hook needs to be drawn separately, so we add an extra point
6190 45 : endHookDrawnSeparately = true;
6191 45 : ldata->points[ldata->npoints++] = PointF(pp2.x() + endHookWidth, pp2.y() - endHookHeight);
6192 478 : } else if (tl->lineStyle() != LineType::SOLID) {
6193 : // For non-solid lines, we also draw the hook separately,
6194 : // so that we can distribute the dashes/dots for each linepiece individually
6195 148 : endHookDrawnSeparately = true;
6196 148 : PointF& endHookStartpoint = ldata->points[ldata->npoints++] = pp2;
6197 :
6198 148 : if (tl->lineStyle() == LineType::DASHED) {
6199 : // For dashes lines, we extend the lines somewhat,
6200 : // so that the corner between them gets filled
6201 146 : bool checkAngle = tl->endHookType() == HookType::HOOK_45 || tl->diagonal();
6202 146 : extendLines(pp1, pp22, endHookStartpoint, endHookEndpoint, lineWidth, checkAngle);
6203 : }
6204 : }
6205 :
6206 523 : ldata->points[ldata->npoints++] = endHookEndpoint;
6207 : }
6208 :
6209 776 : ldata->lineLength = sqrt(PointF::dotProduct(pp22 - pp1, pp22 - pp1));
6210 : }
6211 :
6212 : // Calculate shape of line
6213 : {
6214 776 : int start = 0;
6215 776 : if (beginHookDrawnSeparately) {
6216 2 : PointF& p1 = ldata->points[start++];
6217 2 : PointF& p2 = ldata->points[start++];
6218 2 : shape.add(TextLineBaseSegment::boundingBoxOfLine(p1, p2, lineWidth / 2, isDottedLine), item);
6219 : }
6220 :
6221 776 : int end = ldata->npoints;
6222 776 : if (endHookDrawnSeparately) {
6223 193 : PointF& p1 = ldata->points[--end];
6224 193 : PointF& p2 = ldata->points[--end];
6225 193 : shape.add(TextLineBaseSegment::boundingBoxOfLine(p1, p2, lineWidth / 2, isDottedLine), item);
6226 : }
6227 :
6228 2254 : for (int i = start; i < end - 1; ++i) {
6229 1478 : shape.add(TextLineBaseSegment::boundingBoxOfLine(ldata->points[i], ldata->points[i + 1], lineWidth / 2,
6230 1478 : isDottedLine), item, !tl->lineVisible());
6231 : }
6232 : }
6233 :
6234 776 : ldata->setShape(shape);
6235 825 : }
6236 :
6237 2280 : void TLayout::layoutTie(Tie* item, LayoutContext&)
6238 : {
6239 : UNUSED(item);
6240 2280 : }
6241 :
6242 6530 : void TLayout::layoutTimeSig(const TimeSig* item, TimeSig::LayoutData* ldata, const LayoutContext& ctx)
6243 : {
6244 : LAYOUT_CALL_ITEM(item);
6245 : LD_INDEPENDENT;
6246 :
6247 6530 : ldata->setPos(0.0, 0.0);
6248 6530 : double spatium = item->spatium();
6249 :
6250 6530 : ldata->setBbox(RectF()); // prepare for an empty time signature
6251 :
6252 6530 : ldata->pointLargeLeftParen = PointF();
6253 6530 : ldata->pz = PointF();
6254 6530 : ldata->pn = PointF();
6255 6530 : ldata->pointLargeRightParen = PointF();
6256 :
6257 6530 : const MStyle& style = item->style();
6258 6530 : TimeSigPlacement timeSigPlacement = style.styleV(Sid::timeSigPlacement).value<TimeSigPlacement>();
6259 6530 : if (timeSigPlacement == TimeSigPlacement::NORMAL) {
6260 6496 : const_cast<TimeSig*>(item)->setSystemFlag(false);
6261 : } else {
6262 34 : const_cast<TimeSig*>(item)->setSystemFlag(true);
6263 : }
6264 :
6265 6530 : double lineDist = 0.0;
6266 6530 : int numOfLines = 0;
6267 6530 : TimeSigType sigType = item->timeSigType();
6268 6530 : const Staff* staff = item->staff();
6269 6530 : const Segment* seg = item->segment();
6270 6530 : const Measure* meas = seg ? seg->measure() : nullptr;
6271 6530 : const Fraction tick = meas ? meas->tick() : item->tick();
6272 :
6273 6530 : if (staff) {
6274 : // if staff is without time sig, format as if no text at all
6275 6530 : const StaffType* stVisibility = staff->staffType(item->tick());
6276 6530 : if (!stVisibility->genTimesig()) {
6277 : // reset position and box sizes to 0
6278 : // LOGD("staff: no time sig");
6279 111 : ldata->pointLargeLeftParen.rx() = 0.0;
6280 111 : ldata->pn.rx() = 0.0;
6281 111 : ldata->pz.rx() = 0.0;
6282 111 : ldata->pointLargeRightParen.rx() = 0.0;
6283 111 : ldata->setBbox(RectF());
6284 : // leave everything else as it is:
6285 : // draw() will anyway skip any drawing if staff type has no time sigs
6286 111 : return;
6287 : }
6288 6419 : numOfLines = staff->lines(tick);
6289 6419 : lineDist = staff->lineDistance(tick);
6290 : } else {
6291 : // assume dimensions of a standard staff
6292 0 : lineDist = 1.0;
6293 0 : numOfLines = 5;
6294 : }
6295 :
6296 : // if some symbol
6297 : // compute vert. displacement to center in the staff height
6298 : // determine middle staff position:
6299 :
6300 6419 : double yoff = spatium * (numOfLines - 1) * .5 * lineDist;
6301 :
6302 : // C and Ccut are placed at the middle of the staff: use yoff directly
6303 6419 : IEngravingFontPtr font = ctx.engravingFont();
6304 6419 : ScaleF scale = item->scale();
6305 6419 : SizeF mag(item->magS() * scale);
6306 6419 : TimeSigStyle timeSigStyle = item->timeSigStyle();
6307 :
6308 6419 : double numDist = item->numDist() * scale.height();
6309 :
6310 6419 : Shape shape;
6311 6419 : if (sigType == TimeSigType::FOUR_FOUR) {
6312 76 : ldata->pz = PointF(0.0, yoff);
6313 152 : SymId sym = timeSigStyle == TimeSigStyle::LARGE ? SymId::timeSigCommonLarge
6314 76 : : timeSigStyle == TimeSigStyle::NARROW ? SymId::timeSigCommonNarrow
6315 : : SymId::timeSigCommon;
6316 76 : RectF bbox = font->bbox(sym, mag);
6317 76 : shape.add(bbox.translated(ldata->pz), item);
6318 76 : ldata->ns.clear();
6319 76 : ldata->ns.push_back(sym);
6320 76 : ldata->ds.clear();
6321 6343 : } else if (sigType == TimeSigType::ALLA_BREVE) {
6322 56 : ldata->pz = PointF(0.0, yoff);
6323 112 : SymId sym = timeSigStyle == TimeSigStyle::LARGE ? SymId::timeSigCutCommonLarge
6324 56 : : timeSigStyle == TimeSigStyle::NARROW ? SymId::timeSigCutCommonNarrow
6325 : : SymId::timeSigCutCommon;
6326 56 : RectF bbox = font->bbox(sym, mag);
6327 56 : shape.add(bbox.translated(ldata->pz), item);
6328 56 : ldata->ns.clear();
6329 56 : ldata->ns.push_back(sym);
6330 56 : ldata->ds.clear();
6331 6287 : } else if (sigType == TimeSigType::CUT_BACH) {
6332 0 : ldata->pz = PointF(0.0, yoff);
6333 0 : SymId sym = timeSigStyle == TimeSigStyle::LARGE ? SymId::timeSigCut2Large
6334 0 : : timeSigStyle == TimeSigStyle::NARROW ? SymId::timeSigCut2Narrow
6335 : : SymId::timeSigCut2;
6336 0 : RectF bbox = font->bbox(sym, mag);
6337 0 : shape.add(bbox.translated(ldata->pz), item);
6338 0 : ldata->ns.clear();
6339 0 : ldata->ns.push_back(sym);
6340 0 : ldata->ds.clear();
6341 6287 : } else if (sigType == TimeSigType::CUT_TRIPLE) {
6342 0 : ldata->pz = PointF(0.0, yoff);
6343 0 : SymId sym = timeSigStyle == TimeSigStyle::LARGE ? SymId::timeSigCut3Large
6344 0 : : timeSigStyle == TimeSigStyle::NARROW ? SymId::timeSigCut3Narrow
6345 : : SymId::timeSigCut3;
6346 0 : RectF bbox = font->bbox(sym, mag);
6347 0 : shape.add(bbox.translated(ldata->pz), item);
6348 0 : ldata->ns.clear();
6349 0 : ldata->ns.push_back(sym);
6350 0 : ldata->ds.clear();
6351 : } else {
6352 6287 : if (item->numeratorString().isEmpty()) {
6353 18795 : ldata->ns = timeSigSymIdsFromString(item->numeratorString().isEmpty()
6354 12530 : ? String::number(item->sig().numerator())
6355 0 : : item->numeratorString(),
6356 6265 : timeSigStyle);
6357 :
6358 18795 : ldata->ds = timeSigSymIdsFromString(item->denominatorString().isEmpty()
6359 12530 : ? String::number(item->sig().denominator())
6360 0 : : item->denominatorString(),
6361 6265 : timeSigStyle);
6362 : } else {
6363 22 : ldata->ns = timeSigSymIdsFromString(item->numeratorString(), timeSigStyle);
6364 22 : ldata->ds = timeSigSymIdsFromString(item->denominatorString(), timeSigStyle);
6365 : }
6366 :
6367 6287 : RectF numRect = font->bbox(ldata->ns, mag);
6368 6287 : RectF denRect = font->bbox(ldata->ds, mag);
6369 :
6370 6287 : double pzY = yoff - (denRect.width() < 0.01 ? 0.0 : 0.5 * (numRect.height() + numDist));
6371 6287 : double pnY = yoff + 0.5 * (denRect.height() + numDist);
6372 :
6373 6287 : if (numRect.width() >= denRect.width()) {
6374 : // numerator: one space above centre line, unless denomin. is empty (if so, directly centre in the middle)
6375 5298 : ldata->pz = PointF(0.0, pzY);
6376 : // denominator: horiz: centred around centre of numerator | vert: one space below centre line
6377 5298 : ldata->pn = PointF((numRect.width() - denRect.width()) * .5, pnY);
6378 : } else {
6379 : // numerator: one space above centre line, unless denomin. is empty (if so, directly centre in the middle)
6380 989 : ldata->pz = PointF((denRect.width() - numRect.width()) * .5, pzY);
6381 : // denominator: horiz: centred around centre of numerator | vert: one space below centre line
6382 989 : ldata->pn = PointF(0.0, pnY);
6383 : }
6384 :
6385 : // centering of parenthesis so the middle of the parenthesis is at the divisor marking level
6386 6287 : int centerY = yoff / 2 + spatium;
6387 6287 : int widestPortion = numRect.width() > denRect.width() ? numRect.width() : denRect.width();
6388 6287 : ldata->pointLargeLeftParen = PointF(-spatium, centerY);
6389 6287 : ldata->pointLargeRightParen = PointF(widestPortion + spatium, centerY);
6390 :
6391 6287 : shape.add(numRect.translated(ldata->pz), item); // translate bounding boxes to actual string positions
6392 6287 : shape.add(denRect.translated(ldata->pn), item);
6393 6287 : if (item->largeParentheses()) {
6394 18 : shape.add(RectF(ldata->pointLargeLeftParen.x(), ldata->pointLargeLeftParen.y() - denRect.height(), spatium / 2,
6395 18 : numRect.height() + denRect.height()), item);
6396 18 : shape.add(RectF(ldata->pointLargeRightParen.x(), ldata->pointLargeRightParen.y() - denRect.height(), spatium / 2,
6397 18 : numRect.height() + denRect.height()), item);
6398 : }
6399 : }
6400 :
6401 6419 : ldata->setShape(shape);
6402 :
6403 6419 : ldata->setPosX(-shape.bbox().left());
6404 :
6405 6419 : if (item->isAboveStaves()) {
6406 0 : if (staff->hasSystemObjectsBelowBottomStaff()) {
6407 0 : double staffHeight = spatium * (numOfLines - 1) * lineDist;
6408 0 : ldata->setPosY(staffHeight - ldata->bbox().top());
6409 : } else {
6410 0 : ldata->setPosY(-ldata->bbox().bottom());
6411 : }
6412 6419 : } else if (item->isAcrossStaves()) {
6413 34 : double top = ldata->bbox().top();
6414 34 : ldata->setPosY(-top);
6415 : }
6416 :
6417 6419 : ldata->moveY(item->yPos());
6418 6419 : }
6419 :
6420 498 : void TLayout::layoutTimeTickAnchor(TimeTickAnchor* item, LayoutContext&)
6421 : {
6422 498 : TimeTickAnchor::LayoutData* ldata = item->mutldata();
6423 498 : const StaffType* st = item->staffType();
6424 498 : ldata->setPos(0.0, 0.0);
6425 498 : double width = item->segment()->width();
6426 498 : double height = item->staff()->staffHeight();
6427 498 : if (st && st->lines() == 1) {
6428 : // Special case for single line staves
6429 0 : const double lineDist = st->lineDistance().val() * item->spatium();
6430 0 : ldata->setPosY(-lineDist);
6431 0 : height = lineDist * 2;
6432 : }
6433 498 : ldata->setBbox(0.0, 0.0, width, height);
6434 498 : }
6435 :
6436 0 : void TLayout::layoutTremoloSingle(TremoloSingleChord* item, LayoutContext& ctx)
6437 : {
6438 : LAYOUT_CALL_ITEM(item);
6439 0 : TremoloLayout::layout(item, ctx);
6440 0 : }
6441 :
6442 0 : void TLayout::layoutTremoloTwo(TremoloTwoChord* item, LayoutContext& ctx)
6443 : {
6444 : LAYOUT_CALL_ITEM(item);
6445 0 : TremoloLayout::layout(item, ctx);
6446 0 : }
6447 :
6448 38 : void TLayout::layoutTremoloBar(const TremoloBar* item, TremoloBar::LayoutData* ldata)
6449 : {
6450 : LAYOUT_CALL_ITEM(item);
6451 : LD_INDEPENDENT;
6452 :
6453 38 : double spatium = item->spatium();
6454 :
6455 38 : ldata->setPos(0.0, -spatium * 3.0);
6456 :
6457 : /* we place the tremolo bars starting slightly before the
6458 : * notehead, and end it slightly after, drawing above the
6459 : * note. The values specified in Guitar Pro are very large, too
6460 : * large for the scale used in Musescore. We used the
6461 : * timeFactor and pitchFactor below to reduce these values down
6462 : * consistently to values that make sense to draw with the
6463 : * Musescore scale. */
6464 :
6465 38 : double timeFactor = item->userMag() / 1.0;
6466 38 : double pitchFactor = -spatium * .02;
6467 :
6468 38 : PolygonF polygon;
6469 142 : for (const PitchValue& v : item->points()) {
6470 104 : polygon << PointF(v.time * timeFactor, v.pitch * pitchFactor);
6471 : }
6472 38 : ldata->polygon = polygon;
6473 :
6474 38 : const double w = item->absoluteFromSpatium(item->lineWidth());
6475 38 : ldata->setBbox(ldata->polygon.boundingRect().adjusted(-w, -w, w, w));
6476 38 : }
6477 :
6478 116 : void TLayout::layoutTrillSegment(TrillSegment* item, LayoutContext& ctx)
6479 : {
6480 : LAYOUT_CALL_ITEM(item);
6481 116 : TrillSegment::LayoutData* ldata = item->mutldata();
6482 116 : Trill* trill = item->trill();
6483 116 : EngravingItem* startItem = trill->startElement();
6484 116 : Chord* startChord = startItem && startItem->isChord() ? toChord(startItem) : nullptr;
6485 116 : if (startChord) {
6486 : // Semi-hack: spanners don't have staffMove property, so we change
6487 : // the staffIdx itself to follow cross-staff chords if needed
6488 114 : item->setStaffIdx(startChord->vStaffIdx());
6489 : }
6490 :
6491 116 : if (item->staff()) {
6492 116 : ldata->setMag(item->staff()->staffMag(item->tick()));
6493 : }
6494 116 : if (item->spanner()->placeBelow()) {
6495 1 : ldata->setPosY(item->staff() ? item->staff()->staffHeight(item->tick()) : 0.0);
6496 : }
6497 116 : const double yOff = item->staffOffsetY();
6498 116 : ldata->moveY(yOff);
6499 :
6500 116 : PointF defaultPos = item->defaultPos();
6501 116 : ldata->move(defaultPos);
6502 :
6503 116 : bool accidentalGoesBelow = trill->trillType() == TrillType::DOWNPRALL_LINE;
6504 116 : Ornament* ornament = trill->ornament();
6505 116 : if (ornament) {
6506 116 : if (item->isSingleBeginType()) {
6507 115 : TLayout::layoutOrnament(ornament, ornament->mutldata(), ctx.conf());
6508 : }
6509 116 : trill->setAccidental(accidentalGoesBelow ? ornament->accidentalBelow() : ornament->accidentalAbove());
6510 116 : trill->setCueNoteChord(ornament->cueNoteChord());
6511 116 : ArticulationAnchor anchor = ornament->anchor();
6512 116 : if (anchor == ArticulationAnchor::AUTO) {
6513 113 : trill->setPlacement(trill->track() % 2 ? PlacementV::BELOW : PlacementV::ABOVE);
6514 : } else {
6515 3 : trill->setPlacement(anchor == ArticulationAnchor::TOP ? PlacementV::ABOVE : PlacementV::BELOW);
6516 : }
6517 116 : trill->setPropertyFlags(Pid::PLACEMENT, PropertyFlags::STYLED); // Ensures that the property isn't written (it is written by the ornamnent)
6518 : }
6519 :
6520 116 : if (item->isSingleType() || item->isBeginType()) {
6521 115 : switch (trill->trillType()) {
6522 101 : case TrillType::TRILL_LINE:
6523 101 : item->symbolLine(SymId::ornamentTrill, SymId::wiggleTrill);
6524 101 : break;
6525 6 : case TrillType::PRALLPRALL_LINE:
6526 6 : item->symbolLine(SymId::wiggleTrill, SymId::wiggleTrill);
6527 6 : break;
6528 4 : case TrillType::UPPRALL_LINE:
6529 4 : item->symbolLine(SymId::ornamentBottomLeftConcaveStroke,
6530 : SymId::ornamentZigZagLineNoRightEnd, SymId::ornamentZigZagLineWithRightEnd);
6531 4 : break;
6532 4 : case TrillType::DOWNPRALL_LINE:
6533 4 : item->symbolLine(SymId::ornamentLeftVerticalStroke,
6534 : SymId::ornamentZigZagLineNoRightEnd, SymId::ornamentZigZagLineWithRightEnd);
6535 4 : break;
6536 : }
6537 115 : Accidental* a = trill->accidental();
6538 115 : if (a) {
6539 10 : double vertMargin = 0.35 * item->spatium();
6540 10 : RectF box = item->symBbox(item->symbols().front());
6541 10 : double x = 0;
6542 10 : double y = 0;
6543 10 : x = 0.5 * (box.width() - a->width());
6544 : double minVertDist = accidentalGoesBelow
6545 20 : ? Shape(box).minVerticalDistance(a->shape())
6546 10 : : a->shape().minVerticalDistance(Shape(box));
6547 10 : y = (accidentalGoesBelow ? minVertDist + vertMargin : -minVertDist - vertMargin) + yOff;
6548 10 : a->setPos(x, y);
6549 10 : a->setOwnershipParent(item);
6550 : }
6551 : } else {
6552 1 : switch (trill->trillType()) {
6553 1 : case TrillType::TRILL_LINE:
6554 : case TrillType::PRALLPRALL_LINE:
6555 1 : item->symbolLine(SymId::wiggleTrill, SymId::wiggleTrill);
6556 1 : break;
6557 0 : case TrillType::UPPRALL_LINE:
6558 : case TrillType::DOWNPRALL_LINE:
6559 0 : item->symbolLine(SymId::ornamentZigZagLineNoRightEnd,
6560 : SymId::ornamentZigZagLineNoRightEnd, SymId::ornamentZigZagLineWithRightEnd);
6561 0 : break;
6562 : }
6563 : }
6564 :
6565 116 : Autoplace::autoplaceSpannerSegment(item, ldata);
6566 116 : }
6567 :
6568 232 : void TLayout::fillTrillSegmentShape(const TrillSegment* item, TrillSegment::LayoutData* ldata, const LayoutConfiguration& conf)
6569 : {
6570 : LAYOUT_CALL_ITEM(item);
6571 232 : std::shared_ptr<const IEngravingFont> font = conf.engravingFont();
6572 232 : Shape s = font->shape(item->symbols(), item->magS());
6573 232 : Accidental* accidental = item->trill()->accidental();
6574 232 : if (accidental && accidental->visible() && item->isSingleBeginType()) {
6575 20 : s.add(accidental->shape().translate(accidental->pos()));
6576 : }
6577 :
6578 232 : ldata->setShape(s);
6579 232 : }
6580 :
6581 0 : void TLayout::layoutTripletFeel(const TripletFeel* item, TripletFeel::LayoutData* ldata)
6582 : {
6583 : LAYOUT_CALL_ITEM(item);
6584 0 : layoutSystemText(item, ldata);
6585 0 : }
6586 :
6587 0 : void TLayout::layoutTrill(Trill* item, LayoutContext& ctx)
6588 : {
6589 : LAYOUT_CALL_ITEM(item);
6590 0 : layoutLine(static_cast<SLine*>(item), ctx);
6591 0 : }
6592 :
6593 41 : void TLayout::layoutTuplet(Tuplet* item, LayoutContext& ctx)
6594 : {
6595 : LAYOUT_CALL_ITEM(item);
6596 41 : TupletLayout::layout(item, ctx);
6597 41 : }
6598 :
6599 1941 : void TLayout::fillTupletShape(const Tuplet* item, Tuplet::LayoutData* ldata)
6600 : {
6601 : LAYOUT_CALL_ITEM(item);
6602 1941 : Shape s;
6603 1941 : if (item->hasBracket()) {
6604 4107 : auto tupletRect = [](const PointF& p1, const PointF& p2, double height, const Tuplet* item) {
6605 4107 : Shape shape;
6606 4107 : double xLength = p2.x() - p1.x();
6607 4107 : int nSteps = 2 * std::ceil(xLength / item->spatium());
6608 4107 : double xStep = xLength / nSteps;
6609 4107 : double yStep = (p2.y() - p1.y()) / nSteps;
6610 4107 : double rectHeight = (item->isUp() ? height : -height) - yStep;
6611 4107 : double xCur = p1.x();
6612 4107 : double yCur = p1.y() + yStep;
6613 28095 : for (int i = 0; i < nSteps; ++i) {
6614 23988 : shape.add(RectF(xCur, yCur, xStep, rectHeight).normalized(), item);
6615 23988 : xCur += xStep;
6616 23988 : yCur += yStep;
6617 : }
6618 4107 : return shape;
6619 0 : };
6620 :
6621 1027 : double height = item->style().styleAbsolute(Sid::tupletBracketHookHeight);
6622 1027 : s.add(tupletRect(item->bracketL[0], item->bracketL[1], height, item));
6623 1027 : s.add(tupletRect(item->bracketL[1], item->bracketL[2], height, item));
6624 1027 : if (item->number()) {
6625 1026 : s.add(tupletRect(item->bracketR[0], item->bracketR[1], height, item));
6626 1026 : s.add(tupletRect(item->bracketR[1], item->bracketR[2], height, item));
6627 : } else {
6628 1 : s.add(tupletRect(item->bracketL[2], item->bracketL[3], height, item));
6629 : }
6630 : }
6631 1941 : if (item->number()) {
6632 1694 : s.add(item->number()->ldata()->bbox().translated(item->number()->pos()));
6633 : }
6634 :
6635 1941 : ldata->setShape(s);
6636 1941 : }
6637 :
6638 28 : void TLayout::layoutVibratoSegment(VibratoSegment* item, LayoutContext&)
6639 : {
6640 : LAYOUT_CALL_ITEM(item);
6641 28 : VibratoSegment::LayoutData* ldata = item->mutldata();
6642 28 : if (item->staff()) {
6643 28 : ldata->setMag(item->staff()->staffMag(item->tick()));
6644 : }
6645 28 : if (item->spanner()->placeBelow()) {
6646 0 : ldata->setPosY(item->staff() ? item->staff()->staffHeight(item->tick()) : 0.0);
6647 : }
6648 :
6649 28 : PointF defaultPos = item->defaultPos();
6650 28 : ldata->move(defaultPos);
6651 :
6652 28 : ldata->moveY(item->staffOffsetY());
6653 :
6654 28 : switch (item->vibrato()->vibratoType()) {
6655 10 : case VibratoType::GUITAR_VIBRATO:
6656 10 : item->symbolLine(SymId::guitarVibratoStroke, SymId::guitarVibratoStroke);
6657 10 : break;
6658 4 : case VibratoType::GUITAR_VIBRATO_WIDE:
6659 4 : item->symbolLine(SymId::guitarWideVibratoStroke, SymId::guitarWideVibratoStroke);
6660 4 : break;
6661 11 : case VibratoType::VIBRATO_SAWTOOTH:
6662 11 : item->symbolLine(SymId::wiggleSawtooth, SymId::wiggleSawtooth);
6663 11 : break;
6664 3 : case VibratoType::VIBRATO_SAWTOOTH_WIDE:
6665 3 : item->symbolLine(SymId::wiggleSawtoothWide, SymId::wiggleSawtoothWide);
6666 3 : break;
6667 0 : default:
6668 0 : break;
6669 : }
6670 :
6671 28 : Autoplace::autoplaceSpannerSegment(item, ldata);
6672 28 : }
6673 :
6674 0 : void TLayout::layoutVibrato(Vibrato* item, LayoutContext& ctx)
6675 : {
6676 : LAYOUT_CALL_ITEM(item);
6677 0 : layoutLine(static_cast<SLine*>(item), ctx);
6678 :
6679 0 : if (ctx.conf().isPaletteMode()) {
6680 0 : return;
6681 : }
6682 0 : if (item->spannerSegments().empty()) {
6683 0 : LOGD("Vibrato: no segments");
6684 0 : return;
6685 : }
6686 : }
6687 :
6688 0 : void TLayout::layoutVolta(Volta* item, LayoutContext& ctx)
6689 : {
6690 : LAYOUT_CALL_ITEM(item);
6691 0 : layoutLine(item, ctx);
6692 0 : }
6693 :
6694 279 : void TLayout::layoutVoltaSegment(VoltaSegment* item, LayoutContext& ctx)
6695 : {
6696 : LAYOUT_CALL_ITEM(item);
6697 279 : VoltaSegment::LayoutData* ldata = item->mutldata();
6698 279 : layoutTextLineBaseSegment(item, ctx);
6699 279 : if (item->placeBelow()) {
6700 0 : if (TextBase* beginText = item->text()) {
6701 0 : PointF offset = beginText->offset();
6702 0 : offset.setY(-offset.y() + beginText->ldata()->bbox().height());
6703 0 : beginText->setOffset(offset);
6704 : }
6705 0 : if (TextBase* endText = item->endText()) {
6706 0 : PointF offset = endText->offset();
6707 0 : offset.setY(-offset.y() + endText->ldata()->bbox().height());
6708 0 : endText->setOffset(offset);
6709 : }
6710 : // recalculate shape after moving texts
6711 0 : Shape sh = recalculateTextLineBaseSegmentShape(item);
6712 0 : ldata->setShape(sh);
6713 0 : }
6714 279 : Autoplace::autoplaceSpannerSegment(item, ldata);
6715 279 : }
6716 :
6717 0 : void TLayout::layoutWhammyBarSegment(WhammyBarSegment* item, LayoutContext& ctx)
6718 : {
6719 : LAYOUT_CALL_ITEM(item);
6720 0 : WhammyBarSegment::LayoutData* ldata = item->mutldata();
6721 0 : layoutTextLineBaseSegment(item, ctx);
6722 :
6723 0 : Autoplace::autoplaceSpannerSegment(item, ldata);
6724 0 : }
6725 :
6726 : using LayoutSystemTypes = rtti::TypeList<LyricsLine, Slur, HammerOnPullOff, TappingHalfSlur, Volta>;
6727 :
6728 : class LayoutSystemVisitor : public rtti::Visitor<LayoutSystemVisitor>
6729 : {
6730 : public:
6731 : template<typename T>
6732 8839 : static bool doVisit(Spanner* item, System* system, LayoutContext& ctx, SpannerSegment** segOut)
6733 : {
6734 8839 : if (T::classof(item)) {
6735 1494 : *segOut = TLayout::layoutSystem(static_cast<T*>(item), system, ctx);
6736 1494 : return true;
6737 : }
6738 7345 : return false;
6739 : }
6740 : };
6741 :
6742 2553 : SpannerSegment* TLayout::layoutSystem(Spanner* item, System* system, LayoutContext& ctx)
6743 : {
6744 : LAYOUT_CALL_ITEM(item);
6745 2553 : SpannerSegment* seg = nullptr;
6746 2553 : bool found = LayoutSystemVisitor::visit(LayoutSystemTypes {}, item, system, ctx, &seg);
6747 2553 : if (!found) {
6748 1059 : SLine* line = dynamic_cast<SLine*>(item);
6749 1059 : if (line) {
6750 1059 : seg = layoutSystemSLine(line, system, ctx);
6751 : } else {
6752 0 : DO_ASSERT(found);
6753 : }
6754 : }
6755 2553 : return seg;
6756 : }
6757 :
6758 2609 : SpannerSegment* TLayout::getNextLayoutSystemSegment(Spanner* spanner, System* system,
6759 : std::function<SpannerSegment* ()> createSegment)
6760 : {
6761 : // Prefer a segment which has already been added to the system
6762 2609 : SpannerSegment* seg = nullptr;
6763 2609 : SpannerSegment* detached = nullptr;
6764 3765 : for (SpannerSegment* ss : spanner->spannerSegments()) {
6765 1156 : if (ss->system() == system) {
6766 0 : seg = ss;
6767 0 : break;
6768 : }
6769 1156 : if (!detached && !ss->system()) {
6770 1065 : detached = ss;
6771 : }
6772 : }
6773 2609 : if (!seg) {
6774 2609 : seg = detached;
6775 : }
6776 :
6777 2609 : if (!seg) {
6778 1544 : if ((seg = spanner->popUnusedSegment())) {
6779 0 : spanner->reuse(seg);
6780 : } else {
6781 1544 : seg = createSegment();
6782 1544 : assert(seg);
6783 1544 : spanner->add(seg);
6784 : }
6785 : }
6786 2609 : seg->moveToSystem(system);
6787 2609 : seg->setSpanner(spanner);
6788 2609 : seg->setTrack(spanner->track());
6789 2609 : seg->setVisible(spanner->visible());
6790 2609 : return seg;
6791 : }
6792 :
6793 : // layout spannersegment for system
6794 1338 : SpannerSegment* TLayout::layoutSystemSLine(SLine* line, System* system, LayoutContext& ctx)
6795 : {
6796 : LAYOUT_CALL_ITEM(line);
6797 1338 : Fraction stick = system->firstMeasure()->tick();
6798 1338 : Fraction etick = system->lastMeasure()->endTick();
6799 :
6800 1338 : LineSegment* lineSegm = toLineSegment(TLayout::getNextLayoutSystemSegment(line, system, [line]() {
6801 823 : return line->createLineSegment();
6802 : }));
6803 :
6804 : SpannerSegmentType sst;
6805 1338 : if (line->tick() >= stick) {
6806 : //
6807 : // this is the first call to layoutSystem,
6808 : // processing the first line segment
6809 : //
6810 1314 : line->computeStartElement();
6811 1314 : line->computeEndElement();
6812 1314 : if (line->tick2() < etick) {
6813 1150 : sst = SpannerSegmentType::SINGLE;
6814 164 : } else if (line->tick2() == etick) {
6815 143 : if (line->isPedal() && toPedal(line)->connect45HookToNext()) {
6816 0 : sst = SpannerSegmentType::BEGIN;
6817 : } else {
6818 143 : sst = SpannerSegmentType::SINGLE;
6819 : }
6820 : } else {
6821 21 : sst = SpannerSegmentType::BEGIN;
6822 : }
6823 1314 : if (line->hasVoiceAssignmentProperties()) {
6824 467 : line->setPlacementBasedOnVoiceAssignment(ctx.conf().styleV(Sid::dynamicsHairpinVoiceBasedPlacement).value<DirectionV>());
6825 : }
6826 24 : } else if (line->tick() < stick
6827 24 : && (line->tick2() > etick || (line->tick2() == etick && line->isPedal() && toPedal(line)->connect45HookToNext()))) {
6828 3 : sst = SpannerSegmentType::MIDDLE;
6829 : } else {
6830 : //
6831 : // this is the last call to layoutSystem
6832 : // processing the last line segment
6833 : //
6834 21 : sst = SpannerSegmentType::END;
6835 : }
6836 1338 : lineSegm->setSpannerSegmentType(sst);
6837 1338 : lineSegm->setPlacement(line->placement());
6838 :
6839 1338 : switch (sst) {
6840 1293 : case SpannerSegmentType::SINGLE: {
6841 : System* s;
6842 1293 : PointF p1 = line->linePos(Grip::START, &s);
6843 1293 : PointF p2 = line->linePos(Grip::END, &s);
6844 1293 : double len = p2.x() - p1.x();
6845 1293 : lineSegm->setPos(p1);
6846 1293 : lineSegm->setPos2(PointF(len, p2.y() - p1.y()));
6847 : }
6848 1293 : break;
6849 21 : case SpannerSegmentType::BEGIN: {
6850 : System* s;
6851 21 : PointF p1 = line->linePos(Grip::START, &s);
6852 21 : lineSegm->setPos(p1);
6853 21 : double x2 = system->endingXForOpenEndedLines();
6854 21 : lineSegm->setPos2(PointF(x2 - p1.x(), 0.0));
6855 : }
6856 21 : break;
6857 3 : case SpannerSegmentType::MIDDLE: {
6858 3 : double x1 = line->isVolta() ? voltaMidEndSegmentStartX(toVolta(line), system, ctx)
6859 3 : : system->firstNoteRestSegmentX(true);
6860 3 : double x2 = system->endingXForOpenEndedLines();
6861 : System* s;
6862 3 : PointF p1 = line->linePos(Grip::START, &s);
6863 3 : lineSegm->setPos(PointF(x1, p1.y()));
6864 3 : lineSegm->setPos2(PointF(x2 - x1, 0.0));
6865 : }
6866 3 : break;
6867 21 : case SpannerSegmentType::END: {
6868 : System* s;
6869 21 : PointF p2 = line->linePos(Grip::END, &s);
6870 21 : double x1 = line->isVolta() ? voltaMidEndSegmentStartX(toVolta(line), system, ctx)
6871 15 : : system->firstNoteRestSegmentX(true);
6872 21 : double len = p2.x() - x1;
6873 21 : lineSegm->setPos(PointF(p2.x() - len, p2.y()));
6874 21 : lineSegm->setPos2(PointF(len, 0.0));
6875 : }
6876 21 : break;
6877 : }
6878 :
6879 1338 : layoutLineSegment(lineSegm, ctx);
6880 :
6881 1338 : return lineSegm;
6882 : }
6883 :
6884 6 : double TLayout::voltaMidEndSegmentStartX(Volta* volta, System* system, LayoutContext& ctx)
6885 : {
6886 6 : Measure* firstMeasure = system->firstMeasure();
6887 6 : if (!firstMeasure) {
6888 0 : return 0.0;
6889 : }
6890 :
6891 6 : Segment* refSeg = firstMeasure->repeatStart()
6892 12 : ? firstMeasure->first(SegmentType::StartRepeatBarLine)
6893 6 : : ctx.conf().styleB(Sid::voltaAlignStartBeforeKeySig)
6894 6 : ? firstMeasure->first(SegmentType::KeySig)
6895 6 : : nullptr;
6896 :
6897 6 : if (!refSeg) {
6898 6 : return system->firstNoteRestSegmentX(true);
6899 : }
6900 :
6901 0 : if (refSeg->isStartRepeatBarLineType()) {
6902 0 : return refSeg->x() + firstMeasure->x();
6903 : }
6904 :
6905 0 : KeySig* keySig = toKeySig(refSeg->element(volta->track()));
6906 0 : if (!keySig || keySig->ldata()->keySymbols.empty()) {
6907 0 : return 0.0;
6908 : }
6909 :
6910 0 : KeySym keySym = keySig->ldata()->keySymbols.front();
6911 0 : double xCutout = ctx.engravingFont()->smuflAnchor(keySym.sym, SmuflAnchorId::cutOutNW, 1.0).x();
6912 0 : return keySig->x() + keySym.xPos.val() + xCutout + refSeg->x() + firstMeasure->x();
6913 : }
6914 :
6915 342 : SpannerSegment* TLayout::layoutSystem(LyricsLine* line, System* system, LayoutContext& ctx)
6916 : {
6917 : LAYOUT_CALL_ITEM(line);
6918 342 : if (!line->isPartialLyricsLine() && !line->lyrics()) {
6919 0 : return nullptr;
6920 : }
6921 :
6922 342 : Fraction stick = system->firstMeasure()->tick();
6923 342 : Fraction etick = system->lastMeasure()->endTick();
6924 :
6925 342 : LyricsLineSegment* lineSegm = toLyricsLineSegment(TLayout::getNextLayoutSystemSegment(line, system, [line]() {
6926 250 : return line->createLineSegment();
6927 : }));
6928 :
6929 : SpannerSegmentType sst;
6930 342 : if (line->tick() >= stick) {
6931 328 : TLayout::layoutLyricsLine(line);
6932 328 : if (line->ticks().isZero() && line->isEndMelisma()) {
6933 0 : return nullptr;
6934 : }
6935 328 : line->computeStartElement();
6936 328 : line->computeEndElement();
6937 328 : sst = line->tick2() <= etick ? SpannerSegmentType::SINGLE : SpannerSegmentType::BEGIN;
6938 14 : } else if (line->tick() < stick && line->tick2() > etick) {
6939 4 : sst = SpannerSegmentType::MIDDLE;
6940 : } else {
6941 10 : sst = SpannerSegmentType::END;
6942 : }
6943 342 : lineSegm->setSpannerSegmentType(sst);
6944 :
6945 342 : TLayout::layoutLyricsLineSegment(lineSegm, ctx);
6946 342 : if (!line->isPartialLyricsLine() && !line->lyrics()) {
6947 : // this line could have been removed in the process of laying out surrounding lyrics
6948 0 : return nullptr;
6949 : }
6950 :
6951 342 : return lineSegm;
6952 : }
6953 :
6954 279 : SpannerSegment* TLayout::layoutSystem(Volta* line, System* system, LayoutContext& ctx)
6955 : {
6956 : LAYOUT_CALL_ITEM(line);
6957 279 : return layoutSystemSLine(line, system, ctx);
6958 : }
6959 :
6960 873 : SpannerSegment* TLayout::layoutSystem(Slur* line, System* system, LayoutContext& ctx)
6961 : {
6962 : LAYOUT_CALL_ITEM(line);
6963 873 : return SlurTieLayout::layoutSystem(line, system, ctx);
6964 : }
|